Skip to content

Commit 73ec931

Browse files
authored
Merge pull request #21 from nii236/feature/export_uploads
adds ability to export posts and uploads
2 parents cb003fa + b903174 commit 73ec931

File tree

4 files changed

+217
-0
lines changed

4 files changed

+217
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212
*.out
1313

1414
# Intermediate build artifacts
15+
/*.zip
1516
/assets
1617
/bindata.go
1718
/go.sum
19+
/vendor
1820

1921
# RWTxt databases
2022
*db

cmd/rwtxt/main.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ var (
2121
func main() {
2222
var (
2323
err error
24+
export = flag.Bool("export", false, "export uploads to {{TIMESTAMP}}-uploads.zip and posts to {{TIMESTAMP}}-posts.zip")
2425
resizeWidth = flag.Int("resizewidth", -1, "image width to resize on the fly")
2526
resizeOnUpload = flag.Bool("resizeonupload", false, "resize on upload")
2627
resizeOnRequest = flag.Bool("resizeonrequest", false, "resize on request")
@@ -70,6 +71,18 @@ func main() {
7071
panic(err)
7172
}
7273

74+
if *export {
75+
err = fs.ExportPosts()
76+
if err != nil {
77+
panic(err)
78+
}
79+
err = fs.ExportUploads()
80+
if err != nil {
81+
panic(err)
82+
}
83+
return
84+
}
85+
7386
config := rwtxt.Config{Private: *private,
7487
ResizeWidth: *resizeWidth,
7588
ResizeOnRequest: *resizeOnRequest,

pkg/db/db.go

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,16 @@ package db
22

33
import (
44
"bufio"
5+
"bytes"
56
"compress/gzip"
67
"database/sql"
78
"encoding/json"
9+
"fmt"
810
"html/template"
11+
"io/ioutil"
912
"os"
13+
"path/filepath"
14+
"strconv"
1015
"strings"
1116
"sync"
1217
"time"
@@ -263,6 +268,150 @@ func (fs *FileSystem) SaveBlob(id string, name string, blob []byte) (err error)
263268
return
264269
}
265270

271+
// ExportPosts will save posts to {{TIMESTAMP}}-posts.gz
272+
func (fs *FileSystem) ExportPosts() error {
273+
domains, err := fs.GetDomains()
274+
if err != nil {
275+
return err
276+
}
277+
278+
dir := os.TempDir()
279+
postPaths := []string{}
280+
for _, domain := range domains {
281+
files, err := fs.GetAll(domain)
282+
if err != nil {
283+
return err
284+
}
285+
for _, file := range files {
286+
fname := (fmt.Sprintf("%s-%s.md", file.Slug, file.ID))
287+
r := strings.NewReader(file.Data)
288+
if err != nil {
289+
return err
290+
}
291+
var buf bytes.Buffer
292+
_, err = buf.ReadFrom(r)
293+
if err != nil {
294+
return err
295+
}
296+
err = os.MkdirAll(filepath.Join(dir, domain), os.ModePerm)
297+
if err != nil {
298+
return err
299+
}
300+
fpath := filepath.Join(dir, domain, fname)
301+
err = ioutil.WriteFile(fpath, buf.Bytes(), os.ModePerm)
302+
if err != nil {
303+
return err
304+
}
305+
306+
postPaths = append(postPaths, fpath)
307+
}
308+
}
309+
timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
310+
for _, f := range postPaths {
311+
log.Debug(f)
312+
}
313+
utils.ZipFiles(fmt.Sprintf("%s-posts.zip", timestamp), postPaths)
314+
return nil
315+
316+
}
317+
318+
// ExportUploads will save uploads to {{TIMESTAMP}}-uploads.gz
319+
func (fs *FileSystem) ExportUploads() error {
320+
dir := os.TempDir()
321+
files := []string{}
322+
323+
ids, err := fs.GetBlobIDs()
324+
if err != nil {
325+
return err
326+
}
327+
328+
for _, id := range ids {
329+
name, data, _, err := fs.GetBlob(id)
330+
if err != nil {
331+
return err
332+
}
333+
fname := fmt.Sprintf("%s-%s", id, name)
334+
335+
r, err := gzip.NewReader(bytes.NewReader(data))
336+
if err != nil {
337+
return err
338+
}
339+
var buf bytes.Buffer
340+
_, err = buf.ReadFrom(r)
341+
if err != nil {
342+
return err
343+
}
344+
fpath := filepath.Join(dir, fname)
345+
err = ioutil.WriteFile(fpath, buf.Bytes(), os.ModePerm)
346+
if err != nil {
347+
return err
348+
}
349+
350+
files = append(files, fpath)
351+
}
352+
353+
timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
354+
for _, f := range files {
355+
log.Debug(f)
356+
}
357+
utils.ZipFiles(fmt.Sprintf("%s-uploads.zip", timestamp), files)
358+
return nil
359+
}
360+
361+
// GetBlobIDs will return a list of blob ids
362+
func (fs *FileSystem) GetBlobIDs() ([]string, error) {
363+
fs.Lock()
364+
defer fs.Unlock()
365+
stmt, err := fs.DB.Prepare(`SELECT id FROM blobs`)
366+
if err != nil {
367+
return nil, err
368+
}
369+
defer stmt.Close()
370+
371+
result := []string{}
372+
rows, err := stmt.Query()
373+
if err != nil {
374+
return nil, err
375+
}
376+
for rows.Next() {
377+
var id string
378+
err = rows.Scan(&id)
379+
if err != nil {
380+
return nil, err
381+
}
382+
result = append(result, id)
383+
}
384+
385+
return result, nil
386+
}
387+
388+
// GetDomains will return a list of domains
389+
func (fs *FileSystem) GetDomains() ([]string, error) {
390+
fs.Lock()
391+
defer fs.Unlock()
392+
stmt, err := fs.DB.Prepare(`SELECT name FROM domains`)
393+
if err != nil {
394+
return nil, err
395+
}
396+
defer stmt.Close()
397+
398+
result := []string{}
399+
rows, err := stmt.Query()
400+
if err != nil {
401+
return nil, err
402+
}
403+
for rows.Next() {
404+
var domain string
405+
err = rows.Scan(&domain)
406+
if err != nil {
407+
return nil, err
408+
}
409+
result = append(result, domain)
410+
}
411+
412+
return result, nil
413+
}
414+
266415
// SaveResizedImage will save a resized image
267416
func (fs *FileSystem) SaveResizedImage(id string, name string, blob []byte) (err error) {
268417
fs.Lock()

pkg/utils/utils.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package utils
22

33
import (
4+
"archive/zip"
45
"crypto/hmac"
56
"crypto/sha512"
67
"encoding/hex"
78
"html/template"
9+
"io"
810
"math/rand"
11+
"os"
912
"strings"
1013
"time"
1114

@@ -14,6 +17,56 @@ import (
1417
blackfriday "gopkg.in/russross/blackfriday.v2"
1518
)
1619

20+
// ZipFiles will zip files to filename
21+
func ZipFiles(filename string, files []string) error {
22+
23+
newZipFile, err := os.Create(filename)
24+
if err != nil {
25+
return err
26+
}
27+
defer newZipFile.Close()
28+
29+
zipWriter := zip.NewWriter(newZipFile)
30+
defer zipWriter.Close()
31+
32+
// Add files to zip
33+
for _, file := range files {
34+
35+
zipfile, err := os.Open(file)
36+
if err != nil {
37+
return err
38+
}
39+
defer zipfile.Close()
40+
41+
// Get the file information
42+
info, err := zipfile.Stat()
43+
if err != nil {
44+
return err
45+
}
46+
47+
header, err := zip.FileInfoHeader(info)
48+
if err != nil {
49+
return err
50+
}
51+
52+
// Using FileInfoHeader() above only uses the basename of the file. If we want
53+
// to preserve the folder structure we can overwrite this with the full path.
54+
header.Name = file
55+
56+
// Change to deflate to gain better compression
57+
// see http://golang.org/pkg/archive/zip/#pkg-constants
58+
header.Method = zip.Deflate
59+
60+
writer, err := zipWriter.CreateHeader(header)
61+
if err != nil {
62+
return err
63+
}
64+
if _, err = io.Copy(writer, zipfile); err != nil {
65+
return err
66+
}
67+
}
68+
return nil
69+
}
1770
func RenderMarkdownToHTML(markdown string) template.HTML {
1871
html := string(blackfriday.Run([]byte(markdown),
1972
blackfriday.WithExtensions(

0 commit comments

Comments
 (0)