4 changed files with 120 additions and 99 deletions
@ -0,0 +1,75 @@
@@ -0,0 +1,75 @@
|
||||
package main |
||||
|
||||
import ( |
||||
"errors" |
||||
"fmt" |
||||
"html/template" |
||||
"io/ioutil" |
||||
"net/http" |
||||
"regexp" |
||||
"strings" |
||||
|
||||
"github.com/golang-commonmark/markdown" |
||||
"github.com/labstack/echo" |
||||
) |
||||
|
||||
var ( |
||||
errorCodes = map[int]string{ |
||||
400: "Bad request", |
||||
401: "Unauthorized", |
||||
404: "Not found", |
||||
412: "Precondition failed", |
||||
503: "Service unavailable", |
||||
} |
||||
|
||||
rexpNewLine = regexp.MustCompile("[\n\r]") |
||||
rexpNonAlphaNum = regexp.MustCompile("[`~!@#$%^&*_|+=?;:'\",.<>{}\\/]") |
||||
rexpNoScriptIframe = regexp.MustCompile("<.*?(iframe|script).*?>") |
||||
rexpLink = regexp.MustCompile("(ht|f)tp://[^\\s]+") |
||||
|
||||
errorUnathorised = errors.New("id or password is wrong") |
||||
errorBadRequest = errors.New("password is empty") |
||||
) |
||||
|
||||
func errPage(code int, details ...string) *Note { |
||||
text := errorCodes[code] |
||||
body := text |
||||
if len(details) > 0 { |
||||
body += ": " + strings.Join(details, ";") |
||||
} |
||||
n := &Note{ |
||||
Title: text, |
||||
Text: fmt.Sprintf("# %d %s", code, body), |
||||
} |
||||
n.prepare() |
||||
return n |
||||
} |
||||
|
||||
func (n *Note) prepare() { |
||||
fstLine := rexpNewLine.Split(n.Text, -1)[0] |
||||
maxLength := 25 |
||||
if len(fstLine) < 25 { |
||||
maxLength = len(fstLine) |
||||
} |
||||
n.Text = rexpNoScriptIframe.ReplaceAllString(n.Text, "") |
||||
n.Title = strings.TrimSpace(rexpNonAlphaNum.ReplaceAllString(fstLine[:maxLength], "")) |
||||
n.Content = mdTmplHTML([]byte(n.Text)) |
||||
} |
||||
|
||||
var mdRenderer = markdown.New(markdown.HTML(true)) |
||||
|
||||
func mdTmplHTML(content []byte) template.HTML { |
||||
return template.HTML(mdRenderer.RenderToString(content)) |
||||
} |
||||
|
||||
func md2html(c echo.Context, name string) (*Note, int) { |
||||
path := "assets/markdown/" + name + ".md" |
||||
mdContent, err := ioutil.ReadFile(path) |
||||
if err != nil { |
||||
c.Logger().Errorf("couldn't open markdown page %q: %v", path, err) |
||||
code := http.StatusServiceUnavailable |
||||
return errPage(code), code |
||||
} |
||||
c.Logger().Debugf("rendering markdown page %q", name) |
||||
return &Note{Title: name, Content: mdTmplHTML(mdContent)}, http.StatusOK |
||||
} |
||||
@ -0,0 +1,35 @@
@@ -0,0 +1,35 @@
|
||||
package main |
||||
|
||||
import ( |
||||
"database/sql" |
||||
"sync" |
||||
"time" |
||||
|
||||
"github.com/labstack/echo" |
||||
) |
||||
|
||||
const statsSavingInterval = 1 * time.Minute |
||||
|
||||
func persistStats(logger echo.Logger, db *sql.DB, stats *sync.Map) { |
||||
for { |
||||
time.Sleep(statsSavingInterval) |
||||
tx, err := db.Begin() |
||||
if err != nil { |
||||
logger.Error(err) |
||||
return |
||||
} |
||||
c := 0 |
||||
stats.Range(func(id, views interface{}) bool { |
||||
stmt, _ := tx.Prepare("update notes set views = ? where id = ?") |
||||
_, err := stmt.Exec(views, id) |
||||
if err == nil { |
||||
c++ |
||||
} |
||||
stmt.Close() |
||||
defer stats.Delete(id) |
||||
return true |
||||
}) |
||||
tx.Commit() |
||||
logger.Infof("successfully persisted %d values", c) |
||||
} |
||||
} |
||||
Loading…
Reference in new issue