Merge branch 'develop'
This commit is contained in:
commit
385c1207d3
|
@ -32,9 +32,6 @@ releases
|
||||||
|
|
||||||
.idea
|
.idea
|
||||||
.csrftoken
|
.csrftoken
|
||||||
/clt/createUser
|
|
||||||
/clt/initDatabase
|
|
||||||
/go-blog
|
|
||||||
releases
|
releases
|
||||||
|
|
||||||
db/
|
db/
|
||||||
|
|
8
Makefile
8
Makefile
|
@ -1,8 +1,14 @@
|
||||||
BINARYNAME=go-blog
|
BINARYNAME=go-blog
|
||||||
TMP=tmp
|
TMP=tmp
|
||||||
|
DIST=release
|
||||||
GITHASH=$(shell git rev-parse HEAD)
|
GITHASH=$(shell git rev-parse HEAD)
|
||||||
BUILD_VERSION=$(shell git describe --tags)
|
BUILD_VERSION=$(shell git describe --tags)
|
||||||
|
|
||||||
|
ifndef $(GOPATH)
|
||||||
|
GOPATH=$(shell go env GOPATH)
|
||||||
|
export GOPATH
|
||||||
|
endif
|
||||||
|
|
||||||
RELEASE="releases"
|
RELEASE="releases"
|
||||||
|
|
||||||
LDFLAGS=-ldflags '-X main.BuildVersion=${BUILD_VERSION} -X main.GitHash=${GITHASH}'
|
LDFLAGS=-ldflags '-X main.BuildVersion=${BUILD_VERSION} -X main.GitHash=${GITHASH}'
|
||||||
|
@ -24,7 +30,7 @@ install:
|
||||||
package:
|
package:
|
||||||
-rm -r ${TMP}
|
-rm -r ${TMP}
|
||||||
mkdir -p ${TMP}/clt
|
mkdir -p ${TMP}/clt
|
||||||
-mkdir -p releases
|
-mkdir -p releases/custom
|
||||||
cp ${GOPATH}/bin/go-blog ${TMP}/
|
cp ${GOPATH}/bin/go-blog ${TMP}/
|
||||||
cp ${GOPATH}/bin/create_user ${TMP}/clt
|
cp ${GOPATH}/bin/create_user ${TMP}/clt
|
||||||
cp ${GOPATH}/bin/init_database ${TMP}/clt
|
cp ${GOPATH}/bin/init_database ${TMP}/clt
|
||||||
|
|
|
@ -12,10 +12,9 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/components/database"
|
"git.hoogi.eu/snafu/go-blog/database"
|
||||||
"git.hoogi.eu/snafu/go-blog/components/logger"
|
"git.hoogi.eu/snafu/go-blog/logger"
|
||||||
"git.hoogi.eu/snafu/go-blog/models"
|
"git.hoogi.eu/snafu/go-blog/models"
|
||||||
"git.hoogi.eu/snafu/go-blog/utils"
|
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -38,14 +37,31 @@ func main() {
|
||||||
|
|
||||||
fmt.Printf("create_user version %s\n", BuildVersion)
|
fmt.Printf("create_user version %s\n", BuildVersion)
|
||||||
|
|
||||||
username := flag.String("username", "", "Username for the admin user ")
|
username := flag.String("username", "", "Username for the admin user. (required)")
|
||||||
email := flag.String("email", "", "Email for the created user ")
|
email := flag.String("email", "", "Email for the created user. (required)")
|
||||||
displayName := flag.String("displayname", "", "Display name for the admin user ")
|
displayName := flag.String("displayname", "", "Display name for the admin user. (required)")
|
||||||
isAdmin := flag.Bool("admin", false, "If set a new administrator will be created; otherwise a non-admin is created")
|
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")
|
file := flag.String("sqlite", "", "Location to the sqlite3 database file. (required)")
|
||||||
|
|
||||||
flag.Parse()
|
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() {
|
if flag.Parsed() {
|
||||||
initUser := createUserFlag{
|
initUser := createUserFlag{
|
||||||
username: *username,
|
username: *username,
|
||||||
|
@ -55,16 +71,13 @@ func main() {
|
||||||
sqlite: *file,
|
sqlite: *file,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := initUser.validate(); err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Password: ")
|
fmt.Printf("Password: ")
|
||||||
pw, err := terminal.ReadPassword(int(syscall.Stdin))
|
pw, err := terminal.ReadPassword(int(syscall.Stdin))
|
||||||
|
|
||||||
|
fmt.Println("")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("could not read password %v", err)
|
fmt.Printf("could not read password %v\n", err)
|
||||||
os.Exit(1)
|
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 {
|
func (userFlags createUserFlag) CreateUser() error {
|
||||||
|
|
||||||
var userService models.UserService
|
var userService models.UserService
|
||||||
|
|
||||||
dbConfig := database.SQLiteConfig{
|
dbConfig := database.SQLiteConfig{
|
||||||
|
|
|
@ -12,8 +12,8 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/components/database"
|
"git.hoogi.eu/snafu/go-blog/database"
|
||||||
"git.hoogi.eu/snafu/go-blog/components/logger"
|
"git.hoogi.eu/snafu/go-blog/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -2,46 +2,45 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package utils
|
//package crypt
|
||||||
|
package crypt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
"encoding/base64"
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
var (
|
||||||
//AlphaUpper all upper alphas chars
|
//AlphaUpper all upper alphas chars
|
||||||
AlphaUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
AlphaUpper = RandomSource("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||||
//AlphaLower all lowers alphas chars
|
//AlphaLower all lowers alphas chars
|
||||||
AlphaLower = "abcdefghijklmnopqrstuvwxyz"
|
AlphaLower = RandomSource("abcdefghijklmnopqrstuvwxyz")
|
||||||
//AlphaUpperLower all upper and lowers aplhas chars
|
//AlphaUpperLower all upper and lowers aplhas chars
|
||||||
AlphaUpperLower = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
AlphaUpperLower = RandomSource("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
|
||||||
//AlphaUpperLowerNumeric all upper lowers alphas and numerics
|
//AlphaUpperLowerNumeric all upper lowers alphas and numerics
|
||||||
AlphaUpperLowerNumeric = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz"
|
AlphaUpperLowerNumeric = RandomSource("ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz")
|
||||||
//AlphaUpperLowerNumericSpecial all upper lowers alphas, numerics and special chas
|
//AlphaUpperLowerNumericSpecial all upper lowers alphas, numerics and special chars
|
||||||
AlphaUpperLowerNumericSpecial = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456890" +
|
AlphaUpperLowerNumericSpecial = RandomSource("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456890" +
|
||||||
"!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
|
"!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~")
|
||||||
)
|
)
|
||||||
|
|
||||||
//RandomSource express which chars should be considered
|
//RandomSource string containing which characters should be considered when generating random sequences
|
||||||
type RandomSource struct {
|
type RandomSource string
|
||||||
CharsToGen string
|
|
||||||
}
|
|
||||||
|
|
||||||
//RandomSequence returns random character with given length;
|
//RandomSequence returns random character with given length;
|
||||||
//random source express which chars should be considered
|
|
||||||
func (r RandomSource) RandomSequence(length int) []byte {
|
func (r RandomSource) RandomSequence(length int) []byte {
|
||||||
result := make([]byte, length)
|
result := make([]byte, length)
|
||||||
for i := 0; i < length; i++ {
|
for i := 0; i < length; i++ {
|
||||||
char, _ := rand.Int(rand.Reader, big.NewInt(int64(len(r.CharsToGen))))
|
char, _ := rand.Int(rand.Reader, big.NewInt(int64(len(r))))
|
||||||
result[i] = r.CharsToGen[int(char.Int64())]
|
result[i] = r[int(char.Int64())]
|
||||||
}
|
}
|
||||||
|
fmt.Println(result)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +53,7 @@ func RandomSecureKey(length int) []byte {
|
||||||
return k
|
return k
|
||||||
}
|
}
|
||||||
|
|
||||||
//CryptPassword bcrypts a password at given costs
|
//CryptPassword hashes a password with bcrypt and a given cost
|
||||||
func CryptPassword(password []byte, cost int) ([]byte, error) {
|
func CryptPassword(password []byte, cost int) ([]byte, error) {
|
||||||
s, err := bcrypt.GenerateFromPassword(password, cost)
|
s, err := bcrypt.GenerateFromPassword(password, cost)
|
||||||
|
|
||||||
|
@ -67,21 +66,7 @@ func CryptPassword(password []byte, cost int) ([]byte, error) {
|
||||||
|
|
||||||
//GenerateSalt generates a random salt with alphanumerics and some special characters
|
//GenerateSalt generates a random salt with alphanumerics and some special characters
|
||||||
func GenerateSalt() []byte {
|
func GenerateSalt() []byte {
|
||||||
r := RandomSource{
|
return AlphaUpperLowerNumericSpecial.RandomSequence(32)
|
||||||
CharsToGen: AlphaUpperLowerNumericSpecial,
|
|
||||||
}
|
|
||||||
return r.RandomSequence(32)
|
|
||||||
}
|
|
||||||
|
|
||||||
//EncodeBase64 encodes a string to base64
|
|
||||||
func EncodeBase64(input string) string {
|
|
||||||
return base64.StdEncoding.EncodeToString([]byte(input))
|
|
||||||
}
|
|
||||||
|
|
||||||
//DecodeBase64 descodes a string to base64
|
|
||||||
func DecodeBase64(b64 string) (string, error) {
|
|
||||||
out, err := base64.StdEncoding.DecodeString(b64)
|
|
||||||
return string(out), err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func RandomHash(length int) string {
|
func RandomHash(length int) string {
|
|
@ -17,13 +17,7 @@ type SQLiteConfig struct {
|
||||||
|
|
||||||
//Open receives handle for sqlite database, returns an error if connection failed
|
//Open receives handle for sqlite database, returns an error if connection failed
|
||||||
func (d SQLiteConfig) Open() (*sql.DB, error) {
|
func (d SQLiteConfig) Open() (*sql.DB, error) {
|
||||||
db, err := sql.Open("sqlite3", d.File)
|
return sql.Open("sqlite3", d.File)
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return db, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//InitTables creates the tables
|
//InitTables creates the tables
|
||||||
|
@ -148,5 +142,6 @@ func InitTables(db *sql.DB) error {
|
||||||
");"); err != nil {
|
");"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
15
go.mod
15
go.mod
|
@ -4,17 +4,16 @@ go 1.15
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.hoogi.eu/snafu/cfg v1.0.6
|
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/csrf v1.7.0
|
||||||
github.com/gorilla/handlers v1.5.1
|
github.com/gorilla/handlers v1.5.1
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/justinas/alice v1.2.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/microcosm-cc/bluemonday v1.0.4
|
||||||
github.com/russross/blackfriday/v2 v2.0.1
|
github.com/russross/blackfriday/v2 v2.1.0
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
github.com/sirupsen/logrus v1.7.0
|
||||||
github.com/sirupsen/logrus v1.6.0
|
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c
|
||||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
|
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb // indirect
|
||||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73 // indirect
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88 // indirect
|
||||||
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 // indirect
|
|
||||||
)
|
)
|
||||||
|
|
39
go.sum
39
go.sum
|
@ -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/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 h1:MclTbSqD/9JodRUqFc4OwyJGk5AtonqB6BaG5RRJHf8=
|
||||||
git.hoogi.eu/snafu/session v1.1.2/go.mod h1:kgRDrnHcKc9H18G9533BXy6qO+81eBf6e9gkUzBMDuA=
|
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 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
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=
|
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/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 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo=
|
||||||
github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA=
|
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/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ=
|
||||||
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.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
|
||||||
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/microcosm-cc/bluemonday v1.0.4 h1:p0L+CTpo/PLFdkoPcJemLXG+fpMD7pYOoDEq1axMbGg=
|
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/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 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
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.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
|
|
||||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
|
||||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
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-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-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-20201203163018-be400aefbc4c h1:9HhBz5L/UjnK9XLtiZhYAdue5BVKep3PMmS2LuPDt8k=
|
||||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
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-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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA=
|
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U=
|
||||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
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-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-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-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88 h1:KmZPnMocC93w341XZp26yTJg8Za7lhb2KhkYmixoeso=
|
||||||
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 h1:W0lCpv29Hv0UaM1LXb9QlBHLNP8UFfcKjblhVCWftOM=
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/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.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=
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package controllers
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -8,8 +8,8 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/components/httperror"
|
"git.hoogi.eu/snafu/go-blog/httperror"
|
||||||
"git.hoogi.eu/snafu/go-blog/components/logger"
|
"git.hoogi.eu/snafu/go-blog/logger"
|
||||||
"git.hoogi.eu/snafu/go-blog/middleware"
|
"git.hoogi.eu/snafu/go-blog/middleware"
|
||||||
"git.hoogi.eu/snafu/go-blog/models"
|
"git.hoogi.eu/snafu/go-blog/models"
|
||||||
)
|
)
|
||||||
|
@ -57,7 +57,7 @@ func AdminProfilePostHandler(ctx *middleware.AppContext, w http.ResponseWriter,
|
||||||
|
|
||||||
if len(u.PlainPassword) > 0 {
|
if len(u.PlainPassword) > 0 {
|
||||||
changePassword = true
|
changePassword = true
|
||||||
// Password change
|
|
||||||
u.PlainPassword = []byte(r.FormValue("password"))
|
u.PlainPassword = []byte(r.FormValue("password"))
|
||||||
|
|
||||||
if !bytes.Equal(u.PlainPassword, []byte(r.FormValue("retyped_password"))) {
|
if !bytes.Equal(u.PlainPassword, []byte(r.FormValue("retyped_password"))) {
|
||||||
|
@ -81,11 +81,11 @@ func AdminProfilePostHandler(ctx *middleware.AppContext, w http.ResponseWriter,
|
||||||
|
|
||||||
session.SetValue("userid", u.ID)
|
session.SetValue("userid", u.ID)
|
||||||
|
|
||||||
sids := ctx.SessionService.SessionProvider.SessionIDsFromValues("userid", u.ID)
|
sessions := ctx.SessionService.SessionProvider.FindSessionsByValue("userid", u.ID)
|
||||||
|
|
||||||
for _, sid := range sids {
|
for _, sid := range sessions {
|
||||||
if sid != session.SessionID() {
|
if sid.SessionID() != session.SessionID() {
|
||||||
ctx.SessionService.SessionProvider.Remove(sid)
|
ctx.SessionService.SessionProvider.Remove(sid.SessionID())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -230,7 +230,7 @@ func ResetPasswordHandler(ctx *middleware.AppContext, w http.ResponseWriter, r *
|
||||||
//ResetPasswordPostHandler handles the resetting of the password
|
//ResetPasswordPostHandler handles the resetting of the password
|
||||||
func ResetPasswordPostHandler(ctx *middleware.AppContext, w http.ResponseWriter, r *http.Request) *middleware.Template {
|
func ResetPasswordPostHandler(ctx *middleware.AppContext, w http.ResponseWriter, r *http.Request) *middleware.Template {
|
||||||
password := r.FormValue("password")
|
password := r.FormValue("password")
|
||||||
repassword := r.FormValue("password_repeat")
|
password2 := r.FormValue("password_repeat")
|
||||||
hash := getVar(r, "hash")
|
hash := getVar(r, "hash")
|
||||||
|
|
||||||
t, err := ctx.TokenService.Get(hash, models.PasswordReset, time.Duration(1)*time.Hour)
|
t, err := ctx.TokenService.Get(hash, models.PasswordReset, time.Duration(1)*time.Hour)
|
||||||
|
@ -251,7 +251,7 @@ func ResetPasswordPostHandler(ctx *middleware.AppContext, w http.ResponseWriter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if password != repassword {
|
if password != password2 {
|
||||||
return &middleware.Template{
|
return &middleware.Template{
|
||||||
Name: tplAdminResetPassword,
|
Name: tplAdminResetPassword,
|
||||||
Err: httperror.New(http.StatusUnprocessableEntity, "The passwords entered do not match.", errors.New("the password entered did not match")),
|
Err: httperror.New(http.StatusUnprocessableEntity, "The passwords entered do not match.", errors.New("the password entered did not match")),
|
||||||
|
@ -297,7 +297,7 @@ func ForgotPasswordPostHandler(ctx *middleware.AppContext, w http.ResponseWriter
|
||||||
u, err := ctx.UserService.GetByMail(email)
|
u, err := ctx.UserService.GetByMail(email)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if httperror.Equals(err, sql.ErrNoRows) {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
logger.Log.Error(err)
|
logger.Log.Error(err)
|
||||||
return &middleware.Template{
|
return &middleware.Template{
|
||||||
RedirectPath: "admin",
|
RedirectPath: "admin",
|
|
@ -1,11 +1,11 @@
|
||||||
package controllers_test
|
package handler_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/controllers"
|
"git.hoogi.eu/snafu/go-blog/handler"
|
||||||
"git.hoogi.eu/snafu/go-blog/models"
|
"git.hoogi.eu/snafu/go-blog/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ func doAdminProfileRequest(user reqUser, u *models.User, currentPassword string)
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
re := r.buildRequest()
|
re := r.buildRequest()
|
||||||
tpl := controllers.AdminProfilePostHandler(ctx, rw, re)
|
tpl := handler.AdminProfilePostHandler(ctx, rw, re)
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return tpl.Err
|
return tpl.Err
|
||||||
|
@ -122,7 +122,7 @@ func doActivateAccountRequest(user reqUser, password, passwordRepeat, hash strin
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.ActivateAccountPostHandler(ctx, rw, r.buildRequest())
|
tpl := handler.ActivateAccountPostHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return tpl.Err
|
return tpl.Err
|
||||||
|
@ -150,7 +150,7 @@ func doResetPasswordRequest(user reqUser, password, passwordRepeat, hash string)
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminSiteEditPostHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminSiteEditPostHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return tpl.Err
|
return tpl.Err
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package controllers
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
@ -10,10 +10,9 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/components/httperror"
|
"git.hoogi.eu/snafu/go-blog/httperror"
|
||||||
"git.hoogi.eu/snafu/go-blog/middleware"
|
"git.hoogi.eu/snafu/go-blog/middleware"
|
||||||
"git.hoogi.eu/snafu/go-blog/models"
|
"git.hoogi.eu/snafu/go-blog/models"
|
||||||
"git.hoogi.eu/snafu/go-blog/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//GetArticleHandler returns a specific article
|
//GetArticleHandler returns a specific article
|
||||||
|
@ -21,9 +20,11 @@ import (
|
||||||
func GetArticleHandler(ctx *middleware.AppContext, w http.ResponseWriter, r *http.Request) *middleware.Template {
|
func GetArticleHandler(ctx *middleware.AppContext, w http.ResponseWriter, r *http.Request) *middleware.Template {
|
||||||
year := getVar(r, "year")
|
year := getVar(r, "year")
|
||||||
month := getVar(r, "month")
|
month := getVar(r, "month")
|
||||||
slug := getVar(r, "slug")
|
headline := getVar(r, "slug")
|
||||||
|
|
||||||
a, err := ctx.ArticleService.GetBySlug(utils.AppendString(year, "/", month, "/", slug), nil, models.OnlyPublished)
|
slug := year + "/" + month + "/" + headline
|
||||||
|
|
||||||
|
a, err := ctx.ArticleService.GetBySlug(slug, nil, models.OnlyPublished)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &middleware.Template{
|
return &middleware.Template{
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package controllers_test
|
package handler_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -13,7 +13,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/controllers"
|
"git.hoogi.eu/snafu/go-blog/handler"
|
||||||
"git.hoogi.eu/snafu/go-blog/models"
|
"git.hoogi.eu/snafu/go-blog/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -143,7 +143,7 @@ func doGetArticleBySlugRequest(user reqUser, article *models.Article) (*models.A
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.GetArticleHandler(ctx, rw, r.buildRequest())
|
tpl := handler.GetArticleHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return nil, tpl.Err
|
return nil, tpl.Err
|
||||||
|
@ -166,7 +166,7 @@ func doGetArticleByIDRequest(user reqUser, articleID int) (*models.Article, erro
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.GetArticleByIDHandler(ctx, rw, r.buildRequest())
|
tpl := handler.GetArticleByIDHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return nil, tpl.Err
|
return nil, tpl.Err
|
||||||
|
@ -196,7 +196,7 @@ func doAdminEditArticleRequest(user reqUser, articleID int, article *models.Arti
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
|
|
||||||
tpl := controllers.AdminArticleEditPostHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminArticleEditPostHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return tpl.Err
|
return tpl.Err
|
||||||
|
@ -222,7 +222,7 @@ func doAdminCreateArticleRequest(user reqUser, article *models.Article) (int, er
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminArticleNewPostHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminArticleNewPostHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return 0, tpl.Err
|
return 0, tpl.Err
|
||||||
|
@ -249,7 +249,7 @@ func doAdminGetArticleByIDRequest(user reqUser, articleID int) (*models.Article,
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminPreviewArticleByIDHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminPreviewArticleByIDHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return nil, tpl.Err
|
return nil, tpl.Err
|
||||||
|
@ -266,7 +266,7 @@ func doAdminListArticleRequest(user reqUser) ([]models.Article, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminListArticlesHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminListArticlesHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return nil, tpl.Err
|
return nil, tpl.Err
|
||||||
|
@ -289,7 +289,7 @@ func doAdminPublishArticleRequest(user reqUser, articleID int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminArticlePublishPostHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminArticlePublishPostHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return tpl.Err
|
return tpl.Err
|
||||||
|
@ -312,7 +312,7 @@ func doAdminRemoveArticleRequest(user reqUser, articleID int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminArticleDeletePostHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminArticleDeletePostHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return tpl.Err
|
return tpl.Err
|
|
@ -2,13 +2,13 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package controllers
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/components/httperror"
|
"git.hoogi.eu/snafu/go-blog/httperror"
|
||||||
"git.hoogi.eu/snafu/go-blog/middleware"
|
"git.hoogi.eu/snafu/go-blog/middleware"
|
||||||
"git.hoogi.eu/snafu/go-blog/models"
|
"git.hoogi.eu/snafu/go-blog/models"
|
||||||
)
|
)
|
|
@ -1,4 +1,4 @@
|
||||||
package controllers_test
|
package handler_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
@ -6,7 +6,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/controllers"
|
"git.hoogi.eu/snafu/go-blog/handler"
|
||||||
"git.hoogi.eu/snafu/go-blog/models"
|
"git.hoogi.eu/snafu/go-blog/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ func doAdminGetCategoryRequest(user reqUser, categoryID int) (*models.Category,
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminGetCategoryHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminGetCategoryHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return nil, tpl.Err
|
return nil, tpl.Err
|
||||||
|
@ -99,7 +99,7 @@ func doAdminListCategoriesRequest(user reqUser) ([]models.Category, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminListCategoriesHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminListCategoriesHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return nil, tpl.Err
|
return nil, tpl.Err
|
||||||
|
@ -119,7 +119,7 @@ func doAdminCategoryNewRequest(user reqUser, c *models.Category) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminCategoryNewPostHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminCategoryNewPostHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return -1, tpl.Err
|
return -1, tpl.Err
|
||||||
|
@ -144,7 +144,7 @@ func doAdminCategoryEditRequest(user reqUser, c *models.Category) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminCategoryEditPostHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminCategoryEditPostHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return tpl.Err
|
return tpl.Err
|
||||||
|
@ -166,7 +166,7 @@ func doAdminDeleteCategoryRequest(user reqUser, categoryID int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminCategoryDeletePostHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminCategoryDeletePostHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return tpl.Err
|
return tpl.Err
|
|
@ -1,4 +1,4 @@
|
||||||
package controllers
|
package handler
|
||||||
|
|
||||||
const (
|
const (
|
||||||
tplArticle = "front/article"
|
tplArticle = "front/article"
|
|
@ -1,4 +1,4 @@
|
||||||
package controllers
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -9,8 +9,8 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/components/httperror"
|
"git.hoogi.eu/snafu/go-blog/httperror"
|
||||||
"git.hoogi.eu/snafu/go-blog/components/logger"
|
"git.hoogi.eu/snafu/go-blog/logger"
|
||||||
"git.hoogi.eu/snafu/go-blog/middleware"
|
"git.hoogi.eu/snafu/go-blog/middleware"
|
||||||
"git.hoogi.eu/snafu/go-blog/models"
|
"git.hoogi.eu/snafu/go-blog/models"
|
||||||
)
|
)
|
|
@ -1,4 +1,4 @@
|
||||||
package controllers_test
|
package handler_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -7,7 +7,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/controllers"
|
"git.hoogi.eu/snafu/go-blog/handler"
|
||||||
"git.hoogi.eu/snafu/go-blog/models"
|
"git.hoogi.eu/snafu/go-blog/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ func doAdminListFilesRequest(user reqUser) ([]models.File, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminListFilesHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminListFilesHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return nil, tpl.Err
|
return nil, tpl.Err
|
||||||
|
@ -87,7 +87,7 @@ func doAdminGetFileRequest(user reqUser, uniquename string) (*httptest.ResponseR
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
|
|
||||||
fh := controllers.FileHandler{
|
fh := handler.FileHandler{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ func doAdminUploadFileRequest(user reqUser, file string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminUploadFilePostHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminUploadFilePostHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return tpl.Err
|
return tpl.Err
|
||||||
|
@ -139,7 +139,7 @@ func doAdminFileDeleteRequest(user reqUser, fileID int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminUploadDeletePostHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminUploadDeletePostHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return tpl.Err
|
return tpl.Err
|
|
@ -2,13 +2,13 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package controllers
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/components/logger"
|
"git.hoogi.eu/snafu/go-blog/logger"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package controllers
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
|
@ -1,4 +1,4 @@
|
||||||
package controllers_test
|
package handler_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -6,7 +6,7 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/controllers"
|
"git.hoogi.eu/snafu/go-blog/handler"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLogin(t *testing.T) {
|
func TestLogin(t *testing.T) {
|
||||||
|
@ -71,7 +71,7 @@ func doLoginRequest(user reqUser, login, password string) (responseWrapper, erro
|
||||||
}
|
}
|
||||||
|
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
tpl := controllers.LoginPostHandler(ctx, rr, r.buildRequest())
|
tpl := handler.LoginPostHandler(ctx, rr, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return responseWrapper{response: rr, template: tpl}, tpl.Err
|
return responseWrapper{response: rr, template: tpl}, tpl.Err
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package controllers
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
@ -10,7 +10,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/components/httperror"
|
"git.hoogi.eu/snafu/go-blog/httperror"
|
||||||
"git.hoogi.eu/snafu/go-blog/middleware"
|
"git.hoogi.eu/snafu/go-blog/middleware"
|
||||||
"git.hoogi.eu/snafu/go-blog/models"
|
"git.hoogi.eu/snafu/go-blog/models"
|
||||||
)
|
)
|
|
@ -1,4 +1,4 @@
|
||||||
package controllers_test
|
package handler_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -7,7 +7,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/controllers"
|
"git.hoogi.eu/snafu/go-blog/handler"
|
||||||
"git.hoogi.eu/snafu/go-blog/models"
|
"git.hoogi.eu/snafu/go-blog/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ func doGetSiteRequest(user reqUser, link string) (*models.Site, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.GetSiteHandler(ctx, rw, r.buildRequest())
|
tpl := handler.GetSiteHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return nil, tpl.Err
|
return nil, tpl.Err
|
||||||
|
@ -115,7 +115,7 @@ func doAdminGetSiteRequest(user reqUser, siteID int) (*models.Site, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminGetSiteHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminGetSiteHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return nil, tpl.Err
|
return nil, tpl.Err
|
||||||
|
@ -132,7 +132,7 @@ func doAdminListSitesRequest(user reqUser) ([]models.Site, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminSitesHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminSitesHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return nil, tpl.Err
|
return nil, tpl.Err
|
||||||
|
@ -156,7 +156,7 @@ func doAdminSiteCreateRequest(user reqUser, s *models.Site) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminSiteNewPostHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminSiteNewPostHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return 0, tpl.Err
|
return 0, tpl.Err
|
||||||
|
@ -185,7 +185,7 @@ func doAdminSitePublishRequest(user reqUser, siteID int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminSitePublishPostHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminSitePublishPostHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return tpl.Err
|
return tpl.Err
|
||||||
|
@ -215,7 +215,7 @@ func doAdminSiteEditRequest(user reqUser, s *models.Site) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminSiteEditPostHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminSiteEditPostHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return tpl.Err
|
return tpl.Err
|
||||||
|
@ -238,7 +238,7 @@ func doAdminSiteDeleteRequest(user reqUser, siteID int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminSiteDeletePostHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminSiteDeletePostHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return tpl.Err
|
return tpl.Err
|
||||||
|
@ -261,7 +261,7 @@ func doAdminSiteOrderRequest(user reqUser, siteID int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminSiteOrderHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminSiteOrderHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return tpl.Err
|
return tpl.Err
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
@ -2,13 +2,13 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package controllers
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/components/httperror"
|
"git.hoogi.eu/snafu/go-blog/httperror"
|
||||||
"git.hoogi.eu/snafu/go-blog/middleware"
|
"git.hoogi.eu/snafu/go-blog/middleware"
|
||||||
"git.hoogi.eu/snafu/go-blog/models"
|
"git.hoogi.eu/snafu/go-blog/models"
|
||||||
)
|
)
|
||||||
|
@ -196,11 +196,11 @@ func AdminUserEditPostHandler(ctx *middleware.AppContext, w http.ResponseWriter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sids := ctx.SessionService.SessionProvider.SessionIDsFromValues("userid", u.ID)
|
sessions := ctx.SessionService.SessionProvider.FindSessionsByValue("userid", u.ID)
|
||||||
|
|
||||||
for _, id := range sids {
|
for _, s := range sessions {
|
||||||
if session.SessionID() != id {
|
if session.SessionID() != s.SessionID() {
|
||||||
ctx.SessionService.SessionProvider.Remove(id)
|
ctx.SessionService.SessionProvider.Remove(s.SessionID())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package controllers
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
|
@ -1,4 +1,4 @@
|
||||||
package controllers_test
|
package handler_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
@ -6,7 +6,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/controllers"
|
"git.hoogi.eu/snafu/go-blog/handler"
|
||||||
"git.hoogi.eu/snafu/go-blog/models"
|
"git.hoogi.eu/snafu/go-blog/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ func doAdminCreateUserInviteRequest(user reqUser, ui *models.UserInvite) (int, s
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminUserInviteNewPostHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminUserInviteNewPostHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return -1, "", tpl.Err
|
return -1, "", tpl.Err
|
||||||
|
@ -93,7 +93,7 @@ func doAdminResendUserInviteRequest(user reqUser, inviteID int) (int, string, er
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminUserInviteResendPostHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminUserInviteResendPostHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return -1, "", tpl.Err
|
return -1, "", tpl.Err
|
||||||
|
@ -116,7 +116,7 @@ func doAdminRemoveUserInviteRequest(user reqUser, inviteID int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminUserInviteDeletePostHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminUserInviteDeletePostHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return tpl.Err
|
return tpl.Err
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package controllers_test
|
package handler_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -11,7 +11,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/controllers"
|
"git.hoogi.eu/snafu/go-blog/handler"
|
||||||
"git.hoogi.eu/snafu/go-blog/models"
|
"git.hoogi.eu/snafu/go-blog/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ func doAdminGetUserRequest(user reqUser, userID int) (*models.User, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminUserEditHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminUserEditHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return nil, tpl.Err
|
return nil, tpl.Err
|
||||||
|
@ -156,7 +156,7 @@ func doAdminEditUsersRequest(user reqUser, u *models.User) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminUserEditPostHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminUserEditPostHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return tpl.Err
|
return tpl.Err
|
||||||
|
@ -176,7 +176,7 @@ func doAdminListUsersRequest(user reqUser) ([]models.User, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminUsersHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminUsersHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return nil, tpl.Err
|
return nil, tpl.Err
|
||||||
|
@ -202,7 +202,7 @@ func doAdminCreateUserRequest(user reqUser, u *models.User) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
tpl := controllers.AdminUserNewPostHandler(ctx, rw, r.buildRequest())
|
tpl := handler.AdminUserNewPostHandler(ctx, rw, r.buildRequest())
|
||||||
|
|
||||||
if tpl.Err != nil {
|
if tpl.Err != nil {
|
||||||
return 0, tpl.Err
|
return 0, tpl.Err
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package controllers_test
|
package handler_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -20,13 +20,13 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/components/database"
|
"git.hoogi.eu/snafu/go-blog/crypt"
|
||||||
"git.hoogi.eu/snafu/go-blog/components/logger"
|
"git.hoogi.eu/snafu/go-blog/database"
|
||||||
"git.hoogi.eu/snafu/go-blog/components/mail"
|
"git.hoogi.eu/snafu/go-blog/logger"
|
||||||
|
"git.hoogi.eu/snafu/go-blog/mail"
|
||||||
"git.hoogi.eu/snafu/go-blog/middleware"
|
"git.hoogi.eu/snafu/go-blog/middleware"
|
||||||
"git.hoogi.eu/snafu/go-blog/models"
|
"git.hoogi.eu/snafu/go-blog/models"
|
||||||
"git.hoogi.eu/snafu/go-blog/settings"
|
"git.hoogi.eu/snafu/go-blog/settings"
|
||||||
"git.hoogi.eu/snafu/go-blog/utils"
|
|
||||||
"git.hoogi.eu/snafu/session"
|
"git.hoogi.eu/snafu/session"
|
||||||
|
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
@ -145,9 +145,9 @@ func teardown() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func fillSeeds(db *sql.DB) error {
|
func fillSeeds(db *sql.DB) error {
|
||||||
salt := utils.GenerateSalt()
|
salt := crypt.GenerateSalt()
|
||||||
saltedPassword := utils.AppendBytes([]byte("123456789012"), salt)
|
saltedPassword := append([]byte("123456789012"), salt[:]...)
|
||||||
password, err := utils.CryptPassword([]byte(saltedPassword), 12)
|
password, err := crypt.CryptPassword([]byte(saltedPassword), 12)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
|
@ -84,23 +84,6 @@ func ValueRequired(param string) *Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Equals(a error, b error) bool {
|
|
||||||
v, ok := a.(*Error)
|
|
||||||
v2, ok2 := b.(*Error)
|
|
||||||
|
|
||||||
if ok && ok2 {
|
|
||||||
return v.Err == v2.Err
|
|
||||||
} else if !ok && !ok2 {
|
|
||||||
return v == v2
|
|
||||||
} else if ok && !ok2 {
|
|
||||||
return v.Err == b
|
|
||||||
} else if !ok && ok2 {
|
|
||||||
return a == v2.Err
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e Error) Error() string {
|
func (e Error) Error() string {
|
||||||
return fmt.Sprintf("code=[%d], error=[%s], displayMsg=[%s]", e.HTTPStatus, e.Err.Error(), e.DisplayMsg)
|
return fmt.Sprintf("code=[%d], error=[%s], displayMsg=[%s]", e.HTTPStatus, e.Err.Error(), e.DisplayMsg)
|
||||||
}
|
}
|
|
@ -14,14 +14,9 @@ import (
|
||||||
//Log returns a new logrus instance
|
//Log returns a new logrus instance
|
||||||
var Log = logrus.New()
|
var Log = logrus.New()
|
||||||
|
|
||||||
//InitLogger initializes the logger. Writes to file with the specified log level
|
//InitLogger initializes the logger
|
||||||
//Valid log levels are:
|
//Valid log levels are: debug|info|warn|error|fatal|panic
|
||||||
// debug
|
//Fallback: info
|
||||||
// info (fallback)
|
|
||||||
// warn
|
|
||||||
// error
|
|
||||||
// fatal
|
|
||||||
// panic
|
|
||||||
func InitLogger(w io.Writer, level string) {
|
func InitLogger(w io.Writer, level string) {
|
||||||
level = strings.ToLower(level)
|
level = strings.ToLower(level)
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/smtp"
|
"net/smtp"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/components/logger"
|
"git.hoogi.eu/snafu/go-blog/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
//Service holds configuration for the SMTP server
|
//Service holds configuration for the SMTP server
|
6
main.go
6
main.go
|
@ -13,9 +13,9 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/cfg"
|
"git.hoogi.eu/snafu/cfg"
|
||||||
"git.hoogi.eu/snafu/go-blog/components/database"
|
"git.hoogi.eu/snafu/go-blog/database"
|
||||||
"git.hoogi.eu/snafu/go-blog/components/logger"
|
"git.hoogi.eu/snafu/go-blog/logger"
|
||||||
"git.hoogi.eu/snafu/go-blog/components/mail"
|
"git.hoogi.eu/snafu/go-blog/mail"
|
||||||
m "git.hoogi.eu/snafu/go-blog/middleware"
|
m "git.hoogi.eu/snafu/go-blog/middleware"
|
||||||
"git.hoogi.eu/snafu/go-blog/models"
|
"git.hoogi.eu/snafu/go-blog/models"
|
||||||
"git.hoogi.eu/snafu/go-blog/routers"
|
"git.hoogi.eu/snafu/go-blog/routers"
|
||||||
|
|
|
@ -8,8 +8,8 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/components/httperror"
|
"git.hoogi.eu/snafu/go-blog/httperror"
|
||||||
"git.hoogi.eu/snafu/go-blog/components/logger"
|
"git.hoogi.eu/snafu/go-blog/logger"
|
||||||
"git.hoogi.eu/snafu/go-blog/models"
|
"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)
|
type JHandler func(*AppContext, http.ResponseWriter, *http.Request) (*models.JSONData, error)
|
||||||
|
|
||||||
func (fn JSONHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
func (fn JSONHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||||
statusCode := 200
|
code := http.StatusOK
|
||||||
|
|
||||||
rw.Header().Set("Content-Type", "application/json")
|
rw.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
@ -32,34 +32,34 @@ func (fn JSONHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch e := err.(type) {
|
switch e := err.(type) {
|
||||||
case *httperror.Error:
|
case *httperror.Error:
|
||||||
statusCode = e.HTTPStatus
|
code = e.HTTPStatus
|
||||||
default:
|
default:
|
||||||
statusCode = 500
|
code = http.StatusInternalServerError
|
||||||
logger.Log.Error(e)
|
logger.Log.Error(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Log.Error(err)
|
logger.Log.Error(err)
|
||||||
|
|
||||||
mjson, err2 := json.Marshal(err)
|
j, err := json.Marshal(err)
|
||||||
if err2 != nil {
|
if err != nil {
|
||||||
logger.Log.Error(err2)
|
logger.Log.Error(err)
|
||||||
http.Error(rw, err2.Error(), http.StatusInternalServerError)
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rw.WriteHeader(statusCode)
|
rw.WriteHeader(code)
|
||||||
rw.Write(mjson)
|
rw.Write(j)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mjson, err2 := json.Marshal(data)
|
j, err := json.Marshal(data)
|
||||||
|
|
||||||
if err2 != nil {
|
if err != nil {
|
||||||
http.Error(rw, err2.Error(), http.StatusInternalServerError)
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||||
rw.WriteHeader(500)
|
rw.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rw.WriteHeader(statusCode)
|
rw.WriteHeader(code)
|
||||||
rw.Write(mjson)
|
rw.Write(j)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,13 @@
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/base64"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/components/logger"
|
"git.hoogi.eu/snafu/go-blog/logger"
|
||||||
"git.hoogi.eu/snafu/go-blog/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var locals = [...]net.IPNet{
|
var locals = [...]net.IPNet{
|
||||||
|
@ -72,7 +72,7 @@ func setCookie(rw http.ResponseWriter, name, path, data string) {
|
||||||
c := &http.Cookie{
|
c := &http.Cookie{
|
||||||
Name: name,
|
Name: name,
|
||||||
Path: path,
|
Path: path,
|
||||||
Value: utils.EncodeBase64(data),
|
Value: base64.StdEncoding.EncodeToString([]byte(data)),
|
||||||
}
|
}
|
||||||
|
|
||||||
http.SetCookie(rw, c)
|
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) {
|
func getFlash(w http.ResponseWriter, r *http.Request, name string) (string, error) {
|
||||||
c, err := r.Cookie(name)
|
c, err := r.Cookie(name)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err {
|
switch err {
|
||||||
case http.ErrNoCookie:
|
case http.ErrNoCookie:
|
||||||
|
@ -88,7 +89,9 @@ func getFlash(w http.ResponseWriter, r *http.Request, name string) (string, erro
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
value, err := utils.DecodeBase64(c.Value)
|
|
||||||
|
value, err := base64.StdEncoding.DecodeString(c.Value)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -98,9 +101,10 @@ func getFlash(w http.ResponseWriter, r *http.Request, name string) (string, erro
|
||||||
Name: name,
|
Name: name,
|
||||||
MaxAge: -1,
|
MaxAge: -1,
|
||||||
Expires: time.Unix(1, 0),
|
Expires: time.Unix(1, 0),
|
||||||
Path: "/"}
|
Path: "/",
|
||||||
|
}
|
||||||
|
|
||||||
http.SetCookie(w, dc)
|
http.SetCookie(w, dc)
|
||||||
|
|
||||||
return value, nil
|
return string(value), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,10 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/cfg"
|
"git.hoogi.eu/snafu/cfg"
|
||||||
"git.hoogi.eu/snafu/go-blog/components/httperror"
|
"git.hoogi.eu/snafu/go-blog/httperror"
|
||||||
"git.hoogi.eu/snafu/go-blog/components/logger"
|
"git.hoogi.eu/snafu/go-blog/logger"
|
||||||
"git.hoogi.eu/snafu/go-blog/models"
|
"git.hoogi.eu/snafu/go-blog/models"
|
||||||
"git.hoogi.eu/snafu/go-blog/settings"
|
"git.hoogi.eu/snafu/go-blog/settings"
|
||||||
"git.hoogi.eu/snafu/go-blog/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Template contains the information about the template to render.
|
// 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")
|
getFlash(rw, r, "SuccessMsg")
|
||||||
|
|
||||||
session, _ := ctx.SessionService.Get(rw, r)
|
session, _ := ctx.SessionService.Get(rw, r)
|
||||||
|
|
||||||
if session != nil && strings.HasPrefix(r.URL.EscapedPath(), "/admin") {
|
if session != nil && strings.HasPrefix(r.URL.EscapedPath(), "/admin") {
|
||||||
return &Template{
|
return &Template{
|
||||||
Name: "admin/error",
|
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")
|
return t.Time.In(time.Local).Format("January 2, 2006 at 3:04 PM")
|
||||||
},
|
},
|
||||||
"HumanizeFilesize": func(size int64) string {
|
"HumanizeFilesize": func(size int64) string {
|
||||||
fs := cfg.FileSize(size)
|
return cfg.FileSize(size).HumanReadable()
|
||||||
return fs.HumanReadable()
|
|
||||||
},
|
},
|
||||||
"FormatDateTime": func(t time.Time) string {
|
"FormatDateTime": func(t time.Time) string {
|
||||||
return t.In(time.Local).Format("January 2, 2006 at 3:04 PM")
|
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)))
|
return template.HTML(models.MarkdownToHTML([]byte(s)))
|
||||||
},
|
},
|
||||||
"NToBr": func(in string) template.HTML {
|
"NToBr": func(in string) template.HTML {
|
||||||
out := models.NewlineToBr(models.EscapeHTML(in))
|
return template.HTML(models.NewlineToBr(models.EscapeHTML(in)))
|
||||||
return template.HTML(out)
|
|
||||||
},
|
},
|
||||||
"EscapeHTML": func(in string) string {
|
"EscapeHTML": func(in string) string {
|
||||||
return html.EscapeString(in)
|
return html.EscapeString(in)
|
||||||
|
@ -234,5 +232,5 @@ func (t Template) RedirectURL() string {
|
||||||
if t.RedirectPath[0] == byte('/') {
|
if t.RedirectPath[0] == byte('/') {
|
||||||
return t.RedirectPath
|
return t.RedirectPath
|
||||||
}
|
}
|
||||||
return utils.AppendString("/", t.RedirectPath)
|
return "/" + t.RedirectPath
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,8 @@ import (
|
||||||
|
|
||||||
"github.com/gorilla/csrf"
|
"github.com/gorilla/csrf"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/components/httperror"
|
"git.hoogi.eu/snafu/go-blog/httperror"
|
||||||
"git.hoogi.eu/snafu/go-blog/components/logger"
|
"git.hoogi.eu/snafu/go-blog/logger"
|
||||||
"git.hoogi.eu/snafu/go-blog/models"
|
"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) {
|
func (fn TemplateHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||||
var errorMsg, warnMsg, successMsg string
|
var errorMsg, warnMsg, successMsg string
|
||||||
statusCode := 200
|
|
||||||
|
code := http.StatusOK
|
||||||
|
|
||||||
ip := getIP(r)
|
ip := getIP(r)
|
||||||
en := logger.Log.WithField("ip", ip)
|
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) {
|
switch e := t.Err.(type) {
|
||||||
case *httperror.Error:
|
case *httperror.Error:
|
||||||
statusCode = e.HTTPStatus
|
code = e.HTTPStatus
|
||||||
en.Error(e)
|
en.Error(e)
|
||||||
errorMsg = e.DisplayMsg
|
errorMsg = e.DisplayMsg
|
||||||
default:
|
default:
|
||||||
|
@ -99,14 +100,14 @@ func (fn TemplateHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||||
t.Data[csrf.TemplateTag] = csrf.TemplateField(r)
|
t.Data[csrf.TemplateTag] = csrf.TemplateField(r)
|
||||||
t.Data["active"] = t.Active
|
t.Data["active"] = t.Active
|
||||||
|
|
||||||
rw.WriteHeader(statusCode)
|
rw.WriteHeader(code)
|
||||||
|
|
||||||
if err := fn.AppCtx.Templates.ExecuteTemplate(rw, t.Name, t.Data); err != nil {
|
if err := fn.AppCtx.Templates.ExecuteTemplate(rw, t.Name, t.Data); err != nil {
|
||||||
en.Error(err)
|
en.Error(err)
|
||||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
statusCode = http.StatusFound
|
code = http.StatusFound
|
||||||
if len(errorMsg) > 0 {
|
if len(errorMsg) > 0 {
|
||||||
setCookie(rw, "ErrorMsg", "/", errorMsg)
|
setCookie(rw, "ErrorMsg", "/", errorMsg)
|
||||||
} else if len(warnMsg) > 0 {
|
} else if len(warnMsg) > 0 {
|
||||||
|
@ -114,7 +115,7 @@ func (fn TemplateHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||||
} else if len(successMsg) > 0 {
|
} else if len(successMsg) > 0 {
|
||||||
setCookie(rw, "SuccessMsg", "/", successMsg)
|
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)
|
userid, ok := session.GetValue("userid").(int)
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
logger.Log.Errorf("userid is not an integer %v", userid)
|
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)
|
u, err := ctx.UserService.GetByID(userid)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log.Error(err)
|
logger.Log.Error(err)
|
||||||
rw.WriteHeader(http.StatusUnauthorized)
|
rw.WriteHeader(http.StatusUnauthorized)
|
||||||
|
@ -159,8 +162,7 @@ func (ctx AppContext) AuthHandler(handler http.Handler) http.Handler {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.WithValue(r.Context(), UserContextKey, u)
|
handler.ServeHTTP(rw, r.WithContext(context.WithValue(r.Context(), UserContextKey, u)))
|
||||||
handler.ServeHTTP(rw, r.WithContext(ctx))
|
|
||||||
}
|
}
|
||||||
return http.HandlerFunc(fn)
|
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) {
|
func User(r *http.Request) (*models.User, error) {
|
||||||
v := r.Context().Value(UserContextKey)
|
v := r.Context().Value(UserContextKey)
|
||||||
if v == nil {
|
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
|
return v.(*models.User), nil
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/components/logger"
|
"git.hoogi.eu/snafu/go-blog/logger"
|
||||||
"git.hoogi.eu/snafu/go-blog/models"
|
"git.hoogi.eu/snafu/go-blog/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -26,11 +26,11 @@ func (fn XMLHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Log.Error(err)
|
logger.Log.Error(err)
|
||||||
|
|
||||||
x, err2 := xml.Marshal(err)
|
x, err := xml.Marshal(err)
|
||||||
|
|
||||||
if err2 != nil {
|
if err != nil {
|
||||||
logger.Log.Error(err2)
|
logger.Log.Error(err)
|
||||||
http.Error(rw, err2.Error(), http.StatusInternalServerError)
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,9 +14,9 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"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/settings"
|
||||||
"git.hoogi.eu/snafu/go-blog/utils"
|
"git.hoogi.eu/snafu/go-blog/slug"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Article represents an article
|
// Article represents an article
|
||||||
|
@ -60,17 +60,21 @@ func (a Article) SlugEscape() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Article) buildSlug(now time.Time, suffix int) 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 {
|
func (a *Article) slug(as ArticleService, now time.Time) error {
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
a.Slug = a.buildSlug(now, i)
|
a.Slug = a.buildSlug(now, i)
|
||||||
|
|
||||||
_, err := as.Datasource.GetBySlug(a.Slug, nil, All)
|
if _, err := as.Datasource.GetBySlug(a.Slug, nil, All); err != nil {
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
if err != nil {
|
|
||||||
if err == sql.ErrNoRows {
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
@ -118,18 +122,11 @@ func (as ArticleService) Create(a *Article) (int, error) {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err := a.slug(as, now)
|
if err := a.slug(as, now); err != nil {
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
|
||||||
artID, err := as.Datasource.Create(a)
|
return as.Datasource.Create(a)
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return artID, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Update updates an article
|
//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)
|
a, err := as.Datasource.GetBySlug(s, u, pc)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == sql.ErrNoRows {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
return nil, httperror.NotFound("article", err)
|
return nil, httperror.NotFound("article", err)
|
||||||
}
|
}
|
||||||
return nil, 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)
|
a, err := as.Datasource.Get(id, u, pc)
|
||||||
|
|
||||||
if err != nil {
|
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, httperror.NotFound("article", fmt.Errorf("the article with id %d was not found", id))
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -272,7 +269,8 @@ func (as ArticleService) RSSFeed(p *Pagination, pc PublishedCriteria) (RSS, erro
|
||||||
return RSS{}, err
|
return RSS{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
items := []RSSItem{}
|
var items []RSSItem
|
||||||
|
|
||||||
for _, a := range articles {
|
for _, a := range articles {
|
||||||
link := fmt.Sprint(as.AppConfig.Domain, "/article/by-id/", a.ID)
|
link := fmt.Sprint(as.AppConfig.Domain, "/article/by-id/", a.ID)
|
||||||
item := RSSItem{
|
item := RSSItem{
|
||||||
|
|
|
@ -76,10 +76,9 @@ func (rdb SQLiteArticleDatasource) List(u *User, c *Category, p *Pagination, pc
|
||||||
//the PublishedCritera specifies which articles should be considered
|
//the PublishedCritera specifies which articles should be considered
|
||||||
func (rdb SQLiteArticleDatasource) Count(u *User, c *Category, pc PublishedCriteria) (int, error) {
|
func (rdb SQLiteArticleDatasource) Count(u *User, c *Category, pc PublishedCriteria) (int, error) {
|
||||||
var total int
|
var total int
|
||||||
|
|
||||||
var args []interface{}
|
var args []interface{}
|
||||||
|
|
||||||
var stmt bytes.Buffer
|
var stmt bytes.Buffer
|
||||||
|
|
||||||
stmt.WriteString("SELECT count(a.id) FROM article a ")
|
stmt.WriteString("SELECT count(a.id) FROM article a ")
|
||||||
|
|
||||||
if c != nil {
|
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=? ")
|
stmt.WriteString("AND a.id=? ")
|
||||||
args = append(args, articleID)
|
args = append(args, articleID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if u != nil {
|
if u != nil {
|
||||||
if !u.IsAdmin {
|
if !u.IsAdmin {
|
||||||
stmt.WriteString("AND a.user_id=? ")
|
stmt.WriteString("AND a.user_id=? ")
|
||||||
args = append(args, u.ID)
|
args = append(args, u.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt.WriteString("LIMIT 1")
|
stmt.WriteString("LIMIT 1")
|
||||||
|
|
||||||
return db.QueryRow(stmt.String(), args...)
|
return db.QueryRow(stmt.String(), args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func selectArticlesStmt(db *sql.DB, u *User, c *Category, p *Pagination, pc PublishedCriteria) (*sql.Rows, error) {
|
func selectArticlesStmt(db *sql.DB, u *User, c *Category, p *Pagination, pc PublishedCriteria) (*sql.Rows, error) {
|
||||||
var stmt bytes.Buffer
|
var stmt bytes.Buffer
|
||||||
|
|
||||||
var args []interface{}
|
var args []interface{}
|
||||||
|
|
||||||
stmt.WriteString("SELECT a.id, a.headline, a.teaser, a.content, a.published, a.published_on, a.slug, a.last_modified, ")
|
stmt.WriteString("SELECT a.id, a.headline, a.teaser, a.content, a.published, a.published_on, a.slug, a.last_modified, ")
|
||||||
|
|
|
@ -8,8 +8,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/components/httperror"
|
"git.hoogi.eu/snafu/go-blog/httperror"
|
||||||
"git.hoogi.eu/snafu/go-blog/utils"
|
"git.hoogi.eu/snafu/go-blog/slug"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Category struct {
|
type Category struct {
|
||||||
|
@ -66,7 +66,7 @@ func (cs CategoryService) GetBySlug(s string, fc FilterCriteria) (*Category, err
|
||||||
c, err := cs.Datasource.GetBySlug(s, fc)
|
c, err := cs.Datasource.GetBySlug(s, fc)
|
||||||
|
|
||||||
if err != nil {
|
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, httperror.NotFound("category", fmt.Errorf("the category with slug %s was not found", s))
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -79,7 +79,7 @@ func (cs CategoryService) GetByID(id int, fc FilterCriteria) (*Category, error)
|
||||||
c, err := cs.Datasource.Get(id, fc)
|
c, err := cs.Datasource.Get(id, fc)
|
||||||
|
|
||||||
if err != nil {
|
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, httperror.NotFound("category", fmt.Errorf("the category with id %d was not found", id))
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -99,11 +99,11 @@ func (cs CategoryService) List(fc FilterCriteria) ([]Category, error) {
|
||||||
//Create creates a category
|
//Create creates a category
|
||||||
func (cs CategoryService) Create(c *Category) (int, error) {
|
func (cs CategoryService) Create(c *Category) (int, error) {
|
||||||
for i := 0; i < 10; i++ {
|
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)
|
_, err := cs.Datasource.GetBySlug(c.Slug, AllCategories)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == sql.ErrNoRows {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return -1, err
|
return -1, err
|
||||||
|
|
|
@ -33,9 +33,8 @@ func (rdb SQLiteCategoryDatasource) Create(c *Category) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rdb SQLiteCategoryDatasource) List(fc FilterCriteria) ([]Category, error) {
|
func (rdb SQLiteCategoryDatasource) List(fc FilterCriteria) ([]Category, error) {
|
||||||
var stmt bytes.Buffer
|
|
||||||
|
|
||||||
var args []interface{}
|
var args []interface{}
|
||||||
|
var stmt bytes.Buffer
|
||||||
|
|
||||||
stmt.WriteString("SELECT DISTINCT c.id, c.name, c.slug, c.last_modified, ")
|
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 ")
|
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()
|
defer rows.Close()
|
||||||
|
|
||||||
cs := []Category{}
|
var cs []Category
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var c Category
|
var c Category
|
||||||
|
@ -139,6 +138,7 @@ func (rdb SQLiteCategoryDatasource) GetBySlug(slug string, fc FilterCriteria) (*
|
||||||
stmt.WriteString("FROM category as c ")
|
stmt.WriteString("FROM category as c ")
|
||||||
stmt.WriteString("INNER JOIN user as u ")
|
stmt.WriteString("INNER JOIN user as u ")
|
||||||
stmt.WriteString("ON u.id = c.user_id ")
|
stmt.WriteString("ON u.id = c.user_id ")
|
||||||
|
|
||||||
if fc == CategoriesWithPublishedArticles {
|
if fc == CategoriesWithPublishedArticles {
|
||||||
stmt.WriteString("INNER JOIN article as a ")
|
stmt.WriteString("INNER JOIN article as a ")
|
||||||
stmt.WriteString("ON c.id = a.category_id ")
|
stmt.WriteString("ON c.id = a.category_id ")
|
||||||
|
|
|
@ -11,11 +11,11 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/components/httperror"
|
"git.hoogi.eu/snafu/go-blog/httperror"
|
||||||
"git.hoogi.eu/snafu/go-blog/components/logger"
|
"git.hoogi.eu/snafu/go-blog/logger"
|
||||||
"git.hoogi.eu/snafu/go-blog/settings"
|
"git.hoogi.eu/snafu/go-blog/settings"
|
||||||
"git.hoogi.eu/snafu/go-blog/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//File represents a file
|
//File represents a file
|
||||||
|
@ -67,7 +67,7 @@ func (f *File) validate() error {
|
||||||
|
|
||||||
func (f File) randomFilename() string {
|
func (f File) randomFilename() string {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
sanFilename := utils.SanitizeFilename(f.FileInfo.Name)
|
sanFilename := sanitizeFilename(f.FileInfo.Name)
|
||||||
if len(sanFilename) == 0 {
|
if len(sanFilename) == 0 {
|
||||||
sanFilename = "unnamed"
|
sanFilename = "unnamed"
|
||||||
}
|
}
|
||||||
|
@ -238,3 +238,36 @@ func (fs FileService) Upload(f *File) (int, error) {
|
||||||
|
|
||||||
return i, nil
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ type SQLiteFileDatasource struct {
|
||||||
//only file specific to this user is returned
|
//only file specific to this user is returned
|
||||||
func (rdb SQLiteFileDatasource) GetByUniqueName(uniqueName string, u *User) (*File, error) {
|
func (rdb SQLiteFileDatasource) GetByUniqueName(uniqueName string, u *User) (*File, error) {
|
||||||
var stmt bytes.Buffer
|
var stmt bytes.Buffer
|
||||||
|
|
||||||
var args []interface{}
|
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, ")
|
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
|
//only file specific to this user is returned
|
||||||
func (rdb SQLiteFileDatasource) Get(fileID int, u *User) (*File, error) {
|
func (rdb SQLiteFileDatasource) Get(fileID int, u *User) (*File, error) {
|
||||||
var stmt bytes.Buffer
|
var stmt bytes.Buffer
|
||||||
|
|
||||||
var args []interface{}
|
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, ")
|
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()
|
defer rows.Close()
|
||||||
|
|
||||||
files := []File{}
|
var files []File
|
||||||
|
|
||||||
var f File
|
var f File
|
||||||
var us User
|
var us User
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Defines the extensions that are used
|
// 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.Strikethrough | bf.SpaceHeadings | bf.BackslashLineBreak |
|
||||||
bf.DefinitionLists | bf.Footnotes | bf.HardLineBreak
|
bf.DefinitionLists | bf.Footnotes | bf.HardLineBreak
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ func init() {
|
||||||
//MarkdownToHTML sanitizes and parses markdown to HTML
|
//MarkdownToHTML sanitizes and parses markdown to HTML
|
||||||
func MarkdownToHTML(md []byte) []byte {
|
func MarkdownToHTML(md []byte) []byte {
|
||||||
md = bytes.Replace(md, []byte("\r\n"), []byte("\n"), -1)
|
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)
|
return sanitize(unsafe)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,8 @@ package models
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"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/settings"
|
||||||
"git.hoogi.eu/snafu/go-blog/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Mailer struct {
|
type Mailer struct {
|
||||||
|
@ -14,7 +13,7 @@ type Mailer struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Mailer) SendActivationLink(ui *UserInvite) {
|
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{
|
mail := mail.Mail{
|
||||||
To: ui.Email,
|
To: ui.Email,
|
||||||
|
@ -36,7 +35,7 @@ func (m Mailer) SendPasswordChangeConfirmation(u *User) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Mailer) SendPasswordResetLink(u *User, t *Token) {
|
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{
|
mail := mail.Mail{
|
||||||
To: u.Email,
|
To: u.Email,
|
||||||
|
|
|
@ -5,12 +5,10 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"math"
|
"math"
|
||||||
|
"strings"
|
||||||
"git.hoogi.eu/snafu/go-blog/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//Pagination type is used to provide a page selector
|
//Pagination type is used to provide a page selector
|
||||||
|
@ -29,9 +27,9 @@ func (p Pagination) Offset() int {
|
||||||
//url returns the absolute url
|
//url returns the absolute url
|
||||||
func (p Pagination) url() string {
|
func (p Pagination) url() string {
|
||||||
if p.RelURL[0] == '/' {
|
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
|
//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
|
//PaginationBar returns the HTML for the pagination bar which can be embedded
|
||||||
func (p Pagination) PaginationBar() template.HTML {
|
func (p Pagination) PaginationBar() template.HTML {
|
||||||
var buffer bytes.Buffer
|
var sb strings.Builder
|
||||||
|
|
||||||
if p.pages() > 1 {
|
if p.pages() > 1 {
|
||||||
buffer.WriteString(`<div id="pagination">`)
|
sb.WriteString(`<div id="pagination">`)
|
||||||
|
|
||||||
if !p.hasPrevious() {
|
if !p.hasPrevious() {
|
||||||
buffer.WriteString(`<a class="button button-inactive" href="#">« Backward</a>`)
|
sb.WriteString(`<a class="button button-inactive" href="#">« Backward</a>`)
|
||||||
} else {
|
} else {
|
||||||
buffer.WriteString(fmt.Sprintf(`<a class="button button-active" href="%s/%d">« Backward</a>`, p.url(), p.previousPage()))
|
sb.WriteString(fmt.Sprintf(`<a class="button button-active" href="%s/%d">« Backward</a>`, p.url(), p.previousPage()))
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 1; i <= p.pages(); i++ {
|
for i := 1; i <= p.pages(); i++ {
|
||||||
if p.CurrentPage == 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 {
|
} 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() {
|
if !p.hasNext() {
|
||||||
buffer.WriteString(`<a class="button button-inactive" href="#">Forward »</a>`)
|
sb.WriteString(`<a class="button button-inactive" href="#">Forward »</a>`)
|
||||||
} else {
|
} else {
|
||||||
buffer.WriteString(fmt.Sprintf(`<a class="button button-active" href="%s/%d">Forward »</a>`, p.url(), p.nextPage()))
|
sb.WriteString(fmt.Sprintf(`<a class="button button-active" href="%s/%d">Forward »</a>`, p.url(), p.nextPage()))
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.WriteString(`</div>`)
|
sb.WriteString(`</div>`)
|
||||||
}
|
}
|
||||||
return template.HTML(buffer.String())
|
return template.HTML(sb.String())
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,8 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/components/httperror"
|
"git.hoogi.eu/snafu/go-blog/httperror"
|
||||||
"git.hoogi.eu/snafu/go-blog/utils"
|
"git.hoogi.eu/snafu/go-blog/slug"
|
||||||
)
|
)
|
||||||
|
|
||||||
//SiteDatasourceService defines an interface for CRUD operations on sites
|
//SiteDatasourceService defines an interface for CRUD operations on sites
|
||||||
|
@ -54,14 +54,14 @@ func (s Site) LinkEscape() string {
|
||||||
if s.isExternal() {
|
if s.isExternal() {
|
||||||
return s.Link
|
return s.Link
|
||||||
}
|
}
|
||||||
return utils.AppendString("/site/", url.PathEscape(s.Link))
|
return "/site/" + url.PathEscape(s.Link)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Site) safeLink() string {
|
func (s Site) safeLink() string {
|
||||||
if s.isExternal() {
|
if s.isExternal() {
|
||||||
return s.Link
|
return s.Link
|
||||||
}
|
}
|
||||||
return utils.CreateURLSafeSlug(s.Link, -1)
|
return slug.CreateURLSafeSlug(s.Link, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Site) isExternal() bool {
|
func (s Site) isExternal() bool {
|
||||||
|
@ -174,6 +174,7 @@ func (ss SiteService) Update(s *Site) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
changeLink := false
|
changeLink := false
|
||||||
|
|
||||||
if oldSite.Link != s.Link {
|
if oldSite.Link != s.Link {
|
||||||
changeLink = true
|
changeLink = true
|
||||||
s.Link = s.safeLink()
|
s.Link = s.safeLink()
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/components/logger"
|
"git.hoogi.eu/snafu/go-blog/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
//SQLiteSiteDatasource providing an implementation of SiteDatasourceService for sqlite
|
//SQLiteSiteDatasource providing an implementation of SiteDatasourceService for sqlite
|
||||||
|
@ -16,7 +16,6 @@ type SQLiteSiteDatasource struct {
|
||||||
//List returns a array of sites
|
//List returns a array of sites
|
||||||
func (rdb SQLiteSiteDatasource) List(pc PublishedCriteria, p *Pagination) ([]Site, error) {
|
func (rdb SQLiteSiteDatasource) List(pc PublishedCriteria, p *Pagination) ([]Site, error) {
|
||||||
var stmt bytes.Buffer
|
var stmt bytes.Buffer
|
||||||
|
|
||||||
var args []interface{}
|
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 ")
|
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...)
|
rows, err := rdb.SQLConn.Query(stmt.String(), args...)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
sites := []Site{}
|
var sites []Site
|
||||||
|
|
||||||
var s Site
|
var s Site
|
||||||
var u User
|
var u User
|
||||||
|
|
||||||
|
@ -129,6 +128,7 @@ func (rdb SQLiteSiteDatasource) GetByLink(link string, pc PublishedCriteria) (*S
|
||||||
//Publish publishes or unpublishes a site
|
//Publish publishes or unpublishes a site
|
||||||
func (rdb SQLiteSiteDatasource) Publish(s *Site) error {
|
func (rdb SQLiteSiteDatasource) Publish(s *Site) error {
|
||||||
publishOn := NullTime{Valid: false}
|
publishOn := NullTime{Valid: false}
|
||||||
|
|
||||||
if !s.Published {
|
if !s.Published {
|
||||||
publishOn = NullTime{Time: time.Now(), Valid: true}
|
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 d == Up {
|
||||||
if _, err = tx.Exec("UPDATE site "+
|
if _, err = tx.Exec("UPDATE site "+
|
||||||
"SET order_no=(SELECT order_no AS order_no FROM site WHERE id=?) "+
|
"SET order_no=(SELECT order_no AS order_no FROM site WHERE id=?) "+
|
||||||
|
|
|
@ -29,7 +29,7 @@ const (
|
||||||
All
|
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 {
|
type NullTime struct {
|
||||||
Time time.Time
|
Time time.Time
|
||||||
Valid bool
|
Valid bool
|
||||||
|
|
|
@ -7,9 +7,9 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/components/httperror"
|
"git.hoogi.eu/snafu/go-blog/crypt"
|
||||||
"git.hoogi.eu/snafu/go-blog/components/logger"
|
"git.hoogi.eu/snafu/go-blog/httperror"
|
||||||
"git.hoogi.eu/snafu/go-blog/utils"
|
"git.hoogi.eu/snafu/go-blog/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
//TokenDatasourceService defines an interface for CRUD operations for tokens
|
//TokenDatasourceService defines an interface for CRUD operations for tokens
|
||||||
|
@ -68,11 +68,13 @@ type TokenService struct {
|
||||||
|
|
||||||
//Create creates a new token
|
//Create creates a new token
|
||||||
func (ts TokenService) Create(t *Token) error {
|
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
|
//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()
|
now := time.Now()
|
||||||
|
|
||||||
rate := []Token{}
|
var rate []Token
|
||||||
for _, t := range tokens {
|
for _, t := range tokens {
|
||||||
if now.Sub(t.RequestedAt) < time.Minute*15 {
|
if now.Sub(t.RequestedAt) < time.Minute*15 {
|
||||||
rate = append(rate, t)
|
rate = append(rate, t)
|
||||||
|
|
|
@ -12,10 +12,10 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/components/httperror"
|
"git.hoogi.eu/snafu/go-blog/crypt"
|
||||||
"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/settings"
|
||||||
"git.hoogi.eu/snafu/go-blog/utils"
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
"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 {
|
func (us UserService) duplicateMail(mail string) error {
|
||||||
user, err := us.Datasource.GetByMail(mail)
|
user, err := us.Datasource.GetByMail(mail)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != sql.ErrNoRows {
|
if !errors.Is(err, sql.ErrNoRows) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,7 +150,7 @@ func (us UserService) duplicateMail(mail string) error {
|
||||||
func (us UserService) duplicateUsername(username string) error {
|
func (us UserService) duplicateUsername(username string) error {
|
||||||
user, err := us.Datasource.GetByUsername(username)
|
user, err := us.Datasource.GetByUsername(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != sql.ErrNoRows {
|
if !errors.Is(err, sql.ErrNoRows) {
|
||||||
return err
|
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
|
//GetByID gets the user based on the given id; will not contain the user password
|
||||||
func (us UserService) GetByID(userID int) (*User, error) {
|
func (us UserService) GetByID(userID int) (*User, error) {
|
||||||
u, err := us.Datasource.Get(userID)
|
u, err := us.Datasource.Get(userID)
|
||||||
|
|
||||||
if err != nil {
|
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, httperror.NotFound("user", fmt.Errorf("the user with id %d was not found", userID))
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -189,7 +191,7 @@ func (us UserService) GetByID(userID int) (*User, error) {
|
||||||
func (us UserService) GetByUsername(username string) (*User, error) {
|
func (us UserService) GetByUsername(username string) (*User, error) {
|
||||||
u, err := us.Datasource.GetByUsername(username)
|
u, err := us.Datasource.GetByUsername(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == sql.ErrNoRows {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
return nil, httperror.NotFound("user", err)
|
return nil, httperror.NotFound("user", err)
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -203,7 +205,7 @@ func (us UserService) GetByMail(mail string) (*User, error) {
|
||||||
u, err := us.Datasource.GetByMail(mail)
|
u, err := us.Datasource.GetByMail(mail)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == sql.ErrNoRows {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
return nil, httperror.NotFound("user", err)
|
return nil, httperror.NotFound("user", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,9 +227,9 @@ func (us UserService) Create(u *User) (int, error) {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
|
||||||
salt := utils.GenerateSalt()
|
salt := crypt.GenerateSalt()
|
||||||
saltedPassword := utils.AppendBytes(u.PlainPassword, salt)
|
saltedPassword := append(u.PlainPassword[:], salt[:]...)
|
||||||
password, err := utils.CryptPassword([]byte(saltedPassword), bcryptRounds)
|
password, err := crypt.CryptPassword([]byte(saltedPassword), bcryptRounds)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
|
@ -270,7 +272,6 @@ func (us UserService) Update(u *User, changePassword bool) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if us.UserInterceptor != nil {
|
if us.UserInterceptor != nil {
|
||||||
|
|
||||||
if err := us.UserInterceptor.PreUpdate(oldUser, u); err != nil {
|
if err := us.UserInterceptor.PreUpdate(oldUser, u); err != nil {
|
||||||
return httperror.InternalServerError(fmt.Errorf("error while executing user interceptor 'PreUpdate' error %v", err))
|
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 {
|
if changePassword {
|
||||||
salt := utils.GenerateSalt()
|
salt := crypt.GenerateSalt()
|
||||||
saltedPassword := utils.AppendBytes(u.PlainPassword, salt)
|
saltedPassword := append(u.PlainPassword[:], salt[:]...)
|
||||||
password, err := utils.CryptPassword([]byte(saltedPassword), bcryptRounds)
|
password, err := crypt.CryptPassword([]byte(saltedPassword), bcryptRounds)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -321,7 +322,9 @@ func (us UserService) Update(u *User, changePassword bool) error {
|
||||||
u.Salt = salt
|
u.Salt = salt
|
||||||
}
|
}
|
||||||
|
|
||||||
err = us.Datasource.Update(u, changePassword)
|
if err = us.Datasource.Update(u, changePassword); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
u.Password = nil
|
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)
|
logger.Log.Errorf("error while executing PostUpdate user interceptor method %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u.PlainPassword = nil
|
u.PlainPassword = nil
|
||||||
|
|
||||||
return err
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authenticate authenticates the user by the given login method (email or username)
|
// 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 != nil {
|
||||||
if err == sql.ErrNoRows {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
//Do some extra work
|
//Do some extra work
|
||||||
bcrypt.CompareHashAndPassword([]byte("$2a$12$bQlRnXTNZMp6kCyoAlnf3uZW5vtmSj9CHP7pYplRUVK2n0C5xBHBa"), password)
|
bcrypt.CompareHashAndPassword([]byte("$2a$12$bQlRnXTNZMp6kCyoAlnf3uZW5vtmSj9CHP7pYplRUVK2n0C5xBHBa"), password)
|
||||||
return nil, httperror.New(http.StatusUnauthorized, "Your username or password is invalid.", err)
|
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 {
|
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[:]...))
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,11 @@ package models
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/components/mail"
|
"git.hoogi.eu/snafu/go-blog/crypt"
|
||||||
"git.hoogi.eu/snafu/go-blog/utils"
|
"git.hoogi.eu/snafu/go-blog/mail"
|
||||||
)
|
)
|
||||||
|
|
||||||
//User represents a user
|
//UserInvite represents a new invited user
|
||||||
type UserInvite struct {
|
type UserInvite struct {
|
||||||
ID int
|
ID int
|
||||||
Hash string
|
Hash string
|
||||||
|
@ -64,7 +64,7 @@ func (uis UserInviteService) List() ([]UserInvite, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uis UserInviteService) Update(ui *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 {
|
if err := ui.validate(uis); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -74,7 +74,7 @@ func (uis UserInviteService) Update(ui *UserInvite) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uis UserInviteService) Create(ui *UserInvite) (int, 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 {
|
if err := ui.validate(uis); err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
|
|
|
@ -14,6 +14,9 @@ type SQLiteUserInviteDatasource struct {
|
||||||
func (rdb SQLiteUserInviteDatasource) List() ([]UserInvite, error) {
|
func (rdb SQLiteUserInviteDatasource) List() ([]UserInvite, error) {
|
||||||
var stmt bytes.Buffer
|
var stmt bytes.Buffer
|
||||||
var args []interface{}
|
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("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 ")
|
stmt.WriteString("u.id, u.username, u.email, u.display_name ")
|
||||||
|
@ -30,11 +33,6 @@ func (rdb SQLiteUserInviteDatasource) List() ([]UserInvite, error) {
|
||||||
|
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
invites := []UserInvite{}
|
|
||||||
|
|
||||||
var ui UserInvite
|
|
||||||
var u User
|
|
||||||
|
|
||||||
for rows.Next() {
|
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 {
|
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
|
return nil, err
|
||||||
|
@ -111,6 +109,7 @@ func (rdb SQLiteUserInviteDatasource) Create(ui *UserInvite) (int, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return int(i), nil
|
return int(i), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,11 +126,7 @@ func (rdb SQLiteUserInviteDatasource) Count() (int, error) {
|
||||||
|
|
||||||
//Removes an user invitation
|
//Removes an user invitation
|
||||||
func (rdb SQLiteUserInviteDatasource) Remove(inviteID int) error {
|
func (rdb SQLiteUserInviteDatasource) Remove(inviteID int) error {
|
||||||
var stmt bytes.Buffer
|
if _, err := rdb.SQLConn.Exec("DELETE FROM user_invite WHERE id=?", inviteID); err != nil {
|
||||||
|
|
||||||
stmt.WriteString("DELETE FROM user_invite WHERE id=?")
|
|
||||||
|
|
||||||
if _, err := rdb.SQLConn.Exec(stmt.String(), inviteID); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,8 @@ type SQLiteUserDatasource struct {
|
||||||
func (rdb SQLiteUserDatasource) List(p *Pagination) ([]User, error) {
|
func (rdb SQLiteUserDatasource) List(p *Pagination) ([]User, error) {
|
||||||
var stmt bytes.Buffer
|
var stmt bytes.Buffer
|
||||||
var args []interface{}
|
var args []interface{}
|
||||||
|
var users []User
|
||||||
|
var u User
|
||||||
|
|
||||||
stmt.WriteString("SELECT id, username, email, display_name, last_modified, active, is_admin FROM user ORDER BY username ASC ")
|
stmt.WriteString("SELECT id, username, email, display_name, last_modified, active, is_admin FROM user ORDER BY username ASC ")
|
||||||
|
|
||||||
|
@ -31,10 +33,6 @@ func (rdb SQLiteUserDatasource) List(p *Pagination) ([]User, error) {
|
||||||
|
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
users := []User{}
|
|
||||||
|
|
||||||
var u User
|
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
if err = rows.Scan(&u.ID, &u.Username, &u.Email, &u.DisplayName, &u.LastModified, &u.Active, &u.IsAdmin); err != nil {
|
if err = rows.Scan(&u.ID, &u.Username, &u.Email, &u.DisplayName, &u.LastModified, &u.Active, &u.IsAdmin); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -106,7 +104,6 @@ func (rdb SQLiteUserDatasource) Create(u *User) (int, error) {
|
||||||
//Update updates an user
|
//Update updates an user
|
||||||
func (rdb SQLiteUserDatasource) Update(u *User, changePassword bool) error {
|
func (rdb SQLiteUserDatasource) Update(u *User, changePassword bool) error {
|
||||||
var stmt bytes.Buffer
|
var stmt bytes.Buffer
|
||||||
|
|
||||||
var args []interface{}
|
var args []interface{}
|
||||||
|
|
||||||
stmt.WriteString("UPDATE user SET display_name=?, username=?, email=?, last_modified=?, active=?, is_admin=? ")
|
stmt.WriteString("UPDATE user SET display_name=?, username=?, email=?, last_modified=?, active=?, is_admin=? ")
|
||||||
|
@ -128,10 +125,9 @@ func (rdb SQLiteUserDatasource) Update(u *User, changePassword bool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//Count retuns the amount of users matches the AdminCriteria
|
//Count returns the amount of users matches the AdminCriteria
|
||||||
func (rdb SQLiteUserDatasource) Count(ac AdminCriteria) (int, error) {
|
func (rdb SQLiteUserDatasource) Count(ac AdminCriteria) (int, error) {
|
||||||
var stmt bytes.Buffer
|
var stmt bytes.Buffer
|
||||||
|
|
||||||
stmt.WriteString("SELECT count(id) FROM user ")
|
stmt.WriteString("SELECT count(id) FROM user ")
|
||||||
|
|
||||||
if ac == OnlyAdmins {
|
if ac == OnlyAdmins {
|
||||||
|
@ -151,11 +147,7 @@ func (rdb SQLiteUserDatasource) Count(ac AdminCriteria) (int, error) {
|
||||||
|
|
||||||
//Removes an user
|
//Removes an user
|
||||||
func (rdb SQLiteUserDatasource) Remove(userID int) error {
|
func (rdb SQLiteUserDatasource) Remove(userID int) error {
|
||||||
var stmt bytes.Buffer
|
if _, err := rdb.SQLConn.Exec("DELETE FROM user WHERE id=?", userID); err != nil {
|
||||||
|
|
||||||
stmt.WriteString("DELETE FROM user WHERE id=?")
|
|
||||||
|
|
||||||
if _, err := rdb.SQLConn.Exec(stmt.String(), userID); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
c "git.hoogi.eu/snafu/go-blog/controllers"
|
"git.hoogi.eu/snafu/go-blog/handler"
|
||||||
m "git.hoogi.eu/snafu/go-blog/middleware"
|
m "git.hoogi.eu/snafu/go-blog/middleware"
|
||||||
"git.hoogi.eu/snafu/go-blog/settings"
|
"git.hoogi.eu/snafu/go-blog/settings"
|
||||||
|
|
||||||
|
@ -90,108 +90,108 @@ func fileLoggingHandler(accessLogPath string) (flh func(http.Handler) http.Handl
|
||||||
|
|
||||||
func restrictedRoutes(ctx *m.AppContext, router *mux.Router, chain alice.Chain) {
|
func restrictedRoutes(ctx *m.AppContext, router *mux.Router, chain alice.Chain) {
|
||||||
//article
|
//article
|
||||||
router.Handle("/articles", chain.Then(useTemplateHandler(ctx, c.AdminListArticlesHandler))).Methods("GET")
|
router.Handle("/articles", chain.Then(useTemplateHandler(ctx, handler.AdminListArticlesHandler))).Methods("GET")
|
||||||
router.Handle("/articles/page/{page}", chain.Then(useTemplateHandler(ctx, c.AdminListArticlesHandler))).Methods("GET")
|
router.Handle("/articles/page/{page}", chain.Then(useTemplateHandler(ctx, handler.AdminListArticlesHandler))).Methods("GET")
|
||||||
router.Handle("/article/new", chain.Then(useTemplateHandler(ctx, c.AdminArticleNewHandler))).Methods("GET")
|
router.Handle("/article/new", chain.Then(useTemplateHandler(ctx, handler.AdminArticleNewHandler))).Methods("GET")
|
||||||
router.Handle("/article/new", chain.Then(useTemplateHandler(ctx, c.AdminArticleNewPostHandler))).Methods("POST")
|
router.Handle("/article/new", chain.Then(useTemplateHandler(ctx, handler.AdminArticleNewPostHandler))).Methods("POST")
|
||||||
router.Handle("/article/edit/{articleID}", chain.Then(useTemplateHandler(ctx, c.AdminArticleEditHandler))).Methods("GET")
|
router.Handle("/article/edit/{articleID}", chain.Then(useTemplateHandler(ctx, handler.AdminArticleEditHandler))).Methods("GET")
|
||||||
router.Handle("/article/edit/{articleID}", chain.Then(useTemplateHandler(ctx, c.AdminArticleEditPostHandler))).Methods("POST")
|
router.Handle("/article/edit/{articleID}", chain.Then(useTemplateHandler(ctx, handler.AdminArticleEditPostHandler))).Methods("POST")
|
||||||
router.Handle("/article/publish/{articleID}", chain.Then(useTemplateHandler(ctx, c.AdminArticlePublishHandler))).Methods("GET")
|
router.Handle("/article/publish/{articleID}", chain.Then(useTemplateHandler(ctx, handler.AdminArticlePublishHandler))).Methods("GET")
|
||||||
router.Handle("/article/publish/{articleID}", chain.Then(useTemplateHandler(ctx, c.AdminArticlePublishPostHandler))).Methods("POST")
|
router.Handle("/article/publish/{articleID}", chain.Then(useTemplateHandler(ctx, handler.AdminArticlePublishPostHandler))).Methods("POST")
|
||||||
router.Handle("/article/delete/{articleID}", chain.Then(useTemplateHandler(ctx, c.AdminArticleDeleteHandler))).Methods("GET")
|
router.Handle("/article/delete/{articleID}", chain.Then(useTemplateHandler(ctx, handler.AdminArticleDeleteHandler))).Methods("GET")
|
||||||
router.Handle("/article/delete/{articleID}", chain.Then(useTemplateHandler(ctx, c.AdminArticleDeletePostHandler))).Methods("POST")
|
router.Handle("/article/delete/{articleID}", chain.Then(useTemplateHandler(ctx, handler.AdminArticleDeletePostHandler))).Methods("POST")
|
||||||
router.Handle("/article/{articleID}", chain.Then(useTemplateHandler(ctx, c.AdminPreviewArticleByIDHandler))).Methods("GET")
|
router.Handle("/article/{articleID}", chain.Then(useTemplateHandler(ctx, handler.AdminPreviewArticleByIDHandler))).Methods("GET")
|
||||||
|
|
||||||
//user
|
//user
|
||||||
router.Handle("/user/profile", chain.Then(useTemplateHandler(ctx, c.AdminProfileHandler))).Methods("GET")
|
router.Handle("/user/profile", chain.Then(useTemplateHandler(ctx, handler.AdminProfileHandler))).Methods("GET")
|
||||||
router.Handle("/user/profile", chain.Then(useTemplateHandler(ctx, c.AdminProfilePostHandler))).Methods("POST")
|
router.Handle("/user/profile", chain.Then(useTemplateHandler(ctx, handler.AdminProfilePostHandler))).Methods("POST")
|
||||||
router.Handle("/users", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminUsersHandler))).Methods("GET")
|
router.Handle("/users", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminUsersHandler))).Methods("GET")
|
||||||
router.Handle("/users/page/{page}", chain.Then(useTemplateHandler(ctx, c.AdminUsersHandler))).Methods("GET")
|
router.Handle("/users/page/{page}", chain.Then(useTemplateHandler(ctx, handler.AdminUsersHandler))).Methods("GET")
|
||||||
router.Handle("/user/new", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminUserNewHandler))).Methods("GET")
|
router.Handle("/user/new", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminUserNewHandler))).Methods("GET")
|
||||||
router.Handle("/user/new", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminUserNewPostHandler))).Methods("POST")
|
router.Handle("/user/new", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminUserNewPostHandler))).Methods("POST")
|
||||||
router.Handle("/user/edit/{userID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminUserEditHandler))).Methods("GET")
|
router.Handle("/user/edit/{userID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminUserEditHandler))).Methods("GET")
|
||||||
router.Handle("/user/edit/{userID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminUserEditPostHandler))).Methods("POST")
|
router.Handle("/user/edit/{userID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminUserEditPostHandler))).Methods("POST")
|
||||||
router.Handle("/user/delete/{userID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminUserDeleteHandler))).Methods("GET")
|
router.Handle("/user/delete/{userID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminUserDeleteHandler))).Methods("GET")
|
||||||
router.Handle("/user/delete/{userID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminUserDeletePostHandler))).Methods("POST")
|
router.Handle("/user/delete/{userID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminUserDeletePostHandler))).Methods("POST")
|
||||||
|
|
||||||
//user invites
|
//user invites
|
||||||
router.Handle("/user-invite/new", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminUserInviteNewHandler))).Methods("GET")
|
router.Handle("/user-invite/new", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminUserInviteNewHandler))).Methods("GET")
|
||||||
router.Handle("/user-invite/new", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminUserInviteNewPostHandler))).Methods("POST")
|
router.Handle("/user-invite/new", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminUserInviteNewPostHandler))).Methods("POST")
|
||||||
router.Handle("/user-invite/resend/{inviteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminUserInviteResendPostHandler))).Methods("POST")
|
router.Handle("/user-invite/resend/{inviteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminUserInviteResendPostHandler))).Methods("POST")
|
||||||
router.Handle("/user-invite/delete/{inviteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminUserInviteDeleteHandler))).Methods("GET")
|
router.Handle("/user-invite/delete/{inviteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminUserInviteDeleteHandler))).Methods("GET")
|
||||||
router.Handle("/user-invite/delete/{inviteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminUserInviteDeletePostHandler))).Methods("POST")
|
router.Handle("/user-invite/delete/{inviteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminUserInviteDeletePostHandler))).Methods("POST")
|
||||||
|
|
||||||
//site
|
//site
|
||||||
router.Handle("/sites", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminSitesHandler))).Methods("GET")
|
router.Handle("/sites", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminSitesHandler))).Methods("GET")
|
||||||
router.Handle("/site/page/{page}", chain.Then(useTemplateHandler(ctx, c.AdminSitesHandler))).Methods("GET")
|
router.Handle("/site/page/{page}", chain.Then(useTemplateHandler(ctx, handler.AdminSitesHandler))).Methods("GET")
|
||||||
router.Handle("/site/new", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminSiteNewHandler))).Methods("GET")
|
router.Handle("/site/new", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminSiteNewHandler))).Methods("GET")
|
||||||
router.Handle("/site/new", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminSiteNewPostHandler))).Methods("POST")
|
router.Handle("/site/new", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminSiteNewPostHandler))).Methods("POST")
|
||||||
router.Handle("/site/publish/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminSitePublishHandler))).Methods("GET")
|
router.Handle("/site/publish/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminSitePublishHandler))).Methods("GET")
|
||||||
router.Handle("/site/publish/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminSitePublishPostHandler))).Methods("POST")
|
router.Handle("/site/publish/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminSitePublishPostHandler))).Methods("POST")
|
||||||
router.Handle("/site/edit/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminSiteEditHandler))).Methods("GET")
|
router.Handle("/site/edit/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminSiteEditHandler))).Methods("GET")
|
||||||
router.Handle("/site/edit/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminSiteEditPostHandler))).Methods("POST")
|
router.Handle("/site/edit/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminSiteEditPostHandler))).Methods("POST")
|
||||||
router.Handle("/site/delete/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminSiteDeleteHandler))).Methods("GET")
|
router.Handle("/site/delete/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminSiteDeleteHandler))).Methods("GET")
|
||||||
router.Handle("/site/delete/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminSiteDeletePostHandler))).Methods("POST")
|
router.Handle("/site/delete/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminSiteDeletePostHandler))).Methods("POST")
|
||||||
router.Handle("/site/order/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminSiteOrderHandler))).Methods("POST")
|
router.Handle("/site/order/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminSiteOrderHandler))).Methods("POST")
|
||||||
router.Handle("/site/{siteID:[0-9]+}}", chain.Then(useTemplateHandler(ctx, c.AdminGetSiteHandler))).Methods("GET")
|
router.Handle("/site/{siteID:[0-9]+}}", chain.Then(useTemplateHandler(ctx, handler.AdminGetSiteHandler))).Methods("GET")
|
||||||
|
|
||||||
//article
|
//article
|
||||||
router.Handle("/categories", chain.Then(useTemplateHandler(ctx, c.AdminListCategoriesHandler))).Methods("GET")
|
router.Handle("/categories", chain.Then(useTemplateHandler(ctx, handler.AdminListCategoriesHandler))).Methods("GET")
|
||||||
router.Handle("/category/{categoryID:[0-9]+}}", chain.Then(useTemplateHandler(ctx, c.AdminGetCategoryHandler))).Methods("POST")
|
router.Handle("/category/{categoryID:[0-9]+}}", chain.Then(useTemplateHandler(ctx, handler.AdminGetCategoryHandler))).Methods("POST")
|
||||||
router.Handle("/category/new", chain.Then(useTemplateHandler(ctx, c.AdminCategoryNewHandler))).Methods("GET")
|
router.Handle("/category/new", chain.Then(useTemplateHandler(ctx, handler.AdminCategoryNewHandler))).Methods("GET")
|
||||||
router.Handle("/category/new", chain.Then(useTemplateHandler(ctx, c.AdminCategoryNewPostHandler))).Methods("POST")
|
router.Handle("/category/new", chain.Then(useTemplateHandler(ctx, handler.AdminCategoryNewPostHandler))).Methods("POST")
|
||||||
router.Handle("/category/edit/{categoryID}", chain.Then(useTemplateHandler(ctx, c.AdminCategoryEditHandler))).Methods("GET")
|
router.Handle("/category/edit/{categoryID}", chain.Then(useTemplateHandler(ctx, handler.AdminCategoryEditHandler))).Methods("GET")
|
||||||
router.Handle("/category/edit/{categoryID}", chain.Then(useTemplateHandler(ctx, c.AdminCategoryEditPostHandler))).Methods("POST")
|
router.Handle("/category/edit/{categoryID}", chain.Then(useTemplateHandler(ctx, handler.AdminCategoryEditPostHandler))).Methods("POST")
|
||||||
router.Handle("/category/delete/{categoryID}", chain.Then(useTemplateHandler(ctx, c.AdminCategoryDeleteHandler))).Methods("GET")
|
router.Handle("/category/delete/{categoryID}", chain.Then(useTemplateHandler(ctx, handler.AdminCategoryDeleteHandler))).Methods("GET")
|
||||||
router.Handle("/category/delete/{categoryID}", chain.Then(useTemplateHandler(ctx, c.AdminCategoryDeletePostHandler))).Methods("POST")
|
router.Handle("/category/delete/{categoryID}", chain.Then(useTemplateHandler(ctx, handler.AdminCategoryDeletePostHandler))).Methods("POST")
|
||||||
|
|
||||||
//file
|
//file
|
||||||
router.Handle("/files", chain.Then(useTemplateHandler(ctx, c.AdminListFilesHandler))).Methods("GET")
|
router.Handle("/files", chain.Then(useTemplateHandler(ctx, handler.AdminListFilesHandler))).Methods("GET")
|
||||||
router.Handle("/files/page/{page}", chain.Then(useTemplateHandler(ctx, c.AdminListFilesHandler))).Methods("GET")
|
router.Handle("/files/page/{page}", chain.Then(useTemplateHandler(ctx, handler.AdminListFilesHandler))).Methods("GET")
|
||||||
router.Handle("/file/upload", chain.Then(useTemplateHandler(ctx, c.AdminUploadFileHandler))).Methods("GET")
|
router.Handle("/file/upload", chain.Then(useTemplateHandler(ctx, handler.AdminUploadFileHandler))).Methods("GET")
|
||||||
router.Handle("/file/upload", chain.Then(useTemplateHandler(ctx, c.AdminUploadFilePostHandler))).Methods("POST")
|
router.Handle("/file/upload", chain.Then(useTemplateHandler(ctx, handler.AdminUploadFilePostHandler))).Methods("POST")
|
||||||
router.Handle("/file/toggleInline/{fileID}", chain.Then(useTemplateHandler(ctx, c.AdminToggleInlineFilePostHandler))).Methods("POST")
|
router.Handle("/file/toggleInline/{fileID}", chain.Then(useTemplateHandler(ctx, handler.AdminToggleInlineFilePostHandler))).Methods("POST")
|
||||||
router.Handle("/file/delete/{fileID}", chain.Then(useTemplateHandler(ctx, c.AdminUploadDeleteHandler))).Methods("GET")
|
router.Handle("/file/delete/{fileID}", chain.Then(useTemplateHandler(ctx, handler.AdminUploadDeleteHandler))).Methods("GET")
|
||||||
router.Handle("/file/delete/{fileID}", chain.Then(useTemplateHandler(ctx, c.AdminUploadDeletePostHandler))).Methods("POST")
|
router.Handle("/file/delete/{fileID}", chain.Then(useTemplateHandler(ctx, handler.AdminUploadDeletePostHandler))).Methods("POST")
|
||||||
|
|
||||||
router.Handle("/logout", chain.Then(useTemplateHandler(ctx, c.LogoutHandler))).Methods("GET")
|
router.Handle("/logout", chain.Then(useTemplateHandler(ctx, handler.LogoutHandler))).Methods("GET")
|
||||||
|
|
||||||
router.Handle("/json/session/keep-alive", chain.Then(useJSONHandler(ctx, c.KeepAliveSessionHandler))).Methods("POST")
|
router.Handle("/json/session/keep-alive", chain.Then(useJSONHandler(ctx, handler.KeepAliveSessionHandler))).Methods("POST")
|
||||||
router.Handle("/json/file/upload", chain.Then(useJSONHandler(ctx, c.AdminUploadJSONFilePostHandler))).Methods("POST")
|
router.Handle("/json/file/upload", chain.Then(useJSONHandler(ctx, handler.AdminUploadJSONFilePostHandler))).Methods("POST")
|
||||||
}
|
}
|
||||||
|
|
||||||
func publicRoutes(ctx *m.AppContext, router *mux.Router, chain alice.Chain) {
|
func publicRoutes(ctx *m.AppContext, router *mux.Router, chain alice.Chain) {
|
||||||
fh := c.FileHandler{
|
fh := handler.FileHandler{
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
}
|
}
|
||||||
|
|
||||||
router.Handle("/", chain.Then(useTemplateHandler(ctx, c.ListArticlesHandler))).Methods("GET")
|
router.Handle("/", chain.Then(useTemplateHandler(ctx, handler.ListArticlesHandler))).Methods("GET")
|
||||||
router.Handle("/articles/category/{categorySlug}", chain.Then(useTemplateHandler(ctx, c.ListArticlesCategoryHandler))).Methods("GET")
|
router.Handle("/articles/category/{categorySlug}", chain.Then(useTemplateHandler(ctx, handler.ListArticlesCategoryHandler))).Methods("GET")
|
||||||
router.Handle("/articles/category/{categorySlug}/{page}", chain.Then(useTemplateHandler(ctx, c.ListArticlesCategoryHandler))).Methods("GET")
|
router.Handle("/articles/category/{categorySlug}/{page}", chain.Then(useTemplateHandler(ctx, handler.ListArticlesCategoryHandler))).Methods("GET")
|
||||||
router.Handle("/index", chain.Then(useTemplateHandler(ctx, c.IndexArticlesHandler))).Methods("GET")
|
router.Handle("/index", chain.Then(useTemplateHandler(ctx, handler.IndexArticlesHandler))).Methods("GET")
|
||||||
router.Handle("/index/category/{categorySlug}", chain.Then(useTemplateHandler(ctx, c.IndexArticlesCategoryHandler))).Methods("GET")
|
router.Handle("/index/category/{categorySlug}", chain.Then(useTemplateHandler(ctx, handler.IndexArticlesCategoryHandler))).Methods("GET")
|
||||||
|
|
||||||
router.Handle("/articles/page/{page}", chain.Then(useTemplateHandler(ctx, c.ListArticlesHandler))).Methods("GET")
|
router.Handle("/articles/page/{page}", chain.Then(useTemplateHandler(ctx, handler.ListArticlesHandler))).Methods("GET")
|
||||||
router.Handle("/article/{year}/{month}/{slug}", chain.Then(useTemplateHandler(ctx, c.GetArticleHandler))).Methods("GET")
|
router.Handle("/article/{year}/{month}/{slug}", chain.Then(useTemplateHandler(ctx, handler.GetArticleHandler))).Methods("GET")
|
||||||
router.Handle("/article/by-id/{articleID}", chain.Then(useTemplateHandler(ctx, c.GetArticleByIDHandler))).Methods("GET")
|
router.Handle("/article/by-id/{articleID}", chain.Then(useTemplateHandler(ctx, handler.GetArticleByIDHandler))).Methods("GET")
|
||||||
|
|
||||||
router.Handle("/rss.xml", chain.Then(useXMLHandler(ctx, c.RSSFeed))).Methods("GET")
|
router.Handle("/rss.xml", chain.Then(useXMLHandler(ctx, handler.RSSFeed))).Methods("GET")
|
||||||
|
|
||||||
router.Handle("/site/{site}", chain.Then(useTemplateHandler(ctx, c.GetSiteHandler))).Methods("GET")
|
router.Handle("/site/{site}", chain.Then(useTemplateHandler(ctx, handler.GetSiteHandler))).Methods("GET")
|
||||||
|
|
||||||
router.Handle("/file/{uniquename}", chain.ThenFunc(fh.FileGetHandler)).Methods("GET")
|
router.Handle("/file/{uniquename}", chain.ThenFunc(fh.FileGetHandler)).Methods("GET")
|
||||||
|
|
||||||
router.Handle("/admin", chain.Then(useTemplateHandler(ctx, c.LoginHandler))).Methods("GET")
|
router.Handle("/admin", chain.Then(useTemplateHandler(ctx, handler.LoginHandler))).Methods("GET")
|
||||||
router.Handle("/admin", chain.Then(useTemplateHandler(ctx, c.LoginPostHandler))).Methods("POST")
|
router.Handle("/admin", chain.Then(useTemplateHandler(ctx, handler.LoginPostHandler))).Methods("POST")
|
||||||
|
|
||||||
router.Handle("/admin/forgot-password", chain.Then(useTemplateHandler(ctx, c.ForgotPasswordHandler))).Methods("GET")
|
router.Handle("/admin/forgot-password", chain.Then(useTemplateHandler(ctx, handler.ForgotPasswordHandler))).Methods("GET")
|
||||||
router.Handle("/admin/forgot-password", chain.Then(useTemplateHandler(ctx, c.ForgotPasswordPostHandler))).Methods("POST")
|
router.Handle("/admin/forgot-password", chain.Then(useTemplateHandler(ctx, handler.ForgotPasswordPostHandler))).Methods("POST")
|
||||||
|
|
||||||
router.Handle("/admin/reset-password/{hash}", chain.Then(useTemplateHandler(ctx, c.ResetPasswordHandler))).Methods("GET")
|
router.Handle("/admin/reset-password/{hash}", chain.Then(useTemplateHandler(ctx, handler.ResetPasswordHandler))).Methods("GET")
|
||||||
router.Handle("/admin/reset-password/{hash}", chain.Then(useTemplateHandler(ctx, c.ResetPasswordPostHandler))).Methods("POST")
|
router.Handle("/admin/reset-password/{hash}", chain.Then(useTemplateHandler(ctx, handler.ResetPasswordPostHandler))).Methods("POST")
|
||||||
|
|
||||||
router.Handle("/admin/activate-account/{hash}", chain.Then(useTemplateHandler(ctx, c.ActivateAccountHandler))).Methods("GET")
|
router.Handle("/admin/activate-account/{hash}", chain.Then(useTemplateHandler(ctx, handler.ActivateAccountHandler))).Methods("GET")
|
||||||
router.Handle("/admin/activate-account/{hash}", chain.Then(useTemplateHandler(ctx, c.ActivateAccountPostHandler))).Methods("POST")
|
router.Handle("/admin/activate-account/{hash}", chain.Then(useTemplateHandler(ctx, handler.ActivateAccountPostHandler))).Methods("POST")
|
||||||
}
|
}
|
||||||
|
|
||||||
func useTemplateHandler(ctx *m.AppContext, handler m.Handler) m.TemplateHandler {
|
func useTemplateHandler(ctx *m.AppContext, handler m.Handler) m.TemplateHandler {
|
||||||
|
|
|
@ -15,8 +15,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/cfg"
|
"git.hoogi.eu/snafu/cfg"
|
||||||
"git.hoogi.eu/snafu/go-blog/components/logger"
|
"git.hoogi.eu/snafu/go-blog/crypt"
|
||||||
"git.hoogi.eu/snafu/go-blog/utils"
|
"git.hoogi.eu/snafu/go-blog/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LoginMethod int
|
type LoginMethod int
|
||||||
|
@ -286,11 +286,7 @@ func (cfg *Settings) GenerateCSRF() (bool, error) {
|
||||||
|
|
||||||
if _, err := os.Stat(csrfTokenFilename); os.IsNotExist(err) {
|
if _, err := os.Stat(csrfTokenFilename); os.IsNotExist(err) {
|
||||||
//create a random csrf token
|
//create a random csrf token
|
||||||
r := utils.RandomSource{
|
b = crypt.AlphaUpperLowerNumericSpecial.RandomSequence(32)
|
||||||
CharsToGen: utils.AlphaUpperLowerNumericSpecial,
|
|
||||||
}
|
|
||||||
|
|
||||||
b = r.RandomSequence(32)
|
|
||||||
|
|
||||||
err := ioutil.WriteFile(csrfTokenFilename, b, 0640)
|
err := ioutil.WriteFile(csrfTokenFilename, b, 0640)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
package slug
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
var multipleDashes = regexp.MustCompile(`[-]{2,}`)
|
||||||
|
|
||||||
|
//CreateURLSafeSlug creates a URL safe slug to use in URL
|
||||||
|
func CreateURLSafeSlug(input string, suffix int) string {
|
||||||
|
input = strings.Replace(input, "-", "", -1)
|
||||||
|
|
||||||
|
input = strings.Map(func(r rune) rune {
|
||||||
|
switch {
|
||||||
|
case r == ' ':
|
||||||
|
return '-'
|
||||||
|
case unicode.IsLetter(r), unicode.IsDigit(r):
|
||||||
|
return r
|
||||||
|
default:
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}, strings.ToLower(strings.TrimSpace(input)))
|
||||||
|
|
||||||
|
input = strings.Trim(input, "-")
|
||||||
|
|
||||||
|
input = multipleDashes.ReplaceAllString(input, "-")
|
||||||
|
|
||||||
|
if suffix > 0 {
|
||||||
|
input += strconv.Itoa(suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
|
||||||
|
func substitute(input string, subs map[rune]string) string {
|
||||||
|
var b bytes.Buffer
|
||||||
|
|
||||||
|
for _, c := range input {
|
||||||
|
if _, ok := subs[c]; ok {
|
||||||
|
b.WriteString(subs[c])
|
||||||
|
} else {
|
||||||
|
b.WriteRune(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b.String()
|
||||||
|
}
|
|
@ -2,12 +2,12 @@
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package utils_test
|
package slug_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.hoogi.eu/snafu/go-blog/utils"
|
"git.hoogi.eu/snafu/go-blog/slug"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCreateURLSafeSlug(t *testing.T) {
|
func TestCreateURLSafeSlug(t *testing.T) {
|
||||||
|
@ -26,7 +26,7 @@ func TestCreateURLSafeSlug(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < len(testcases)-1; i = i + 2 {
|
for i := 0; i < len(testcases)-1; i = i + 2 {
|
||||||
actual := utils.CreateURLSafeSlug(testcases[i], 0)
|
actual := slug.CreateURLSafeSlug(testcases[i], 0)
|
||||||
|
|
||||||
if actual != testcases[i+1] {
|
if actual != testcases[i+1] {
|
||||||
t.Errorf("Got: '%s'; want '%s'", actual, testcases[i+1])
|
t.Errorf("Got: '%s'; want '%s'", actual, testcases[i+1])
|
136
utils/strings.go
136
utils/strings.go
|
@ -1,136 +0,0 @@
|
||||||
// Copyright 2018 Lars Hoogestraat
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
)
|
|
||||||
|
|
||||||
//AppendString uses byte buffer to append multiple strings
|
|
||||||
func AppendString(s ...string) string {
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
for _, value := range s {
|
|
||||||
buffer.WriteString(value)
|
|
||||||
}
|
|
||||||
return buffer.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
//AppendBytes uses byte buffer to append multiple byte arrays
|
|
||||||
func AppendBytes(s ...[]byte) []byte {
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
for _, value := range s {
|
|
||||||
buffer.Write(value)
|
|
||||||
}
|
|
||||||
return buffer.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
//TrimmedStringIsEmpty trims spaces returns true if length is 0
|
|
||||||
func TrimmedStringIsEmpty(s string) bool {
|
|
||||||
return len(strings.TrimSpace(s)) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
//IsOneOfStringsEmpty checks if one of the given strings is empty
|
|
||||||
func IsOneOfStringsEmpty(s ...string) bool {
|
|
||||||
for _, value := range s {
|
|
||||||
if len(value) == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
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.TrimFunc(s, unicode.IsSpace)
|
|
||||||
s = removeControlCharacters(s)
|
|
||||||
s = substitute(s, filenameSubs)
|
|
||||||
s = strings.TrimLeftFunc(s, isDot)
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
var slugSubs = map[rune]string{
|
|
||||||
'&': "",
|
|
||||||
'$': "",
|
|
||||||
'+': "",
|
|
||||||
',': "",
|
|
||||||
'/': "",
|
|
||||||
':': "",
|
|
||||||
';': "",
|
|
||||||
'=': "",
|
|
||||||
'?': "",
|
|
||||||
'@': "",
|
|
||||||
'#': "",
|
|
||||||
'!': "",
|
|
||||||
'\'': "",
|
|
||||||
'(': "",
|
|
||||||
')': "",
|
|
||||||
'*': "",
|
|
||||||
'%': "",
|
|
||||||
}
|
|
||||||
|
|
||||||
var multipleDashes = regexp.MustCompile(`[-]{2,}`)
|
|
||||||
|
|
||||||
//CreateURLSafeSlug creates a url safe slug to use in urls
|
|
||||||
func CreateURLSafeSlug(input string, suffix int) string {
|
|
||||||
input = removeControlCharacters(input)
|
|
||||||
input = substitute(input, slugSubs)
|
|
||||||
input = strings.TrimSpace(input)
|
|
||||||
|
|
||||||
input = strings.Replace(input, " ", "-", -1)
|
|
||||||
|
|
||||||
input = strings.ToLower(input)
|
|
||||||
|
|
||||||
input = multipleDashes.ReplaceAllString(input, "-")
|
|
||||||
|
|
||||||
if suffix > 0 {
|
|
||||||
input += strconv.Itoa(suffix)
|
|
||||||
}
|
|
||||||
|
|
||||||
return input
|
|
||||||
}
|
|
||||||
|
|
||||||
func substitute(input string, subs map[rune]string) string {
|
|
||||||
var b bytes.Buffer
|
|
||||||
|
|
||||||
for _, c := range input {
|
|
||||||
if _, ok := subs[c]; ok {
|
|
||||||
b.WriteString(subs[c])
|
|
||||||
} else {
|
|
||||||
b.WriteRune(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return b.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeControlCharacters(input string) string {
|
|
||||||
var b bytes.Buffer
|
|
||||||
for _, c := range input {
|
|
||||||
if c > 31 {
|
|
||||||
b.WriteRune(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return b.String()
|
|
||||||
}
|
|
Loading…
Reference in New Issue