Compare commits

..

1 Commits

Author SHA1 Message Date
Noah 6a36c0ed76 WIP Bindata stuff 2020-10-28 18:56:52 -07:00
13 changed files with 148 additions and 242 deletions

View File

@ -18,6 +18,14 @@ build:
gofmt -w .
go build $(LDFLAGS) -i -o bin/blog cmd/blog/main.go
# `make bindata` to make the bindata module.
# `make bindata-dev` for debug mode module for editing files locally.
.PHONY: bindata bindata-dev
bindata:
go-bindata -pkg root -prefix root/ -o src/root/bundle.go root/...
bindata-dev:
go-bindata -debug -pkg root -prefix root/ -o src/root/bundle.go root/...
# `make run` to run it in debug mode.
.PHONY: run
run:

View File

@ -29,7 +29,6 @@ import (
"github.com/kirsle/blog/src/middleware"
"github.com/kirsle/blog/src/middleware/auth"
"github.com/kirsle/blog/src/models"
"github.com/kirsle/blog/src/ratelimit"
"github.com/kirsle/blog/src/render"
"github.com/kirsle/blog/src/responses"
"github.com/kirsle/blog/src/sessions"
@ -124,7 +123,6 @@ func (b *Blog) Configure() {
b.Cache = cache
b.jsonDB.Cache = cache
markdown.Cache = cache
ratelimit.Cache = cache
}
}

27
go.mod
View File

@ -1,8 +1,9 @@
module github.com/kirsle/blog
go 1.22
go 1.12
require (
github.com/disintegration/imaging v1.6.0 // indirect
github.com/edwvee/exiffix v0.0.0-20180602190213-b57537c92a6b
github.com/garyburd/redigo v1.6.0
github.com/google/uuid v1.1.1
@ -13,24 +14,9 @@ require (
github.com/kirsle/golog v0.0.0-20180411020913-51290b4f9292
github.com/microcosm-cc/bluemonday v1.0.2
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470
github.com/urfave/negroni v1.0.0
golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
)
require (
github.com/disintegration/imaging v1.6.0 // indirect
github.com/gorilla/context v1.1.1 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/mattn/go-sqlite3 v1.10.0 // indirect
github.com/russross/blackfriday v1.5.2 // indirect
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd // indirect
github.com/sergi/go-diff v1.0.0 // indirect
github.com/shurcooL/go v0.0.0-20230706063926-5fe729b41b3a // indirect
github.com/shurcooL/go-goon v1.0.0 // indirect
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470
github.com/shurcooL/highlight_diff v0.0.0-20181222201841-111da2e7d480 // indirect
github.com/shurcooL/highlight_go v0.0.0-20181215221002-9d8641ddf2e1 // indirect
github.com/shurcooL/octicon v0.0.0-20181222203144-9ff1a4cf27f4 // indirect
@ -38,8 +24,7 @@ require (
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d // indirect
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e // indirect
github.com/tomnomnom/xtermcolor v0.0.0-20160428124646-b78803f00a7e // indirect
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81 // indirect
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 // indirect
golang.org/x/sys v0.0.0-20190412213103-97732733099d // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
github.com/urfave/negroni v1.0.0
golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
)

24
go.sum
View File

@ -1,6 +1,5 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
@ -10,10 +9,7 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 h1:tkum0XDgfR0jcVVXuTsYv/erY2NnEDqwRojbxR1rBYA=
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
github.com/disintegration/imaging v1.6.0 h1:nVPXRUUQ36Z7MNf0O77UzgnOb1mkMMor7lmJMJXc/mA=
github.com/disintegration/imaging v1.6.0/go.mod h1:xuIt+sRxDFrHS0drzXUlCJthkJ8k7lkkUojDSR247MQ=
@ -22,14 +18,12 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/edwvee/exiffix v0.0.0-20180602190213-b57537c92a6b h1:6CBzNasH8+bKeFwr5Bt5JtALHLFN4iQp7sf4ShlP/ik=
github.com/edwvee/exiffix v0.0.0-20180602190213-b57537c92a6b/go.mod h1:KoE3Ti1qbQXCb3s/XGj0yApHnbnNnn1bXTtB5Auq/Vc=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/garyburd/redigo v1.6.0 h1:0VruCpn7yAIIu7pWVClQC8wxCJEcG3nyzpMSHKi1PQc=
github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@ -63,7 +57,6 @@ github.com/jinzhu/gorm v1.9.9 h1:Gc8bP20O+vroFUzZEXA1r7vNGQZGQ+RKgOnriuNF3ds=
github.com/jinzhu/gorm v1.9.9/go.mod h1:Kh6hTsSGffh4ui079FHrR5Gg+5D0hgihqDcsDN2BBJY=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
@ -72,11 +65,6 @@ github.com/kirsle/golog v0.0.0-20180411020913-51290b4f9292/go.mod h1:0KaOvOX8s5Y
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
@ -91,9 +79,7 @@ github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
@ -103,8 +89,6 @@ github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK3dcGsnCnO41eRBOnY12zwkn5qVwgc=
@ -113,10 +97,6 @@ github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470 h1:qb9IthCFBmROJ6YBS31BEMeSYjOscSiG+EO+JVNTz64=
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
github.com/shurcooL/go v0.0.0-20230706063926-5fe729b41b3a h1:ZHfoO7ZJhws9NU1kzZhStUnnVQiPtDe1PzpUnc6HirU=
github.com/shurcooL/go v0.0.0-20230706063926-5fe729b41b3a/go.mod h1:DNrlr0AR9NsHD/aoc2pPeu4uSBZ/71yCHkR42yrzW3M=
github.com/shurcooL/go-goon v1.0.0 h1:BCQPvxGkHHJ4WpBO4m/9FXbITVIsvAm/T66cCcCGI7E=
github.com/shurcooL/go-goon v1.0.0/go.mod h1:2wTHMsGo7qnpmqA8ADYZtP4I1DD94JpXGQ3Dxq2YQ5w=
github.com/shurcooL/highlight_diff v0.0.0-20181222201841-111da2e7d480 h1:KaKXZldeYH73dpQL+Nr38j1r5BgpAYQjYvENOUpIZDQ=
github.com/shurcooL/highlight_diff v0.0.0-20181222201841-111da2e7d480/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
github.com/shurcooL/highlight_go v0.0.0-20181215221002-9d8641ddf2e1 h1:a6a6gGfBoO2ty+yyHNd7M6gkp37EwE3GIoycUnLo1Oo=
@ -131,7 +111,6 @@ github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:Udh
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e h1:qpG93cPwA5f7s/ZPBJnGOYQNK/vKsaDaseuKT5Asee8=
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/tomnomnom/xtermcolor v0.0.0-20160428124646-b78803f00a7e h1:Ee+VZw13r9NTOMnwTPs6O5KZ0MJU54hsxu9FpZ4pQ10=
github.com/tomnomnom/xtermcolor v0.0.0-20160428124646-b78803f00a7e/go.mod h1:fSIW/szJHsRts/4U8wlMPhs+YqJC+7NYR+Qqb1uJVpA=
@ -183,7 +162,6 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@ -191,8 +169,6 @@ google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRn
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=

View File

@ -1,7 +1,6 @@
package redis
import (
"encoding/json"
"fmt"
"time"
@ -45,16 +44,6 @@ func (r *Redis) Get(key string) ([]byte, error) {
return n, err
}
// GetJSON gets a JSON value from a Redis key.
func (r *Redis) GetJSON(key string, v any) error {
val, err := r.Get(key)
if err != nil {
return err
}
return json.Unmarshal(val, v)
}
// Set a key in Redis.
func (r *Redis) Set(key string, v []byte, expires int) error {
conn := r.pool.Get()
@ -66,16 +55,6 @@ func (r *Redis) Set(key string, v []byte, expires int) error {
return nil
}
// SetJSON sets a JSON encoded value into a Redis key.
func (r *Redis) SetJSON(key string, v any, expires int) error {
bin, err := json.Marshal(v)
if err != nil {
return err
}
return r.Set(key, bin, expires)
}
// Delete keys from Redis.
func (r *Redis) Delete(key ...string) {
conn := r.pool.Get()

View File

@ -3,7 +3,9 @@ package blog
import (
"html/template"
"io/ioutil"
"mime"
"net/http"
"path/filepath"
"strings"
"github.com/kirsle/blog/src/controllers/posts"
@ -11,6 +13,7 @@ import (
"github.com/kirsle/blog/src/markdown"
"github.com/kirsle/blog/src/render"
"github.com/kirsle/blog/src/responses"
"github.com/kirsle/blog/src/root"
)
// PageHandler is the catch-all route handler, for serving static web pages.
@ -31,7 +34,7 @@ func (b *Blog) PageHandler(w http.ResponseWriter, r *http.Request) {
}
// Search for a file that matches their URL.
filepath, err := render.ResolvePath(path)
fp, err := render.ResolvePath(path)
if err != nil {
// See if it resolves as a blog entry.
err = postctl.ViewPost(w, r, strings.TrimLeft(path, "/"))
@ -43,17 +46,30 @@ func (b *Blog) PageHandler(w http.ResponseWriter, r *http.Request) {
}
// Is it a template file?
if strings.HasSuffix(filepath.URI, ".gohtml") {
render.Template(w, r, filepath.URI, nil)
if strings.HasSuffix(fp.URI, ".gohtml") {
render.Template(w, r, fp.URI, nil)
return
}
// Is it a Markdown file?
if strings.HasSuffix(filepath.URI, ".md") || strings.HasSuffix(filepath.URI, ".markdown") {
source, err := ioutil.ReadFile(filepath.Absolute)
if err != nil {
responses.Error(w, r, "Couldn't read Markdown source!")
return
if strings.HasSuffix(fp.URI, ".md") || strings.HasSuffix(fp.URI, ".markdown") {
var source []byte
if len(fp.BindataKey) > 0 {
data, err := root.Asset(fp.BindataKey)
if err != nil {
responses.Error(w, r, "Couldn't read bindata key: "+fp.BindataKey)
return
}
source = data
} else {
data, err := ioutil.ReadFile(fp.Absolute)
if err != nil {
responses.Error(w, r, "Couldn't read Markdown source!")
return
}
source = data
}
// Render it to HTML and find out its title.
@ -64,10 +80,22 @@ func (b *Blog) PageHandler(w http.ResponseWriter, r *http.Request) {
render.Template(w, r, ".markdown", map[string]interface{}{
"Title": title,
"HTML": template.HTML(html),
"MarkdownPath": filepath.URI,
"MarkdownPath": fp.URI,
})
return
}
http.ServeFile(w, r, filepath.Absolute)
// It's a regular static file we can serve directly.
{
// Check if we have bindata for it.
if fp.BindataKey != "" {
data, _ := root.Asset(fp.BindataKey)
w.Header().Set("Content-Type", mime.TypeByExtension(filepath.Ext(fp.URI)))
w.Write(data)
return
}
// Try the filesystem.
http.ServeFile(w, r, fp.Absolute)
}
}

View File

@ -1,9 +1,7 @@
{{ define "title" }}{{ .Data.Title }}{{ end }}
{{ define "content" }}
<div class="markdown">
{{ .Data.HTML }}
</div>
{{ if and .CurrentUser.Admin .Editable }}
<p class="mt-4">

View File

@ -5,11 +5,10 @@ import (
"net/http"
"github.com/gorilla/mux"
"github.com/kirsle/blog/models/users"
"github.com/kirsle/blog/src/forms"
"github.com/kirsle/blog/src/log"
"github.com/kirsle/blog/src/middleware/auth"
"github.com/kirsle/blog/src/ratelimit"
"github.com/kirsle/blog/models/users"
"github.com/kirsle/blog/src/render"
"github.com/kirsle/blog/src/responses"
"github.com/kirsle/blog/src/sessions"
@ -45,20 +44,6 @@ func loginHandler(w http.ResponseWriter, r *http.Request) {
if err != nil {
vars["Error"] = err
} else {
// Rate limit by guessed username.
limiter := &ratelimit.Limiter{
Namespace: "login",
ID: form.Username,
Limit: 10,
Window: 3600,
CooldownAt: 3,
Cooldown: 10,
}
if err := limiter.Ping(); err != nil {
responses.FlashAndReload(w, r, err.Error())
return
}
// Test the login.
user, err := users.CheckAuth(form.Username, form.Password)
if err != nil {

View File

@ -8,11 +8,12 @@ import (
"net/url"
"strings"
"github.com/kirsle/blog/models/comments"
"github.com/kirsle/blog/models/settings"
"github.com/kirsle/blog/src/log"
"github.com/kirsle/blog/src/markdown"
"github.com/kirsle/blog/src/render"
"github.com/kirsle/blog/models/comments"
"github.com/kirsle/blog/models/settings"
"github.com/kirsle/blog/src/root"
"github.com/microcosm-cc/bluemonday"
gomail "gopkg.in/gomail.v2"
)
@ -51,10 +52,21 @@ func SendEmail(email Email) {
// Render the template to HTML.
var html bytes.Buffer
t := template.New(tmpl.Basename)
t, err = template.ParseFiles(tmpl.Absolute)
// Load it from bindata or filesystem.
if tmpl.BindataKey != "" {
log.Debug("Parse %s from bindata", tmpl.BindataKey)
asset, _ := root.Asset(tmpl.BindataKey)
t, err = t.Parse(string(asset))
} else {
log.Debug("Parse %s from file", tmpl.Absolute)
t, err = t.ParseFiles(tmpl.Absolute)
}
if err != nil {
log.Error("SendEmail: template parsing error: %s", err.Error())
}
err = t.ExecuteTemplate(&html, tmpl.Basename, email)
if err != nil {
log.Error("SendEmail: template execution error: %s", err.Error())

View File

@ -24,11 +24,6 @@ var ageGateSuffixes = []string{
".gif",
".mp4",
".webm",
".ttf",
".eot",
".svg",
".woff",
".woff2",
}
// AgeGate is a middleware generator that does age verification for NSFW sites.
@ -54,12 +49,6 @@ func AgeGate(verifyHandler func(http.ResponseWriter, *http.Request)) negroni.Han
}
}
// POST requests are allowed.
if r.Method == http.MethodPost {
next(w, r)
return
}
// See if they've been cleared.
session := sessions.Get(r)
if val, _ := session.Values["age-ok"].(bool); !val {

View File

@ -1,108 +0,0 @@
package ratelimit
import (
"errors"
"fmt"
"time"
"github.com/kirsle/blog/jsondb/caches/redis"
)
// Limiter implements a Redis-backed rate limit for logins or otherwise.
type Limiter struct {
Namespace string // kind of rate limiter ("login")
ID interface{} // unique ID of the resource being pinged (str or ints)
Limit int // how many pings within the window period
Window int // the window period/expiration of Redis key
CooldownAt int // how many pings before the cooldown is enforced
Cooldown int // time to wait between fails
}
// The active Redis cache given by the webapp.
var Cache *redis.Redis
// Redis object behind the rate limiter.
type Data struct {
Pings int
NotBefore time.Time
}
// Ping the rate limiter.
func (l *Limiter) Ping() error {
if Cache == nil {
return errors.New("redis not ready")
}
var (
key = l.Key()
now = time.Now()
)
// Get stored data from Redis if any.
var data Data
Cache.GetJSON(key, &data)
// Are we cooling down?
if now.Before(data.NotBefore) {
return fmt.Errorf(
"You are doing that too often.",
)
}
// Increment the ping count.
data.Pings++
// Have we hit the wall?
if data.Pings >= l.Limit {
return fmt.Errorf(
"You have hit the rate limit; come back later.",
)
}
// Are we throttled?
if l.CooldownAt > 0 && data.Pings > l.CooldownAt {
data.NotBefore = now.Add(time.Duration(l.Cooldown) * time.Second)
if err := Cache.SetJSON(key, data, l.Window); err != nil {
return fmt.Errorf("Couldn't set Redis key for rate limiter: %s", err)
}
return fmt.Errorf(
"Please wait %ds before trying again. You have %d more attempt(s) remaining before you will be locked "+
"out for %ds.",
l.Cooldown,
l.Limit-data.Pings,
l.Window,
)
}
// Save their ping count to Redis.
if err := Cache.SetJSON(key, data, l.Window); err != nil {
return fmt.Errorf("Couldn't set Redis key for rate limiter: %s", err)
}
return nil
}
// Clear the rate limiter, cleaning up the Redis key (e.g., after successful login).
func (l *Limiter) Clear() {
Cache.Delete(l.Key())
}
// Key formats the Redis key.
func (l *Limiter) Key() string {
var str string
switch t := l.ID.(type) {
case int:
str = fmt.Sprintf("%d", t)
case uint64:
str = fmt.Sprintf("%d", t)
case int64:
str = fmt.Sprintf("%d", t)
case uint32:
str = fmt.Sprintf("%d", t)
case int32:
str = fmt.Sprintf("%d", t)
default:
str = fmt.Sprintf("%s", t)
}
return fmt.Sprintf("rlimit/%s/%s", l.Namespace, str)
}

View File

@ -8,6 +8,7 @@ import (
"strings"
"github.com/kirsle/blog/src/log"
"github.com/kirsle/blog/src/root"
)
// Blog configuration bindings.
@ -37,6 +38,10 @@ type Filepath struct {
Basename string
Relative string // Relative path including document root (i.e. "root/about.html")
Absolute string // Absolute path on disk (i.e. "/opt/blog/root/about.html")
// If file was resolved to embedded bindata, this is the bindata key name.
// Zero value means it resolved to a file on filesystem.
BindataKey string
}
func (f Filepath) String() string {
@ -55,39 +60,78 @@ func ResolvePath(path string) (Filepath, error) {
// If you need to debug this function, edit this block.
debug := func(tmpl string, args ...interface{}) {
if false {
if true { // edit this to enable
log.Debug(tmpl, args...)
}
}
debug("Resolving filepath for URI: %s", path)
for _, root := range []string{*UserRoot, *DocumentRoot} {
if len(root) == 0 {
continue
}
debug("ResolvePath(%s) called", path)
if len(*UserRoot) > 0 {
debug("1. Resolving filepath for URI in user root: %s", path)
// Resolve the file path.
relPath := filepath.Join(root, path)
relPath := filepath.Join(*UserRoot, path)
absPath, err := filepath.Abs(relPath)
basename := filepath.Base(relPath)
if err != nil {
log.Error("%v", err)
}
debug("Expected filepath: %s", absPath)
debug(" Expected filepath: %s", absPath)
// Found an exact hit?
if stat, err := os.Stat(absPath); !os.IsNotExist(err) && !stat.IsDir() {
debug("Exact filepath found: %s", absPath)
return Filepath{path, basename, relPath, absPath}, nil
debug(" + Exact filepath found: %s", absPath)
return Filepath{
URI: path,
Basename: basename,
Relative: relPath,
Absolute: absPath,
}, nil
}
// Try some supported suffixes.
for _, suffix := range hiddenSuffixes {
test := absPath + suffix
if stat, err := os.Stat(test); !os.IsNotExist(err) && !stat.IsDir() {
debug("Filepath found via suffix %s: %s", suffix, test)
return Filepath{path + suffix, basename + suffix, relPath + suffix, test}, nil
debug(" + Filepath found via suffix %s: %s", suffix, test)
return Filepath{
URI: path + suffix,
Basename: basename + suffix,
Relative: relPath + suffix,
Absolute: test,
}, nil
}
}
}
debug("2. Not found in filesystem, checking bindata for: %s", path)
{
// Exact hit?
if _, err := root.Asset(path); err == nil {
debug(" Found in bindata as: %s", path)
return Filepath{
URI: path,
Basename: filepath.Base(path),
Relative: path,
Absolute: path,
BindataKey: path,
}, nil
}
// Try some supported suffixes.
for _, suffix := range hiddenSuffixes {
test := path + suffix
if _, err := root.Asset(test); err == nil {
debug(" Filepath found via suffix %s: %s", suffix, test)
return Filepath{
URI: test,
Basename: filepath.Base(test),
Relative: test,
Absolute: test,
BindataKey: test,
}, nil
}
}
}

View File

@ -8,13 +8,14 @@ import (
"time"
gorilla "github.com/gorilla/sessions"
"github.com/kirsle/blog/models/settings"
"github.com/kirsle/blog/models/users"
"github.com/kirsle/blog/src/log"
"github.com/kirsle/blog/src/middleware"
"github.com/kirsle/blog/src/middleware/auth"
"github.com/kirsle/blog/src/root"
"github.com/kirsle/blog/src/sessions"
"github.com/kirsle/blog/src/types"
"github.com/kirsle/blog/models/settings"
"github.com/kirsle/blog/models/users"
)
// Vars is an interface to implement by the templates to pass their own custom
@ -113,19 +114,19 @@ func Template(w io.Writer, r *http.Request, path string, data interface{}) error
// Get the layout template.
if !isPartial {
templateName = "layout"
layout, err = ResolvePath(".layout")
layout, err = ResolvePath(".layout.gohtml")
if err != nil {
log.Error("RenderTemplate(%s): layout template not found", path)
return err
}
} else {
templateName = filepath.Basename
templateName = filepath.Absolute
}
// The comment entry partial.
commentEntry, err := ResolvePath("comments/entry.partial")
commentEntry, err := ResolvePath("comments/entry.partial.gohtml")
if err != nil {
log.Error("RenderTemplate(%s): comments/entry.partial not found")
log.Error("RenderTemplate: comments/entry.partial not found")
return err
}
@ -135,17 +136,28 @@ func Template(w io.Writer, r *http.Request, path string, data interface{}) error
// and allows the filepath template to set the page title.
var templates []string
if !isPartial {
templates = append(templates, layout.Absolute)
templates = append(templates, layout.Absolute, commentEntry.Absolute, filepath.Absolute)
}
t, err = t.ParseFiles(append(templates, commentEntry.Absolute, filepath.Absolute)...)
if err != nil {
log.Error(err.Error())
return err
for _, filename := range templates {
if asset, err2 := root.Asset(filename); err2 == nil {
log.Debug("Parse %s from bindata", filename)
t, err = t.Parse(string(asset))
} else {
log.Debug("Parse %s from file", filename)
t, err = t.ParseFiles(filename)
}
if err != nil {
log.Error(err.Error())
return err
}
}
err = t.ExecuteTemplate(w, templateName, v)
if err != nil {
log.Error("Template parsing error: %s", err)
log.Error("Template parsing error(tmpl name: %s; ): %s", err)
return err
}