Skip to content

Commit

Permalink
[enh] add download snapshot endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
asciimoo committed Aug 7, 2024
1 parent 460c0bc commit b0d8851
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 79 deletions.
2 changes: 1 addition & 1 deletion templates/index.tpl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{{ define "full-content" }}
<div class="hero">
<div class="hero is-medium">
<div class="hero-body">
<div class="columns">
<div class="column is-2 has-text-right">
Expand Down
6 changes: 5 additions & 1 deletion templates/snapshot_wrapper.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
<div class="snapshot__container">
<div class="container content my-5">
<h3 class="title mb-0">Snapshot of <a href="{{ .Bookmark.URL }}">{{ Truncate .Bookmark.URL 100 }}</a></h3>
<p><strong>{{ .Snapshot.CreatedAt | ToDate }}</strong> <span class="tag is-info is-light">{{ .Snapshot.Size | FormatSize }}</span> <a href="{{ SnapshotURL .Snapshot.Key }}"><small>Fullscreen snapshot</small></a></p>
<p>
<strong>{{ .Snapshot.CreatedAt | ToDate }}</strong>
<span class="tag is-info is-light">{{ .Snapshot.Size | FormatSize }}</span> <a href="{{ SnapshotURL .Snapshot.Key }}"><small>Fullscreen</small></a>
- <a href="{{ BaseURL "/download_snapshot" }}?sid={{ .Snapshot.Key }}"><small>Download</small></a>
</p>
</div>
{{ if .OtherSnapshots }}
<div class="accordion-tabs">
Expand Down
16 changes: 16 additions & 0 deletions webapp/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,22 @@ func init() {
},
},
},
&Endpoint{
Name: "Download snapshot",
Path: "/download_snapshot",
Method: GET,
AuthRequired: false,
Handler: downloadSnapshot,
Description: "Download a self contained single file version of a snapshot",
Args: []*EndpointArg{
&EndpointArg{
Name: "sid",
Type: "string",
Required: true,
Description: "Snapshot key",
},
},
},
//&Endpoint{
// Name: "Self-contained snapshot view",
// Path: "/self_contained_snapshot",
Expand Down
178 changes: 101 additions & 77 deletions webapp/snapshot.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package webapp

import (
//"bytes"
//"compress/gzip"
//"encoding/base64"
//"fmt"
//"io"
//"mime"
"bytes"
"compress/gzip"
"encoding/base64"
"fmt"
"golang.org/x/net/html"

Check failure on line 8 in webapp/snapshot.go

View workflow job for this annotation

GitHub Actions / lint

File is not `goimports`-ed (goimports)
"io"
"mime"
"net/http"
//"path/filepath"
//"strings"
//"golang.org/x/net/html"
"path/filepath"
"strings"

"github.com/asciimoo/omnom/model"
"github.com/asciimoo/omnom/storage"
Expand Down Expand Up @@ -79,74 +79,98 @@ func snapshotWrapper(c *gin.Context) {
})
}

//func selfContainedSnapshot(c *gin.Context) {
// id, ok := c.GetQuery("sid")
// if !ok {
// return
// }
// r, err := storage.GetSnapshot(id)
// if err != nil {
// return
// }
// defer r.Close()
// gr, err := gzip.NewReader(r)
// if err != nil {
// return
// }
// c.Header("Content-Type", "text/html; charset=utf-8")
// c.Status(http.StatusOK)
// doc := html.NewTokenizer(gr)
// for {
// tt := doc.Next()
// switch tt {
// case html.ErrorToken:
// err := doc.Err()
// if err == io.EOF {
// return
// }
// // TODO error handling
// return
// case html.SelfClosingTagToken:
// case html.StartTagToken:
// c.Writer.Write([]byte("<"))
// tn, hasAttr := doc.TagName()
// c.Writer.Write(tn)
// if hasAttr {
// for {
// aName, aVal, moreAttr := doc.TagAttr()
// c.Writer.Write([]byte(fmt.Sprintf(` %s="`, aName)))
// if bytes.HasPrefix(aVal, []byte("../../resources/")) && false {
// href := string(aVal)
// res, err := storage.GetResource(filepath.Base(href))
// if err == nil {
// gres, err := gzip.NewReader(r)
// if err == nil {
// ext := filepath.Ext(href)
// c.Writer.Write([]byte(fmt.Sprintf("data:%s;base64,", strings.Split(mime.TypeByExtension(ext), ";")[0])))
// bw := base64.NewEncoder(base64.StdEncoding, c.Writer)
// io.Copy(bw, gres)
// bw.Close()
// res.Close()
// }
// }
// } else {
// c.Writer.Write([]byte(html.EscapeString(string(aVal))))
// }
// c.Writer.Write([]byte(`"`))
// if !moreAttr {
// break
// }
// }
// }
// c.Writer.Write([]byte(">"))
// case html.TextToken:
// c.Writer.Write(doc.Text())
// case html.EndTagToken:
// tn, _ := doc.TagName()
// c.Writer.Write([]byte(fmt.Sprintf(`</%s>`, tn)))
// }
// }
//}
func downloadSnapshot(c *gin.Context) {
id, ok := c.GetQuery("sid")
if !ok {
return
}
r, err := storage.GetSnapshot(id)
if err != nil {
return
}
defer r.Close()
gr, err := gzip.NewReader(r)
if err != nil {
return
}
c.Header("Content-Type", "text/html; charset=utf-8")
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=omnom_snapshot_%s.html;", id))
c.Status(http.StatusOK)
doc := html.NewTokenizer(gr)
for {
tt := doc.Next()
switch tt {
case html.ErrorToken:
err := doc.Err()
if err == io.EOF {

Check failure on line 105 in webapp/snapshot.go

View workflow job for this annotation

GitHub Actions / lint

comparing with == will fail on wrapped errors. Use errors.Is to check for a specific error (errorlint)
return
}
// TODO error handling
return
case html.SelfClosingTagToken:
case html.StartTagToken:
c.Writer.Write([]byte("<"))

Check failure on line 112 in webapp/snapshot.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `c.Writer.Write` is not checked (errcheck)
tn, hasAttr := doc.TagName()
c.Writer.Write(tn)

Check failure on line 114 in webapp/snapshot.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `c.Writer.Write` is not checked (errcheck)
if hasAttr {
generateTagAttributes(tn, doc, c.Writer)
}
c.Writer.Write([]byte(">"))

Check failure on line 118 in webapp/snapshot.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `c.Writer.Write` is not checked (errcheck)
case html.TextToken:
c.Writer.Write(doc.Text())

Check failure on line 120 in webapp/snapshot.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `c.Writer.Write` is not checked (errcheck)
case html.EndTagToken:
tn, _ := doc.TagName()
c.Writer.Write([]byte(fmt.Sprintf(`</%s>`, tn)))

Check failure on line 123 in webapp/snapshot.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `c.Writer.Write` is not checked (errcheck)
}
}
}

func generateTagAttributes(tagName []byte, doc *html.Tokenizer, out io.Writer) {

Check failure on line 128 in webapp/snapshot.go

View workflow job for this annotation

GitHub Actions / lint

`generateTagAttributes` - `tagName` is unused (unparam)
for {
aName, aVal, moreAttr := doc.TagAttr()
out.Write([]byte(fmt.Sprintf(` %s="`, aName)))

Check failure on line 131 in webapp/snapshot.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `out.Write` is not checked (errcheck)
if attributeHasResource(aName, aVal) {
href := string(aVal)
res, err := storage.GetResource(filepath.Base(href))
if err == nil {
gres, err := gzip.NewReader(res)
if err == nil {
ext := filepath.Ext(href)
out.Write([]byte(fmt.Sprintf("data:%s;base64,", strings.Split(mime.TypeByExtension(ext), ";")[0])))

Check failure on line 139 in webapp/snapshot.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `out.Write` is not checked (errcheck)
bw := base64.NewEncoder(base64.StdEncoding, out)
io.Copy(bw, gres)
bw.Close()
res.Close()
}
} else {
out.Write(aVal)
}
} else {
out.Write(aVal)
}
out.Write([]byte(`"`))
if !moreAttr {
break
}
}
}

func attributeHasResource(name, val []byte) bool {
match := false
if bytes.Equal(name, []byte("img")) && bytes.Equal(name, []byte("src")) {
match = true
}
if bytes.Equal(name, []byte("link")) && bytes.Equal(name, []byte("href")) {
match = true
}
if bytes.Equal(name, []byte("iframe")) && bytes.Equal(name, []byte("src")) {
match = true
}
if match && bytes.HasPrefix(val, []byte("../../resources/")) {
return true
}
return false
}

func deleteSnapshot(c *gin.Context) {
u, _ := c.Get("user")
Expand Down

0 comments on commit b0d8851

Please sign in to comment.