Browse Source

Merge branch 'develop'

master v0.7.17
Lars Hoogestraat 7 months ago
parent
commit
385c1207d3
59 changed files with 511 additions and 625 deletions
  1. +0
    -3
      .gitignore
  2. +7
    -1
      Makefile
  3. +27
    -33
      clt/createuser/create_user.go
  4. +2
    -2
      clt/initdatabase/init_database.go
  5. +18
    -33
      crypt/crypt.go
  6. +2
    -7
      database/database.go
  7. +7
    -8
      go.mod
  8. +20
    -19
      go.sum
  9. +11
    -11
      handler/account.go
  10. +5
    -5
      handler/account_test.go
  11. +6
    -5
      handler/article.go
  12. +10
    -10
      handler/article_test.go
  13. +2
    -2
      handler/category.go
  14. +7
    -7
      handler/category_test.go
  15. +1
    -1
      handler/const.go
  16. +3
    -3
      handler/file.go
  17. +6
    -6
      handler/file_test.go
  18. +2
    -2
      handler/form_util.go
  19. +1
    -1
      handler/login.go
  20. +3
    -3
      handler/login_test.go
  21. +2
    -2
      handler/site.go
  22. +10
    -10
      handler/site_test.go
  23. +0
    -0
      handler/testdata/color.png
  24. +6
    -6
      handler/user.go
  25. +1
    -1
      handler/user_invite.go
  26. +5
    -5
      handler/user_invite_test.go
  27. +6
    -6
      handler/user_test.go
  28. +8
    -8
      handler/utils_test.go
  29. +0
    -17
      httperror/error.go
  30. +3
    -8
      logger/log.go
  31. +1
    -1
      mail/mail.go
  32. +3
    -3
      main.go
  33. +17
    -17
      middleware/json_handler.go
  34. +10
    -6
      middleware/middleware_util.go
  35. +6
    -8
      middleware/template.go
  36. +12
    -10
      middleware/template_handler.go
  37. +5
    -5
      middleware/xml_handler.go
  38. +17
    -19
      models/article.go
  39. +4
    -3
      models/article_sqlite.go
  40. +6
    -6
      models/category.go
  41. +3
    -3
      models/category_sqlite.go
  42. +37
    -4
      models/file.go
  43. +1
    -4
      models/file_sqlite.go
  44. +2
    -2
      models/html.go
  45. +3
    -4
      models/mail.go
  46. +13
    -15
      models/pagination.go
  47. +5
    -4
      models/site.go
  48. +4
    -8
      models/site_sqlite.go
  49. +1
    -1
      models/sql.go
  50. +9
    -7
      models/token.go
  51. +23
    -19
      models/user.go
  52. +5
    -5
      models/user_invite.go
  53. +5
    -10
      models/user_invite_sqlite.go
  54. +4
    -12
      models/user_sqlite.go
  55. +78
    -78
      routers/router.go
  56. +3
    -7
      settings/config.go
  57. +50
    -0
      slug/slug.go
  58. +3
    -3
      slug/slug_test.go
  59. +0
    -136
      utils/strings.go

+ 0
- 3
.gitignore View File

@ -32,9 +32,6 @@ releases
.idea
.csrftoken
/clt/createUser
/clt/initDatabase
/go-blog
releases
db/


+ 7
- 1
Makefile View File

@ -1,8 +1,14 @@
BINARYNAME=go-blog
TMP=tmp
DIST=release
GITHASH=$(shell git rev-parse HEAD)
BUILD_VERSION=$(shell git describe --tags)
ifndef $(GOPATH)
GOPATH=$(shell go env GOPATH)
export GOPATH
endif
RELEASE="releases"
LDFLAGS=-ldflags '-X main.BuildVersion=${BUILD_VERSION} -X main.GitHash=${GITHASH}'
@ -24,7 +30,7 @@ install:
package:
-rm -r ${TMP}
mkdir -p ${TMP}/clt
-mkdir -p releases
-mkdir -p releases/custom
cp ${GOPATH}/bin/go-blog ${TMP}/
cp ${GOPATH}/bin/create_user ${TMP}/clt
cp ${GOPATH}/bin/init_database ${TMP}/clt


+ 27
- 33
clt/createuser/create_user.go View File

@ -12,10 +12,9 @@ import (
"os"
"syscall"
"git.hoogi.eu/snafu/go-blog/components/database"
"git.hoogi.eu/snafu/go-blog/components/logger"
"git.hoogi.eu/snafu/go-blog/database"
"git.hoogi.eu/snafu/go-blog/logger"
"git.hoogi.eu/snafu/go-blog/models"
"git.hoogi.eu/snafu/go-blog/utils"
"golang.org/x/crypto/ssh/terminal"
)
@ -38,14 +37,31 @@ func main() {
fmt.Printf("create_user version %s\n", BuildVersion)
username := flag.String("username", "", "Username for the admin user ")
email := flag.String("email", "", "Email for the created user ")
displayName := flag.String("displayname", "", "Display name for the admin user ")
isAdmin := flag.Bool("admin", false, "If set a new administrator will be created; otherwise a non-admin is created")
file := flag.String("sqlite", "", "Location to the sqlite3 database file")
username := flag.String("username", "", "Username for the admin user. (required)")
email := flag.String("email", "", "Email for the created user. (required)")
displayName := flag.String("displayname", "", "Display name for the admin user. (required)")
isAdmin := flag.Bool("admin", false, "If set a new user with admin permissions will be created; otherwise a non-admin is created.")
file := flag.String("sqlite", "", "Location to the sqlite3 database file. (required)")
flag.Parse()
if *username == "" {
fmt.Println("the username (-username) must be specified")
os.Exit(1)
}
if *email == "" {
fmt.Println("the email (-email) must be specified")
os.Exit(1)
}
if *displayName == "" {
fmt.Println("the display name (-displayname) must be specified")
os.Exit(1)
}
if *file == "" {
fmt.Println("the argument -sqlite is empty. Please specify the location of the sqlite3 database file")
os.Exit(1)
}
if flag.Parsed() {
initUser := createUserFlag{
username: *username,
@ -55,16 +71,13 @@ func main() {
sqlite: *file,
}
if err := initUser.validate(); err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Printf("Password: ")
pw, err := terminal.ReadPassword(int(syscall.Stdin))
fmt.Println("")
if err != nil {
fmt.Printf("could not read password %v", err)
fmt.Printf("could not read password %v\n", err)
os.Exit(1)
}
@ -85,26 +98,7 @@ func main() {
}
}
func (userFlags createUserFlag) validate() error {
if utils.TrimmedStringIsEmpty(userFlags.username) {
return fmt.Errorf("the username (-username) must be specified")
}
if utils.TrimmedStringIsEmpty(userFlags.email) {
return fmt.Errorf("the email (-email) must be specified")
}
if utils.TrimmedStringIsEmpty(userFlags.displayName) {
return fmt.Errorf("the display name (-displayname) must be specified")
}
if utils.TrimmedStringIsEmpty(userFlags.sqlite) {
return fmt.Errorf("the argument -sqlite is empty. Please specify the location of the sqlite3 database file")
}
return nil
}
func (userFlags createUserFlag) CreateUser() error {
var userService models.UserService
dbConfig := database.SQLiteConfig{


+ 2
- 2
clt/initdatabase/init_database.go View File

@ -12,8 +12,8 @@ import (
"os"
"strings"
"git.hoogi.eu/snafu/go-blog/components/database"
"git.hoogi.eu/snafu/go-blog/components/logger"
"git.hoogi.eu/snafu/go-blog/database"
"git.hoogi.eu/snafu/go-blog/logger"
)
var (


utils/crypt.go → crypt/crypt.go View File


components/database/database.go → database/database.go View File


+ 7
- 8
go.mod View File

@ -4,17 +4,16 @@ go 1.15
require (
git.hoogi.eu/snafu/cfg v1.0.6
git.hoogi.eu/snafu/session v1.1.2
git.hoogi.eu/snafu/session v1.2.0
github.com/gorilla/csrf v1.7.0
github.com/gorilla/handlers v1.5.1
github.com/gorilla/mux v1.8.0
github.com/justinas/alice v1.2.0
github.com/mattn/go-sqlite3 v1.14.3
github.com/mattn/go-sqlite3 v1.14.5
github.com/microcosm-cc/bluemonday v1.0.4
github.com/russross/blackfriday/v2 v2.0.1
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/sirupsen/logrus v1.6.0
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
golang.org/x/net v0.0.0-20200904194848-62affa334b73 // indirect
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 // indirect
github.com/russross/blackfriday/v2 v2.1.0
github.com/sirupsen/logrus v1.7.0
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb // indirect
golang.org/x/sys v0.0.0-20201204225414-ed752295db88 // indirect
)

+ 20
- 19
go.sum View File

@ -2,6 +2,8 @@ git.hoogi.eu/snafu/cfg v1.0.6 h1:O34hYFqjwfnMjEwB4M8GaQQmBtf3H+AA0RFHb7PoMNs=
git.hoogi.eu/snafu/cfg v1.0.6/go.mod h1:LQolv8bqH8ZPz7h9PSVswdBXj1BIM+kW79AVS/jpE7A=
git.hoogi.eu/snafu/session v1.1.2 h1:MclTbSqD/9JodRUqFc4OwyJGk5AtonqB6BaG5RRJHf8=
git.hoogi.eu/snafu/session v1.1.2/go.mod h1:kgRDrnHcKc9H18G9533BXy6qO+81eBf6e9gkUzBMDuA=
git.hoogi.eu/snafu/session v1.2.0 h1:qmZXMCAZRrzcOOLsOltwPUeA4ckpjwjjJ8fQpFDSx+w=
git.hoogi.eu/snafu/session v1.2.0/go.mod h1:kgRDrnHcKc9H18G9533BXy6qO+81eBf6e9gkUzBMDuA=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/chris-ramon/douceur v0.2.0 h1:IDMEdxlEUUBYBKE4z/mJnFyVXox+MjuEVDJNN27glkU=
@ -22,37 +24,36 @@ github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyC
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo=
github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA=
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/mattn/go-sqlite3 v1.14.3 h1:j7a/xn1U6TKA/PHHxqZuzh64CdtRc7rU9M+AvkOl5bA=
github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
github.com/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ=
github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
github.com/microcosm-cc/bluemonday v1.0.4 h1:p0L+CTpo/PLFdkoPcJemLXG+fpMD7pYOoDEq1axMbGg=
github.com/microcosm-cc/bluemonday v1.0.4/go.mod h1:8iwZnFn2CDDNZ0r6UXhF4xawGvzaqzCRa1n3/lO3W2w=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/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/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c h1:9HhBz5L/UjnK9XLtiZhYAdue5BVKep3PMmS2LuPDt8k=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA=
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 h1:W0lCpv29Hv0UaM1LXb9QlBHLNP8UFfcKjblhVCWftOM=
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88 h1:KmZPnMocC93w341XZp26yTJg8Za7lhb2KhkYmixoeso=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

controllers/account.go → handler/account.go View File


controllers/account_test.go → handler/account_test.go View File


controllers/article.go → handler/article.go View File


controllers/article_test.go → handler/article_test.go View File


controllers/category.go → handler/category.go View File


controllers/category_test.go → handler/category_test.go View File


controllers/const.go → handler/const.go View File


controllers/file.go → handler/file.go View File


controllers/file_test.go → handler/file_test.go View File


controllers/form_util.go → handler/form_util.go View File


controllers/login.go → handler/login.go View File


controllers/login_test.go → handler/login_test.go View File


controllers/site.go → handler/site.go View File


controllers/site_test.go → handler/site_test.go View File


controllers/testdata/color.png → handler/testdata/color.png View File


controllers/user.go → handler/user.go View File


controllers/user_invite.go → handler/user_invite.go View File


controllers/user_invite_test.go → handler/user_invite_test.go View File


controllers/user_test.go → handler/user_test.go View File


controllers/utils_test.go → handler/utils_test.go View File


components/httperror/error.go → httperror/error.go View File


components/logger/log.go → logger/log.go View File


components/mail/mail.go → mail/mail.go View File


+ 3
- 3
main.go View File

@ -13,9 +13,9 @@ import (
"time"
"git.hoogi.eu/snafu/cfg"
"git.hoogi.eu/snafu/go-blog/components/database"
"git.hoogi.eu/snafu/go-blog/components/logger"
"git.hoogi.eu/snafu/go-blog/components/mail"
"git.hoogi.eu/snafu/go-blog/database"
"git.hoogi.eu/snafu/go-blog/logger"
"git.hoogi.eu/snafu/go-blog/mail"
m "git.hoogi.eu/snafu/go-blog/middleware"
"git.hoogi.eu/snafu/go-blog/models"
"git.hoogi.eu/snafu/go-blog/routers"


+ 17
- 17
middleware/json_handler.go View File

@ -8,8 +8,8 @@ import (
"encoding/json"
"net/http"
"git.hoogi.eu/snafu/go-blog/components/httperror"
"git.hoogi.eu/snafu/go-blog/components/logger"
"git.hoogi.eu/snafu/go-blog/httperror"
"git.hoogi.eu/snafu/go-blog/logger"
"git.hoogi.eu/snafu/go-blog/models"
)
@ -23,7 +23,7 @@ type JSONHandler struct {
type JHandler func(*AppContext, http.ResponseWriter, *http.Request) (*models.JSONData, error)
func (fn JSONHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
statusCode := 200
code := http.StatusOK
rw.Header().Set("Content-Type", "application/json")
@ -32,34 +32,34 @@ func (fn JSONHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
if err != nil {
switch e := err.(type) {
case *httperror.Error:
statusCode = e.HTTPStatus
code = e.HTTPStatus
default:
statusCode = 500
code = http.StatusInternalServerError
logger.Log.Error(e)
}
logger.Log.Error(err)
mjson, err2 := json.Marshal(err)
if err2 != nil {
logger.Log.Error(err2)
http.Error(rw, err2.Error(), http.StatusInternalServerError)
j, err := json.Marshal(err)
if err != nil {
logger.Log.Error(err)
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}
rw.WriteHeader(statusCode)
rw.Write(mjson)
rw.WriteHeader(code)
rw.Write(j)
return
}
mjson, err2 := json.Marshal(data)
j, err := json.Marshal(data)
if err2 != nil {
http.Error(rw, err2.Error(), http.StatusInternalServerError)
rw.WriteHeader(500)
if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError)
rw.WriteHeader(http.StatusInternalServerError)
return
}
rw.WriteHeader(statusCode)
rw.Write(mjson)
rw.WriteHeader(code)
rw.Write(j)
}

+ 10
- 6
middleware/middleware_util.go View File

@ -5,13 +5,13 @@
package middleware
import (
"encoding/base64"
"net"
"net/http"
"strings"
"time"
"git.hoogi.eu/snafu/go-blog/components/logger"
"git.hoogi.eu/snafu/go-blog/utils"
"git.hoogi.eu/snafu/go-blog/logger"
)
var locals = [...]net.IPNet{
@ -72,7 +72,7 @@ func setCookie(rw http.ResponseWriter, name, path, data string) {
c := &http.Cookie{
Name: name,
Path: path,
Value: utils.EncodeBase64(data),
Value: base64.StdEncoding.EncodeToString([]byte(data)),
}
http.SetCookie(rw, c)
@ -80,6 +80,7 @@ func setCookie(rw http.ResponseWriter, name, path, data string) {
func getFlash(w http.ResponseWriter, r *http.Request, name string) (string, error) {
c, err := r.Cookie(name)
if err != nil {
switch err {
case http.ErrNoCookie:
@ -88,7 +89,9 @@ func getFlash(w http.ResponseWriter, r *http.Request, name string) (string, erro
return "", err
}
}
value, err := utils.DecodeBase64(c.Value)
value, err := base64.StdEncoding.DecodeString(c.Value)
if err != nil {
return "", err
}
@ -98,9 +101,10 @@ func getFlash(w http.ResponseWriter, r *http.Request, name string) (string, erro
Name: name,
MaxAge: -1,
Expires: time.Unix(1, 0),
Path: "/"}
Path: "/",
}
http.SetCookie(w, dc)
return value, nil
return string(value), nil
}

+ 6
- 8
middleware/template.go View File

@ -16,11 +16,10 @@ import (
"time"
"git.hoogi.eu/snafu/cfg"
"git.hoogi.eu/snafu/go-blog/components/httperror"
"git.hoogi.eu/snafu/go-blog/components/logger"
"git.hoogi.eu/snafu/go-blog/httperror"
"git.hoogi.eu/snafu/go-blog/logger"
"git.hoogi.eu/snafu/go-blog/models"
"git.hoogi.eu/snafu/go-blog/settings"
"git.hoogi.eu/snafu/go-blog/utils"
)
// Template contains the information about the template to render.
@ -50,6 +49,7 @@ func NotFound(ctx *AppContext, rw http.ResponseWriter, r *http.Request) *Templat
getFlash(rw, r, "SuccessMsg")
session, _ := ctx.SessionService.Get(rw, r)
if session != nil && strings.HasPrefix(r.URL.EscapedPath(), "/admin") {
return &Template{
Name: "admin/error",
@ -157,8 +157,7 @@ func FuncMap(ss models.SiteService, settings *settings.Settings) template.FuncMa
return t.Time.In(time.Local).Format("January 2, 2006 at 3:04 PM")
},
"HumanizeFilesize": func(size int64) string {
fs := cfg.FileSize(size)
return fs.HumanReadable()
return cfg.FileSize(size).HumanReadable()
},
"FormatDateTime": func(t time.Time) string {
return t.In(time.Local).Format("January 2, 2006 at 3:04 PM")
@ -185,8 +184,7 @@ func FuncMap(ss models.SiteService, settings *settings.Settings) template.FuncMa
return template.HTML(models.MarkdownToHTML([]byte(s)))
},
"NToBr": func(in string) template.HTML {
out := models.NewlineToBr(models.EscapeHTML(in))
return template.HTML(out)
return template.HTML(models.NewlineToBr(models.EscapeHTML(in)))
},
"EscapeHTML": func(in string) string {
return html.EscapeString(in)
@ -234,5 +232,5 @@ func (t Template) RedirectURL() string {
if t.RedirectPath[0] == byte('/') {
return t.RedirectPath
}
return utils.AppendString("/", t.RedirectPath)
return "/" + t.RedirectPath
}

+ 12
- 10
middleware/template_handler.go View File

@ -12,8 +12,8 @@ import (
"github.com/gorilla/csrf"
"git.hoogi.eu/snafu/go-blog/components/httperror"
"git.hoogi.eu/snafu/go-blog/components/logger"
"git.hoogi.eu/snafu/go-blog/httperror"
"git.hoogi.eu/snafu/go-blog/logger"
"git.hoogi.eu/snafu/go-blog/models"
)
@ -34,7 +34,8 @@ type Handler func(*AppContext, http.ResponseWriter, *http.Request) *Template
func (fn TemplateHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
var errorMsg, warnMsg, successMsg string
statusCode := 200
code := http.StatusOK
ip := getIP(r)
en := logger.Log.WithField("ip", ip)
@ -60,7 +61,7 @@ func (fn TemplateHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
switch e := t.Err.(type) {
case *httperror.Error:
statusCode = e.HTTPStatus
code = e.HTTPStatus
en.Error(e)
errorMsg = e.DisplayMsg
default:
@ -99,14 +100,14 @@ func (fn TemplateHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
t.Data[csrf.TemplateTag] = csrf.TemplateField(r)
t.Data["active"] = t.Active
rw.WriteHeader(statusCode)
rw.WriteHeader(code)
if err := fn.AppCtx.Templates.ExecuteTemplate(rw, t.Name, t.Data); err != nil {
en.Error(err)
http.Error(rw, err.Error(), http.StatusInternalServerError)
}
} else {
statusCode = http.StatusFound
code = http.StatusFound
if len(errorMsg) > 0 {
setCookie(rw, "ErrorMsg", "/", errorMsg)
} else if len(warnMsg) > 0 {
@ -114,7 +115,7 @@ func (fn TemplateHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
} else if len(successMsg) > 0 {
setCookie(rw, "SuccessMsg", "/", successMsg)
}
http.Redirect(rw, r, path.Clean(t.RedirectURL()), statusCode)
http.Redirect(rw, r, path.Clean(t.RedirectURL()), code)
}
}
@ -135,6 +136,7 @@ func (ctx AppContext) AuthHandler(handler http.Handler) http.Handler {
}
userid, ok := session.GetValue("userid").(int)
if !ok {
logger.Log.Errorf("userid is not an integer %v", userid)
@ -148,6 +150,7 @@ func (ctx AppContext) AuthHandler(handler http.Handler) http.Handler {
}
u, err := ctx.UserService.GetByID(userid)
if err != nil {
logger.Log.Error(err)
rw.WriteHeader(http.StatusUnauthorized)
@ -159,8 +162,7 @@ func (ctx AppContext) AuthHandler(handler http.Handler) http.Handler {
return
}
ctx := context.WithValue(r.Context(), UserContextKey, u)
handler.ServeHTTP(rw, r.WithContext(ctx))
handler.ServeHTTP(rw, r.WithContext(context.WithValue(r.Context(), UserContextKey, u)))
}
return http.HandlerFunc(fn)
}
@ -195,7 +197,7 @@ func (ctx AppContext) RequireAdmin(handler http.Handler) http.Handler {
func User(r *http.Request) (*models.User, error) {
v := r.Context().Value(UserContextKey)
if v == nil {
return nil, httperror.InternalServerError(errors.New("user is not available in context. is the authentication handler in chain?"))
return nil, httperror.InternalServerError(errors.New("user is not available in context"))
}
return v.(*models.User), nil


+ 5
- 5
middleware/xml_handler.go View File

@ -5,7 +5,7 @@ import (
"encoding/xml"
"net/http"
"git.hoogi.eu/snafu/go-blog/components/logger"
"git.hoogi.eu/snafu/go-blog/logger"
"git.hoogi.eu/snafu/go-blog/models"
)
@ -26,11 +26,11 @@ func (fn XMLHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
if err != nil {
logger.Log.Error(err)
x, err2 := xml.Marshal(err)
x, err := xml.Marshal(err)
if err2 != nil {
logger.Log.Error(err2)
http.Error(rw, err2.Error(), http.StatusInternalServerError)
if err != nil {
logger.Log.Error(err)
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}


+ 17
- 19
models/article.go View File

@ -14,9 +14,9 @@ import (
"strings"
"time"
"git.hoogi.eu/snafu/go-blog/components/httperror"
"git.hoogi.eu/snafu/go-blog/httperror"
"git.hoogi.eu/snafu/go-blog/settings"
"git.hoogi.eu/snafu/go-blog/utils"
"git.hoogi.eu/snafu/go-blog/slug"
)
// Article represents an article
@ -60,17 +60,21 @@ func (a Article) SlugEscape() string {
}
func (a *Article) buildSlug(now time.Time, suffix int) string {
return utils.AppendString(strconv.Itoa(now.Year()), "/", strconv.Itoa(int(now.Month())), "/", utils.CreateURLSafeSlug(a.Headline, suffix))
var sb strings.Builder
sb.WriteString(strconv.Itoa(now.Year()))
sb.WriteString("/")
sb.WriteString(strconv.Itoa(int(now.Month())))
sb.WriteString("/")
sb.WriteString(slug.CreateURLSafeSlug(a.Headline, suffix))
return sb.String()
}
func (a *Article) slug(as ArticleService, now time.Time) error {
for i := 0; i < 10; i++ {
a.Slug = a.buildSlug(now, i)
_, err := as.Datasource.GetBySlug(a.Slug, nil, All)
if err != nil {
if err == sql.ErrNoRows {
if _, err := as.Datasource.GetBySlug(a.Slug, nil, All); err != nil {
if errors.Is(err, sql.ErrNoRows) {
break
}
return err
@ -118,18 +122,11 @@ func (as ArticleService) Create(a *Article) (int, error) {
return 0, err
}
err := a.slug(as, now)
if err != nil {
if err := a.slug(as, now); err != nil {
return -1, err
}
artID, err := as.Datasource.Create(a)
if err != nil {
return 0, err
}
return artID, nil
return as.Datasource.Create(a)
}
//Update updates an article
@ -204,7 +201,7 @@ func (as ArticleService) GetBySlug(s string, u *User, pc PublishedCriteria) (*Ar
a, err := as.Datasource.GetBySlug(s, u, pc)
if err != nil {
if err == sql.ErrNoRows {
if errors.Is(err, sql.ErrNoRows) {
return nil, httperror.NotFound("article", err)
}
return nil, err
@ -227,7 +224,7 @@ func (as ArticleService) GetByID(id int, u *User, pc PublishedCriteria) (*Articl
a, err := as.Datasource.Get(id, u, pc)
if err != nil {
if err == sql.ErrNoRows {
if errors.Is(err, sql.ErrNoRows) {
return nil, httperror.NotFound("article", fmt.Errorf("the article with id %d was not found", id))
}
return nil, err
@ -272,7 +269,8 @@ func (as ArticleService) RSSFeed(p *Pagination, pc PublishedCriteria) (RSS, erro
return RSS{}, err
}
items := []RSSItem{}
var items []RSSItem
for _, a := range articles {
link := fmt.Sprint(as.AppConfig.Domain, "/article/by-id/", a.ID)
item := RSSItem{


+ 4
- 3
models/article_sqlite.go View File

@ -76,10 +76,9 @@ func (rdb SQLiteArticleDatasource) List(u *User, c *Category, p *Pagination, pc
//the PublishedCritera specifies which articles should be considered
func (rdb SQLiteArticleDatasource) Count(u *User, c *Category, pc PublishedCriteria) (int, error) {
var total int
var args []interface{}
var stmt bytes.Buffer
stmt.WriteString("SELECT count(a.id) FROM article a ")
if c != nil {
@ -211,19 +210,21 @@ func selectArticleStmt(db *sql.DB, articleID int, slug string, u *User, pc Publi
stmt.WriteString("AND a.id=? ")
args = append(args, articleID)
}
if u != nil {
if !u.IsAdmin {
stmt.WriteString("AND a.user_id=? ")
args = append(args, u.ID)
}
}
stmt.WriteString("LIMIT 1")
return db.QueryRow(stmt.String(), args...)
}
func selectArticlesStmt(db *sql.DB, u *User, c *Category, p *Pagination, pc PublishedCriteria) (*sql.Rows, error) {
var stmt bytes.Buffer
var args []interface{}
stmt.WriteString("SELECT a.id, a.headline, a.teaser, a.content, a.published, a.published_on, a.slug, a.last_modified, ")


+ 6
- 6
models/category.go View File

@ -8,8 +8,8 @@ import (
"strings"
"time"
"git.hoogi.eu/snafu/go-blog/components/httperror"
"git.hoogi.eu/snafu/go-blog/utils"
"git.hoogi.eu/snafu/go-blog/httperror"
"git.hoogi.eu/snafu/go-blog/slug"
)
type Category struct {
@ -66,7 +66,7 @@ func (cs CategoryService) GetBySlug(s string, fc FilterCriteria) (*Category, err
c, err := cs.Datasource.GetBySlug(s, fc)
if err != nil {
if err == sql.ErrNoRows {
if errors.Is(err, sql.ErrNoRows) {
return nil, httperror.NotFound("category", fmt.Errorf("the category with slug %s was not found", s))
}
return nil, err
@ -79,7 +79,7 @@ func (cs CategoryService) GetByID(id int, fc FilterCriteria) (*Category, error)
c, err := cs.Datasource.Get(id, fc)
if err != nil {
if err == sql.ErrNoRows {
if errors.Is(err, sql.ErrNoRows) {
return nil, httperror.NotFound("category", fmt.Errorf("the category with id %d was not found", id))
}
return nil, err
@ -99,11 +99,11 @@ func (cs CategoryService) List(fc FilterCriteria) ([]Category, error) {
//Create creates a category
func (cs CategoryService) Create(c *Category) (int, error) {
for i := 0; i < 10; i++ {
c.Slug = utils.CreateURLSafeSlug(c.Name, i)
c.Slug = slug.CreateURLSafeSlug(c.Name, i)
_, err := cs.Datasource.GetBySlug(c.Slug, AllCategories)
if err != nil {
if err == sql.ErrNoRows {
if errors.Is(err, sql.ErrNoRows) {
break
}
return -1, err


+ 3
- 3
models/category_sqlite.go View File

@ -33,9 +33,8 @@ func (rdb SQLiteCategoryDatasource) Create(c *Category) (int, error) {
}
func (rdb SQLiteCategoryDatasource) List(fc FilterCriteria) ([]Category, error) {
var stmt bytes.Buffer
var args []interface{}
var stmt bytes.Buffer
stmt.WriteString("SELECT DISTINCT c.id, c.name, c.slug, c.last_modified, ")
stmt.WriteString("u.id, u.display_name, u.username, u.email, u.is_admin ")
@ -63,7 +62,7 @@ func (rdb SQLiteCategoryDatasource) List(fc FilterCriteria) ([]Category, error)
defer rows.Close()
cs := []Category{}
var cs []Category
for rows.Next() {
var c Category
@ -139,6 +138,7 @@ func (rdb SQLiteCategoryDatasource) GetBySlug(slug string, fc FilterCriteria) (*
stmt.WriteString("FROM category as c ")
stmt.WriteString("INNER JOIN user as u ")
stmt.WriteString("ON u.id = c.user_id ")
if fc == CategoriesWithPublishedArticles {
stmt.WriteString("INNER JOIN article as a ")
stmt.WriteString("ON c.id = a.category_id ")


+ 37
- 4
models/file.go View File

@ -11,11 +11,11 @@ import (
"path/filepath"
"strings"
"time"
"unicode"
"git.hoogi.eu/snafu/go-blog/components/httperror"
"git.hoogi.eu/snafu/go-blog/components/logger"
"git.hoogi.eu/snafu/go-blog/httperror"
"git.hoogi.eu/snafu/go-blog/logger"
"git.hoogi.eu/snafu/go-blog/settings"
"git.hoogi.eu/snafu/go-blog/utils"
)
//File represents a file
@ -67,7 +67,7 @@ func (f *File) validate() error {
func (f File) randomFilename() string {
var buf bytes.Buffer
sanFilename := utils.SanitizeFilename(f.FileInfo.Name)
sanFilename := sanitizeFilename(f.FileInfo.Name)
if len(sanFilename) == 0 {
sanFilename = "unnamed"
}
@ -238,3 +238,36 @@ func (fs FileService) Upload(f *File) (int, error) {
return i, nil
}
var filenameSubs = map[rune]string{
'/': "",
'\\': "",
':': "",
'*': "",
'?': "",
'"': "",
'<': "",
'>': "",
'|': "",
' ': "",
}
func isDot(r rune) bool {
return '.' == r
}
//SanitizeFilename sanitizes a filename for safe use when serving file
func sanitizeFilename(s string) string {
s = strings.ToValidUTF8(s, "")
s = strings.TrimFunc(s, unicode.IsSpace)
s = strings.Map(func(r rune) rune {
if _, ok := filenameSubs[r]; ok {
return -1
}
return r
}, s)
s = strings.TrimLeftFunc(s, isDot)
return s
}

+ 1
- 4
models/file_sqlite.go View File

@ -15,7 +15,6 @@ type SQLiteFileDatasource struct {
//only file specific to this user is returned
func (rdb SQLiteFileDatasource) GetByUniqueName(uniqueName string, u *User) (*File, error) {
var stmt bytes.Buffer
var args []interface{}
stmt.WriteString("SELECT f.id, f.filename, f.unique_name, f.content_type, f.inline, f.size, f.last_modified, f.user_id, ")
@ -51,7 +50,6 @@ func (rdb SQLiteFileDatasource) GetByUniqueName(uniqueName string, u *User) (*Fi
//only file specific to this user is returned
func (rdb SQLiteFileDatasource) Get(fileID int, u *User) (*File, error) {
var stmt bytes.Buffer
var args []interface{}
stmt.WriteString("SELECT f.id, f.filename, f.unique_name, f.content_type, f.inline, f.size, f.last_modified, f.user_id, ")
@ -144,8 +142,7 @@ func (rdb SQLiteFileDatasource) List(u *User, p *Pagination) ([]File, error) {
defer rows.Close()
files := []File{}
var files []File
var f File
var us User


+ 2
- 2
models/html.go View File

@ -14,7 +14,7 @@ import (
)
// Defines the extensions that are used
var exts = bf.NoIntraEmphasis | bf.Tables | bf.FencedCode | bf.Autolink |
var ext = bf.NoIntraEmphasis | bf.Tables | bf.FencedCode | bf.Autolink |
bf.Strikethrough | bf.SpaceHeadings | bf.BackslashLineBreak |
bf.DefinitionLists | bf.Footnotes | bf.HardLineBreak
@ -33,7 +33,7 @@ func init() {
//MarkdownToHTML sanitizes and parses markdown to HTML
func MarkdownToHTML(md []byte) []byte {
md = bytes.Replace(md, []byte("\r\n"), []byte("\n"), -1)
unsafe := bf.Run((md), bf.WithExtensions(exts))
unsafe := bf.Run((md), bf.WithExtensions(ext))
return sanitize(unsafe)
}


+ 3
- 4
models/mail.go View File

@ -3,9 +3,8 @@ package models
import (
"fmt"
"git.hoogi.eu/snafu/go-blog/components/mail"
"git.hoogi.eu/snafu/go-blog/mail"
"git.hoogi.eu/snafu/go-blog/settings"
"git.hoogi.eu/snafu/go-blog/utils"
)
type Mailer struct {
@ -14,7 +13,7 @@ type Mailer struct {
}
func (m Mailer) SendActivationLink(ui *UserInvite) {
activation := utils.AppendString(m.AppConfig.Domain, "/admin/activate-account/", ui.Hash)
activation := m.AppConfig.Domain + "/admin/activate-account/" + ui.Hash
mail := mail.Mail{
To: ui.Email,
@ -36,7 +35,7 @@ func (m Mailer) SendPasswordChangeConfirmation(u *User) {
}
func (m Mailer) SendPasswordResetLink(u *User, t *Token) {
resetLink := utils.AppendString(m.AppConfig.Domain, "/admin/reset-password/", t.Hash)
resetLink := m.AppConfig.Domain + "/admin/reset-password/" + t.Hash
mail := mail.Mail{
To: u.Email,


+ 13
- 15
models/pagination.go View File

@ -5,12 +5,10 @@
package models
import (
"bytes"
"fmt"
"html/template"
"math"
"git.hoogi.eu/snafu/go-blog/utils"
"strings"
)
//Pagination type is used to provide a page selector
@ -29,9 +27,9 @@ func (p Pagination) Offset() int {
//url returns the absolute url
func (p Pagination) url() string {
if p.RelURL[0] == '/' {
return utils.AppendString(p.RelURL)
return p.RelURL
}
return utils.AppendString("/", p.RelURL)
return "/" + p.RelURL
}
//pages returns the amount of pages
@ -75,32 +73,32 @@ func (p Pagination) previousPage() int {
//PaginationBar returns the HTML for the pagination bar which can be embedded
func (p Pagination) PaginationBar() template.HTML {
var buffer bytes.Buffer
var sb strings.Builder
if p.pages() > 1 {
buffer.WriteString(`<div id="pagination">`)
sb.WriteString(`<div id="pagination">`)
if !p.hasPrevious() {
buffer.WriteString(`<a class="button button-inactive" href="#">&laquo; Backward</a>`)
sb.WriteString(`<a class="button button-inactive" href="#">&laquo; Backward</a>`)
} else {
buffer.WriteString(fmt.Sprintf(`<a class="button button-active" href="%s/%d">&laquo; Backward</a>`, p.url(), p.previousPage()))
sb.WriteString(fmt.Sprintf(`<a class="button button-active" href="%s/%d">&laquo; Backward</a>`, p.url(), p.previousPage()))
}
for i := 1; i <= p.pages(); i++ {
if p.CurrentPage == i {
buffer.WriteString(fmt.Sprintf(`<a class="button button-inactive" href="#">%d</a></li>`, i))
sb.WriteString(fmt.Sprintf(`<a class="button button-inactive" href="#">%d</a></li>`, i))
} else {
buffer.WriteString(fmt.Sprintf(`<a class="button button-active" href="%s/%d">%d</a></li>`, p.url(), i, i))
sb.WriteString(fmt.Sprintf(`<a class="button button-active" href="%s/%d">%d</a></li>`, p.url(), i, i))
}
}
if !p.hasNext() {
buffer.WriteString(`<a class="button button-inactive" href="#">Forward &raquo;</a>`)
sb.WriteString(`<a class="button button-inactive" href="#">Forward &raquo;</a>`)
} else {
buffer.WriteString(fmt.Sprintf(`<a class="button button-active" href="%s/%d">Forward &raquo;</a>`, p.url(), p.nextPage()))
sb.WriteString(fmt.Sprintf(`<a class="button button-active" href="%s/%d">Forward &raquo;</a>`, p.url(), p.nextPage()))
}
buffer.WriteString(`</div>`)
sb.WriteString(`</div>`)
}
return template.HTML(buffer.String())
return template.HTML(sb.String())
}

+ 5
- 4
models/site.go View File

@ -7,8 +7,8 @@ import (
"net/url"
"time"
"git.hoogi.eu/snafu/go-blog/components/httperror"
"git.hoogi.eu/snafu/go-blog/utils"
"git.hoogi.eu/snafu/go-blog/httperror"
"git.hoogi.eu/snafu/go-blog/slug"
)
//SiteDatasourceService defines an interface for CRUD operations on sites
@ -54,14 +54,14 @@ func (s Site) LinkEscape() string {
if s.isExternal() {
return s.Link
}
return utils.AppendString("/site/", url.PathEscape(s.Link))
return "/site/" + url.PathEscape(s.Link)
}
func (s Site) safeLink() string {
if s.isExternal() {
return s.Link
}
return utils.CreateURLSafeSlug(s.Link, -1)
return slug.CreateURLSafeSlug(s.Link, -1)
}
func (s Site) isExternal() bool {
@ -174,6 +174,7 @@ func (ss SiteService) Update(s *Site) error {
}
changeLink := false
if oldSite.Link != s.Link {
changeLink = true
s.Link = s.safeLink()


+ 4
- 8
models/site_sqlite.go View File

@ -5,7 +5,7 @@ import (
"database/sql"
"time"
"git.hoogi.eu/snafu/go-blog/components/logger"
"git.hoogi.eu/snafu/go-blog/logger"
)
//SQLiteSiteDatasource providing an implementation of SiteDatasourceService for sqlite
@ -16,7 +16,6 @@ type SQLiteSiteDatasource struct {
//List returns a array of sites
func (rdb SQLiteSiteDatasource) List(pc PublishedCriteria, p *Pagination) ([]Site, error) {
var stmt bytes.Buffer
var args []interface{}
stmt.WriteString("SELECT s.id, s.title, s.link, s.section, s.content, s.published, s.published_on, s.last_modified, s.order_no, u.id, u.display_name, u.email, u.username ")
@ -40,14 +39,14 @@ func (rdb SQLiteSiteDatasource) List(pc PublishedCriteria, p *Pagination) ([]Sit
}
rows, err := rdb.SQLConn.Query(stmt.String(), args...)
if err != nil {
return nil, err
}
defer rows.Close()
sites := []Site{}
var sites []Site
var s Site
var u User
@ -129,6 +128,7 @@ func (rdb SQLiteSiteDatasource) GetByLink(link string, pc PublishedCriteria) (*S
//Publish publishes or unpublishes a site
func (rdb SQLiteSiteDatasource) Publish(s *Site) error {
publishOn := NullTime{Valid: false}
if !s.Published {
publishOn = NullTime{Time: time.Now(), Valid: true}
}
@ -172,10 +172,6 @@ func (rdb SQLiteSiteDatasource) Order(id int, d Direction) error {
}
}()
if err != nil {
return err
}
if d == Up {
if _, err = tx.Exec("UPDATE site "+
"SET order_no=(SELECT order_no AS order_no FROM site WHERE id=?) "+


+ 1
- 1
models/sql.go View File

@ -29,7 +29,7 @@ const (
All
)
//NullTime reprensents a time which may not valid if time is null
//NullTime represents a time which may not valid if time is null
type NullTime struct {
Time time.Time
Valid bool


+ 9
- 7
models/token.go View File

@ -7,9 +7,9 @@ import (
"net/http"
"time"
"git.hoogi.eu/snafu/go-blog/components/httperror"
"git.hoogi.eu/snafu/go-blog/components/logger"
"git.hoogi.eu/snafu/go-blog/utils"
"git.hoogi.eu/snafu/go-blog/crypt"
"git.hoogi.eu/snafu/go-blog/httperror"
"git.hoogi.eu/snafu/go-blog/logger"
)
//TokenDatasourceService defines an interface for CRUD operations for tokens
@ -68,11 +68,13 @@ type TokenService struct {
//Create creates a new token
func (ts TokenService) Create(t *Token) error {
t.Hash = utils.RandomHash(32)
t.Hash = crypt.RandomHash(32)
_, err := ts.Datasource.Create(t)
if _, err := ts.Datasource.Create(t); err != nil {
return err
}
return err
return nil
}
//Get token for a defined token type expires after a defined time
@ -106,7 +108,7 @@ func (ts TokenService) RateLimit(userID int, tt TokenType) error {
now := time.Now()
rate := []Token{}
var rate []Token
for _, t := range tokens {
if now.Sub(t.RequestedAt) < time.Minute*15 {
rate = append(rate, t)


+ 23
- 19
models/user.go View File

@ -12,10 +12,10 @@ import (
"strings"
"time"
"git.hoogi.eu/snafu/go-blog/components/httperror"
"git.hoogi.eu/snafu/go-blog/components/logger"
"git.hoogi.eu/snafu/go-blog/crypt"
"git.hoogi.eu/snafu/go-blog/httperror"
"git.hoogi.eu/snafu/go-blog/logger"
"git.hoogi.eu/snafu/go-blog/settings"
"git.hoogi.eu/snafu/go-blog/utils"
"golang.org/x/crypto/bcrypt"
)
@ -133,8 +133,9 @@ func (u *User) validate(us UserService, minPasswordLength int, v Validations) er
func (us UserService) duplicateMail(mail string) error {
user, err := us.Datasource.GetByMail(mail)
if err != nil {
if err != sql.ErrNoRows {
if !errors.Is(err, sql.ErrNoRows) {
return err
}
}
@ -149,7 +150,7 @@ func (us UserService) duplicateMail(mail string) error {
func (us UserService) duplicateUsername(username string) error {
user, err := us.Datasource.GetByUsername(username)
if err != nil {
if err != sql.ErrNoRows {
if !errors.Is(err, sql.ErrNoRows) {
return err
}
}
@ -175,8 +176,9 @@ func (us UserService) List(p *Pagination) ([]User, error) {
//GetByID gets the user based on the given id; will not contain the user password
func (us UserService) GetByID(userID int) (*User, error) {
u, err := us.Datasource.Get(userID)
if err != nil {
if err == sql.ErrNoRows {
if errors.Is(err, sql.ErrNoRows) {
return nil, httperror.NotFound("user", fmt.Errorf("the user with id %d was not found", userID))
}
return nil, err
@ -189,7 +191,7 @@ func (us UserService) GetByID(userID int) (*User, error) {
func (us UserService) GetByUsername(username string) (*User, error) {
u, err := us.Datasource.GetByUsername(username)
if err != nil {
if err == sql.ErrNoRows {
if errors.Is(err, sql.ErrNoRows) {
return nil, httperror.NotFound("user", err)
}
return nil, err
@ -203,7 +205,7 @@ func (us UserService) GetByMail(mail string) (*User, error) {
u, err := us.Datasource.GetByMail(mail)
if err != nil {
if err == sql.ErrNoRows {
if errors.Is(err, sql.ErrNoRows) {
return nil, httperror.NotFound("user", err)
}
@ -225,9 +227,9 @@ func (us UserService) Create(u *User) (int, error) {
return -1, err
}
salt := utils.GenerateSalt()
saltedPassword := utils.AppendBytes(u.PlainPassword, salt)
password, err := utils.CryptPassword([]byte(saltedPassword), bcryptRounds)
salt := crypt.GenerateSalt()
saltedPassword := append(u.PlainPassword[:], salt[:]...)
password, err := crypt.CryptPassword([]byte(saltedPassword), bcryptRounds)
if err != nil {
return -1, err
@ -270,7 +272,6 @@ func (us UserService) Update(u *User, changePassword bool) error {
}
if us.UserInterceptor != nil {
if err := us.UserInterceptor.PreUpdate(oldUser, u); err != nil {
return httperror.InternalServerError(fmt.Errorf("error while executing user interceptor 'PreUpdate' error %v", err))
}
@ -309,9 +310,9 @@ func (us UserService) Update(u *User, changePassword bool) error {
}
if changePassword {
salt := utils.GenerateSalt()
saltedPassword := utils.AppendBytes(u.PlainPassword, salt)
password, err := utils.CryptPassword([]byte(saltedPassword), bcryptRounds)
salt := crypt.GenerateSalt()
saltedPassword := append(u.PlainPassword[:], salt[:]...)
password, err := crypt.CryptPassword([]byte(saltedPassword), bcryptRounds)
if err != nil {
return err
@ -321,7 +322,9 @@ func (us UserService) Update(u *User, changePassword bool) error {
u.Salt = salt
}
err = us.Datasource.Update(u, changePassword)
if err = us.Datasource.Update(u, changePassword); err != nil {
return err
}
u.Password = nil
@ -330,9 +333,10 @@ func (us UserService) Update(u *User, changePassword bool) error {
logger.Log.Errorf("error while executing PostUpdate user interceptor method %v", err)
}
}
u.PlainPassword = nil
return err
return nil
}
// Authenticate authenticates the user by the given login method (email or username)
@ -353,7 +357,7 @@ func (us UserService) Authenticate(u *User, loginMethod settings.LoginMethod) (*
}
if err != nil {
if err == sql.ErrNoRows {
if errors.Is(err, sql.ErrNoRows) {
//Do some extra work
bcrypt.CompareHashAndPassword([]byte("$2a$12$bQlRnXTNZMp6kCyoAlnf3uZW5vtmSj9CHP7pYplRUVK2n0C5xBHBa"), password)
return nil, httperror.New(http.StatusUnauthorized, "Your username or password is invalid.", err)
@ -429,5 +433,5 @@ func (us UserService) OneAdmin() (bool, error) {
}
func (u User) comparePassword() error {
return bcrypt.CompareHashAndPassword(u.Password, utils.AppendBytes(u.PlainPassword, u.Salt))
return bcrypt.CompareHashAndPassword(u.Password, append(u.PlainPassword[:], u.Salt[:]...))
}

+ 5
- 5
models/user_invite.go View File

@ -3,11 +3,11 @@ package models
import (
"time"
"git.hoogi.eu/snafu/go-blog/components/mail"
"git.hoogi.eu/snafu/go-blog/utils"
"git.hoogi.eu/snafu/go-blog/crypt"
"git.hoogi.eu/snafu/go-blog/mail"
)
//User represents a user
//UserInvite represents a new invited user
type UserInvite struct {
ID int
Hash string
@ -64,7 +64,7 @@ func (uis UserInviteService) List() ([]UserInvite, error) {
}
func (uis UserInviteService) Update(ui *UserInvite) error {
ui.Hash = utils.RandomHash(32)
ui.Hash = crypt.RandomHash(32)
if err := ui.validate(uis); err != nil {
return err
@ -74,7 +74,7 @@ func (uis UserInviteService) Update(ui *UserInvite) error {
}
func (uis UserInviteService) Create(ui *UserInvite) (int, error) {
ui.Hash = utils.RandomHash(32)
ui.Hash = crypt.RandomHash(32)
if err := ui.validate(uis); err != nil {
return -1, err


+ 5
- 10
models/user_invite_sqlite.go View File

@ -14,6 +14,9 @@ type SQLiteUserInviteDatasource struct {
func (rdb SQLiteUserInviteDatasource) List() ([]UserInvite, error) {
var stmt bytes.Buffer
var args []interface{}
var invites []UserInvite
var ui UserInvite
var u User
stmt.WriteString("SELECT ui.id, ui.username, ui.email, ui.display_name, ui.created_at, ui.is_admin, ")
stmt.WriteString("u.id, u.username, u.email, u.display_name ")
@ -30,11 +33,6 @@ func (rdb SQLiteUserInviteDatasource) List() ([]UserInvite, error) {
defer rows.Close()
invites := []UserInvite{}
var ui UserInvite
var u User
for rows.Next() {
if err = rows.Scan(&ui.ID, &ui.Username, &ui.Email, &ui.DisplayName, &ui.CreatedAt, &ui.IsAdmin, &u.ID, &u.Username, &u.Email, &u.DisplayName); err != nil {
return nil, err
@ -111,6 +109,7 @@ func (rdb SQLiteUserInviteDatasource) Create(ui *UserInvite) (int, error) {
if err != nil {
return -1, err
}
return int(i), nil
}
@ -127,11 +126,7 @@ func (rdb SQLiteUserInviteDatasource) Count() (int, error) {
//Removes an user invitation
func (rdb SQLiteUserInviteDatasource) Remove(inviteID int) error {