Initial archive page

This commit is contained in:
Radhi Fadlillah 2019-08-05 18:26:37 +07:00
parent 3345d8db29
commit 43040a9bc4
7 changed files with 178 additions and 29 deletions

View file

@ -2,7 +2,7 @@
<html lang="en">
<head>
<title>Shiori - Bookmarks Manager</title>
<title>$$.Title$$ - Shiori - Bookmarks Manager</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
@ -24,11 +24,11 @@
<body>
<div id="content-scene" :class="{night: displayOptions.nightMode}">
<div id="header">
<p id="metadata">Added {{modified}} UTC</p>
<p id="title">{{title}}</p>
<p id="metadata">Added $$.Modified$$ UTC</p>
<p id="title">$$.Title$$</p>
<div id="links">
<a href="$$.URL$$" target="_blank">View Original</a>
<a href="$$.URL$$" target="_blank">View Archive</a>
<a href="/bookmark/$$.ID$$/archive" target="_blank">View Archive</a>
</div>
</div>
<div id="content">
@ -43,11 +43,6 @@
new Vue({
el: '#content-scene',
mixins: [basePage],
data: {
title: "$$.Title$$",
author: "$$.Author$$",
modified: "$$.Modified$$"
},
methods: {
loadSetting() {
var opts = JSON.parse(localStorage.getItem("shiori-setting")) || {},
@ -67,10 +62,6 @@
}
},
mounted() {
// Set title
document.title = `${this.title} - Shiori - Bookmarks Manager`;
// Load setting
this.loadSetting();
}
});

View file

@ -0,0 +1 @@
:root{--main:#F44336;--border:#E5E5E5;--colorLink:#999;--archiveHeaderBg:rgba(255,255,255,0.95)}@media (prefers-color-scheme:dark){:root{--border:#191919;--archiveHeaderBg:rgba(41,41,41,0.95)}}#shiori-archive-header{top:0;left:0;right:0;height:60px;position:fixed;padding:0 16px;display:-webkit-box;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;flex-flow:row wrap;-webkit-box-align:center;align-items:center;font-size:16px;border-bottom:1px solid var(--border);background-color:var(--archiveHeaderBg);z-index:9999}#shiori-archive-header *{border-width:0;box-sizing:border-box;font-family:"Source Sans Pro",sans-serif;margin:0;padding:0}#shiori-archive-header>*:not(:last-child){margin-right:8px}#shiori-archive-header>.spacer{-webkit-box-flex:1;flex:1}#shiori-archive-header #shiori-logo{font-size:2em;font-weight:100;color:var(--main)}#shiori-archive-header #shiori-logo span{margin-right:8px}#shiori-archive-header a{font-size:1em;display:block;color:var(--colorLink);text-decoration:underline}#shiori-archive-header a:hover,#shiori-archive-header a:focus{color:var(--main)}

View file

@ -0,0 +1,65 @@
:root {
--main: #F44336;
--border: #E5E5E5;
--colorLink: #999;
--archiveHeaderBg: rgba(255, 255, 255, 0.95);
@media (prefers-color-scheme: dark) {
--border: #191919;
--archiveHeaderBg: rgba(41, 41, 41, 0.95);
}
}
#shiori-archive-header {
top: 0;
left: 0;
right: 0;
height: 60px;
position: fixed;
padding: 0 16px;
display: flex;
flex-flow: row wrap;
align-items: center;
font-size: 16px;
border-bottom: 1px solid var(--border);
background-color: var(--archiveHeaderBg);
z-index: 9999;
* {
border-width: 0;
box-sizing: border-box;
font-family: "Source Sans Pro", sans-serif;
margin: 0;
padding: 0;
}
>*:not(:last-child) {
margin-right: 8px;
}
>.spacer {
flex: 1;
}
#shiori-logo {
font-size: 2em;
font-weight: 100;
color: var(--main);
span {
margin-right: 8px;
}
}
a {
font-size: 1em;
display: block;
color: var(--colorLink);
text-decoration: underline;
&:hover,
&:focus {
color: var(--main);
}
}
}

File diff suppressed because one or more lines are too long

View file

@ -1,17 +1,20 @@
package webserver
import (
"bytes"
"compress/gzip"
"fmt"
"html/template"
"io"
"net/http"
nurl "net/url"
"os"
"path"
fp "path/filepath"
"strconv"
"strings"
"github.com/PuerkitoBio/goquery"
"github.com/go-shiori/shiori/pkg/warc"
"github.com/julienschmidt/httprouter"
)
@ -85,13 +88,6 @@ func (h *handler) serveBookmarkContent(w http.ResponseWriter, r *http.Request, p
"html": func(s string) template.HTML {
return template.HTML(s)
},
"hostname": func(s string) string {
parsed, err := nurl.ParseRequestURI(s)
if err != nil || len(parsed.Scheme) == 0 {
return s
}
return parsed.Hostname()
},
}
tplCache, err := createTemplate("content.html", funcMap)
@ -126,3 +122,80 @@ func (h *handler) serveThumbnailImage(w http.ResponseWriter, r *http.Request, ps
_, err = io.Copy(w, img)
checkError(err)
}
// serveBookmarkArchive is handler for GET /bookmark/:id/archive/*filepath
func (h *handler) serveBookmarkArchive(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
// Get parameter from URL
strID := ps.ByName("id")
resourcePath := ps.ByName("filepath")
resourcePath = strings.TrimPrefix(resourcePath, "/")
// Get bookmark from database
id, err := strconv.Atoi(strID)
checkError(err)
bookmark, exist := h.DB.GetBookmark(id, "")
if !exist {
panic(fmt.Errorf("Bookmark not found"))
}
// Open archive, look in cache first
var archive *warc.Archive
cacheData, found := h.ArchiveCache.Get(strID)
if found {
archive = cacheData.(*warc.Archive)
} else {
archivePath := fp.Join(h.DataDir, "archive", strID)
archive, err = warc.Open(archivePath)
checkError(err)
h.ArchiveCache.Set(strID, archive, 0)
}
content, contentType, err := archive.Read(resourcePath)
checkError(err)
// Set response header
w.Header().Set("Content-Encoding", "gzip")
w.Header().Set("Content-Type", contentType)
// If this is HTML and root, inject shiori header
if strings.Contains(strings.ToLower(contentType), "text/html") && resourcePath == "" {
// Extract gzip
buffer := bytes.NewBuffer(content)
gzipReader, err := gzip.NewReader(buffer)
checkError(err)
// Parse gzipped content
doc, err := goquery.NewDocumentFromReader(gzipReader)
checkError(err)
// Add Shiori overlay
headerHTML := fmt.Sprintf(
`<div id="shiori-archive-header">
<p id="shiori-logo">
<span></span>shiori
</p>
<div class="spacer"></div>
<a href="%s" target="_blank">View Original</a>
<a href="/bookmark/%s/content" target="_blank">View Readable</a>
</div>`, bookmark.URL, strID)
doc.Find("head").AppendHtml(`<link href="/css/archive.css" rel="stylesheet">`)
doc.Find("body").PrependHtml(headerHTML)
// Revert back to HTML
outerHTML, err := goquery.OuterHtml(doc.Selection)
checkError(err)
// Gzip it again and send to response writer
gzipWriter := gzip.NewWriter(w)
gzipWriter.Write([]byte(outerHTML))
gzipWriter.Flush()
return
}
// Serve content
w.Write(content)
}

View file

@ -16,6 +16,7 @@ type handler struct {
DataDir string
UserCache *cch.Cache
SessionCache *cch.Cache
ArchiveCache *cch.Cache
}
// prepareLoginCache prepares login cache for future use

View file

@ -21,6 +21,7 @@ func ServeApp(DB database.DB, dataDir string, port int) error {
DataDir: dataDir,
UserCache: cch.New(time.Hour, 10*time.Minute),
SessionCache: cch.New(time.Hour, 10*time.Minute),
ArchiveCache: cch.New(time.Minute, 5*time.Minute),
}
// Create router
@ -35,6 +36,7 @@ func ServeApp(DB database.DB, dataDir string, port int) error {
router.GET("/login", hdl.serveLoginPage)
router.GET("/bookmark/:id/thumb", hdl.serveThumbnailImage)
router.GET("/bookmark/:id/content", hdl.serveBookmarkContent)
router.GET("/bookmark/:id/archive/*filepath", hdl.serveBookmarkArchive)
router.POST("/api/login", hdl.apiLogin)
router.POST("/api/logout", hdl.apiLogout)