refactoring
This commit is contained in:
@@ -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
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"html/template"
|
"html/template"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
@@ -74,7 +75,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
defer stats.Store(n.ID, views+1)
|
defer stats.Store(n.ID, views+1)
|
||||||
if n.Fraud() {
|
if fraudelent(n) {
|
||||||
n.Ads = mdTmplHTML(ads)
|
n.Ads = mdTmplHTML(ads)
|
||||||
}
|
}
|
||||||
c.Logger().Debugf("/%q requested; response code: %d", n.ID, code)
|
c.Logger().Debugf("/%q requested; response code: %d", n.ID, code)
|
||||||
@@ -160,14 +161,10 @@ func get(vals url.Values, key string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func md2html(c echo.Context, name string) (*Note, int) {
|
func fraudelent(n *Note) bool {
|
||||||
path := "assets/markdown/" + name + ".md"
|
stripped := rexpLink.ReplaceAllString(n.Text, "")
|
||||||
mdContent, err := ioutil.ReadFile(path)
|
l1 := len(n.Text)
|
||||||
if err != nil {
|
l2 := len(stripped)
|
||||||
c.Logger().Errorf("couldn't open markdown page %q: %v", path, err)
|
return n.Views > 100 &&
|
||||||
code := http.StatusServiceUnavailable
|
int(math.Ceil(100*float64(l1-l2)/float64(l1))) > fraudThreshold
|
||||||
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 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-88
@@ -4,18 +4,13 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"math"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang-commonmark/markdown"
|
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -24,27 +19,8 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
idLength = 5
|
idLength = 5
|
||||||
statsSavingInterval = 1 * time.Minute
|
fraudThreshold = 7
|
||||||
fraudThreshold = 7
|
|
||||||
)
|
|
||||||
|
|
||||||
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")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Note struct {
|
type Note struct {
|
||||||
@@ -54,55 +30,6 @@ type Note struct {
|
|||||||
Content, Ads template.HTML
|
Content, Ads template.HTML
|
||||||
}
|
}
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func save(c echo.Context, db *sql.DB, n *Note) (*Note, error) {
|
func save(c echo.Context, db *sql.DB, n *Note) (*Note, error) {
|
||||||
if n.Password != "" {
|
if n.Password != "" {
|
||||||
n.Password = fmt.Sprintf("%x", sha256.Sum256([]byte(n.Password)))
|
n.Password = fmt.Sprintf("%x", sha256.Sum256([]byte(n.Password)))
|
||||||
@@ -201,16 +128,3 @@ func load(c echo.Context, db *sql.DB) (*Note, int) {
|
|||||||
}
|
}
|
||||||
return n, http.StatusOK
|
return n, http.StatusOK
|
||||||
}
|
}
|
||||||
|
|
||||||
var mdRenderer = markdown.New(markdown.HTML(true))
|
|
||||||
|
|
||||||
func mdTmplHTML(content []byte) template.HTML {
|
|
||||||
return template.HTML(mdRenderer.RenderToString(content))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *Note) Fraud() bool {
|
|
||||||
stripped := rexpLink.ReplaceAllString(n.Text, "")
|
|
||||||
l1 := len(n.Text)
|
|
||||||
l2 := len(stripped)
|
|
||||||
return int(math.Ceil(100*float64(l1-l2)/float64(l1))) > fraudThreshold
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user