Compare commits
49 Commits
123071ab1b
...
v0.13.0
Author | SHA1 | Date | |
---|---|---|---|
d62d71b5d1 | |||
07e1983fcc | |||
878f57af08 | |||
d86b9027bf | |||
31484dd44a | |||
3b4e1e01d2 | |||
03425fc2c6 | |||
0b9bae3bf8 | |||
8ed0676e51 | |||
d7cbb34814 | |||
23f0c6f4a4 | |||
cb322c57f9 | |||
b36e0ea503 | |||
bc4d8fa37e | |||
d2b21e7405 | |||
e3c192359f | |||
46532e4c85 | |||
c722135a56 | |||
887fa863bc | |||
74d71cfb6a | |||
ca7e7cddd3 | |||
94431a2aa9 | |||
5b1f20c5bc | |||
d0c566f8df | |||
5e586aa49a | |||
66b2743d3d | |||
3723b2b5e6 | |||
ce788bfd50 | |||
230a6278cc | |||
42d6e0c198 | |||
e1af2979af | |||
f6dedc6f10 | |||
cdf0a49550 | |||
c3c0650210 | |||
d077f700d8 | |||
ec752b1c66 | |||
46aef4f12f | |||
1b29e328cf | |||
e50cb819f3 | |||
c32e38ca10 | |||
d7c8c7a43a | |||
1cd3edc90c | |||
0e768c9f61 | |||
1fcd775cc5 | |||
203a1ed147 | |||
ef1914ee5c | |||
084b101e31 | |||
b2db128aa9 | |||
081e880fb6 |
@ -4,22 +4,23 @@ tmp_dir = "tmp"
|
||||
|
||||
[build]
|
||||
args_bin = [
|
||||
"-aes tmp/cpolis.aes",
|
||||
"-articles tmp/articles",
|
||||
"-banner-width 512",
|
||||
"-config tmp/config.toml",
|
||||
"-desc 'Freiheit, Gleichheit, Brüderlichkeit, Toleranz und Humanität'",
|
||||
"-domain localhost",
|
||||
"-feed tmp/cpolis.atom",
|
||||
"-firebase tmp/firebase.json",
|
||||
"-key tmp/key.gob",
|
||||
"-gob tmp/cpolis.gob",
|
||||
"-img-width 256",
|
||||
"-link https://distrikt-ni-st.de",
|
||||
"-log tmp/cpolis.log",
|
||||
"-pdfs tmp/pdfs",
|
||||
"-pics tmp/pics",
|
||||
"-port 8080",
|
||||
"-rss tmp/orientexpress_alle.rss",
|
||||
"-title 'Freimaurer Distrikt Niedersachsen und Sachsen-Anhalt'",
|
||||
"-web web",
|
||||
"-width 1024",
|
||||
]
|
||||
bin = "./tmp/main"
|
||||
cmd = "go build -o ./tmp/main ./cmd/main.go"
|
||||
|
@ -18,7 +18,7 @@ func GenerateAtomFeed(c *Config, db *DB) (*string, error) {
|
||||
|
||||
feed.Generator = atom.NewGenerator("cpolis")
|
||||
feed.Generator.URI = "https://git.streifling.com/jason/cpolis"
|
||||
feed.Generator.Version = "0.13"
|
||||
feed.Generator.Version = c.Version
|
||||
|
||||
articles, err := db.GetCertainArticles("published", true)
|
||||
if err != nil {
|
||||
@ -39,7 +39,7 @@ func GenerateAtomFeed(c *Config, db *DB) (*string, error) {
|
||||
entry.Links[linkID].Rel = "enclosure"
|
||||
entry.Links[linkID].Type = "image/webp"
|
||||
|
||||
user, err := db.GetUser(article.AuthorID)
|
||||
user, err := db.GetUser(c, article.AuthorID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting user user info for RSS feed: %v", err)
|
||||
}
|
||||
|
@ -12,42 +12,48 @@ import (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
ArticleDir string
|
||||
ConfigFile string
|
||||
DBName string
|
||||
Description string
|
||||
Domain string
|
||||
AtomFeed string
|
||||
FirebaseKey string
|
||||
KeyFile string
|
||||
Link string
|
||||
LogFile string
|
||||
PDFDir string
|
||||
PicsDir string
|
||||
Port string
|
||||
RSSFile string
|
||||
Title string
|
||||
WebDir string
|
||||
MaxImgHeight int
|
||||
MaxImgWidth int
|
||||
AESKeyFile string
|
||||
ArticleDir string
|
||||
ConfigFile string
|
||||
DBName string
|
||||
Description string
|
||||
Domain string
|
||||
AtomFeed string
|
||||
FirebaseKey string
|
||||
GOBKeyFile string
|
||||
Link string
|
||||
LogFile string
|
||||
PDFDir string
|
||||
PicsDir string
|
||||
Port string
|
||||
Title string
|
||||
Version string
|
||||
WebDir string
|
||||
MaxBannerHeight int
|
||||
MaxBannerWidth int
|
||||
MaxImgHeight int
|
||||
MaxImgWidth int
|
||||
}
|
||||
|
||||
func newConfig() *Config {
|
||||
return &Config{
|
||||
ArticleDir: "/var/www/cpolis/articles",
|
||||
ConfigFile: "/etc/cpolis/config.toml",
|
||||
DBName: "cpolis",
|
||||
AtomFeed: "/var/www/cpolis/cpolis.atom",
|
||||
FirebaseKey: "/var/www/cpolis/serviceAccountKey.json",
|
||||
KeyFile: "/var/www/cpolis/cpolis.key",
|
||||
LogFile: "/var/log/cpolis.log",
|
||||
MaxImgHeight: 1080,
|
||||
MaxImgWidth: 1920,
|
||||
PDFDir: "/var/www/cpolis/pdfs",
|
||||
PicsDir: "/var/www/cpolis/pics",
|
||||
Port: ":8080",
|
||||
RSSFile: "/var/www/cpolis/cpolis.rss",
|
||||
WebDir: "/var/www/cpolis/web",
|
||||
AESKeyFile: "/var/www/cpolis/aes.key",
|
||||
ArticleDir: "/var/www/cpolis/articles",
|
||||
AtomFeed: "/var/www/cpolis/cpolis.atom",
|
||||
ConfigFile: "/etc/cpolis/config.toml",
|
||||
DBName: "cpolis",
|
||||
FirebaseKey: "/var/www/cpolis/serviceAccountKey.json",
|
||||
GOBKeyFile: "/var/www/cpolis/gob.key",
|
||||
LogFile: "/var/log/cpolis.log",
|
||||
MaxBannerHeight: 1080,
|
||||
MaxBannerWidth: 1920,
|
||||
MaxImgHeight: 1080,
|
||||
MaxImgWidth: 1920,
|
||||
PDFDir: "/var/www/cpolis/pdfs",
|
||||
PicsDir: "/var/www/cpolis/pics",
|
||||
Port: ":8080",
|
||||
Version: "v0.13.0",
|
||||
WebDir: "/var/www/cpolis/web",
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,6 +108,7 @@ func (c *Config) handleCliArgs() error {
|
||||
var port int
|
||||
var err error
|
||||
|
||||
flag.StringVar(&c.AESKeyFile, "aes", c.AESKeyFile, "aes key file")
|
||||
flag.StringVar(&c.ArticleDir, "articles", c.ArticleDir, "articles directory")
|
||||
flag.StringVar(&c.AtomFeed, "feed", c.AtomFeed, "atom feed file")
|
||||
flag.StringVar(&c.ConfigFile, "config", c.ConfigFile, "config file")
|
||||
@ -109,16 +116,17 @@ func (c *Config) handleCliArgs() error {
|
||||
flag.StringVar(&c.Description, "desc", c.Description, "channel description")
|
||||
flag.StringVar(&c.Domain, "domain", c.Domain, "domain name")
|
||||
flag.StringVar(&c.FirebaseKey, "firebase", c.FirebaseKey, "Firebase service account key file")
|
||||
flag.StringVar(&c.KeyFile, "key", c.KeyFile, "key file")
|
||||
flag.StringVar(&c.GOBKeyFile, "gob", c.GOBKeyFile, "gob key file")
|
||||
flag.StringVar(&c.Link, "link", c.Link, "channel Link")
|
||||
flag.StringVar(&c.LogFile, "log", c.LogFile, "log file")
|
||||
flag.StringVar(&c.PDFDir, "pdfs", c.PDFDir, "pdf directory")
|
||||
flag.StringVar(&c.PicsDir, "pics", c.PicsDir, "pictures directory")
|
||||
flag.StringVar(&c.RSSFile, "rss", c.RSSFile, "RSS file")
|
||||
flag.StringVar(&c.Title, "title", c.Title, "channel title")
|
||||
flag.StringVar(&c.WebDir, "web", c.WebDir, "web directory")
|
||||
flag.IntVar(&c.MaxImgHeight, "height", c.MaxImgHeight, "maximum image height")
|
||||
flag.IntVar(&c.MaxImgWidth, "width", c.MaxImgWidth, "maximum image width")
|
||||
flag.IntVar(&c.MaxBannerHeight, "banner-height", c.MaxBannerHeight, "maximum banner height")
|
||||
flag.IntVar(&c.MaxBannerWidth, "banner-width", c.MaxBannerWidth, "maximum banner width")
|
||||
flag.IntVar(&c.MaxImgHeight, "img-height", c.MaxImgHeight, "maximum image height")
|
||||
flag.IntVar(&c.MaxImgWidth, "img-width", c.MaxImgWidth, "maximum image width")
|
||||
flag.IntVar(&port, "port", port, "port")
|
||||
flag.Parse()
|
||||
|
||||
@ -147,6 +155,14 @@ func (c *Config) setupConfig(cliConfig *Config) error {
|
||||
var err error
|
||||
defaultConfig := newConfig()
|
||||
|
||||
if cliConfig.AESKeyFile != defaultConfig.AESKeyFile {
|
||||
c.AESKeyFile = cliConfig.AESKeyFile
|
||||
}
|
||||
c.AESKeyFile, err = mkFile(c.AESKeyFile, 0600, 0700)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error setting up file: %v", err)
|
||||
}
|
||||
|
||||
if cliConfig.ArticleDir != defaultConfig.ArticleDir {
|
||||
c.ArticleDir = cliConfig.ArticleDir
|
||||
}
|
||||
@ -187,10 +203,10 @@ func (c *Config) setupConfig(cliConfig *Config) error {
|
||||
return fmt.Errorf("error setting up file: %v", err)
|
||||
}
|
||||
|
||||
if cliConfig.KeyFile != defaultConfig.KeyFile {
|
||||
c.KeyFile = cliConfig.KeyFile
|
||||
if cliConfig.GOBKeyFile != defaultConfig.GOBKeyFile {
|
||||
c.GOBKeyFile = cliConfig.GOBKeyFile
|
||||
}
|
||||
c.KeyFile, err = mkFile(c.KeyFile, 0600, 0700)
|
||||
c.GOBKeyFile, err = mkFile(c.GOBKeyFile, 0600, 0700)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error setting up file: %v", err)
|
||||
}
|
||||
@ -207,6 +223,14 @@ func (c *Config) setupConfig(cliConfig *Config) error {
|
||||
return fmt.Errorf("error setting up file: %v", err)
|
||||
}
|
||||
|
||||
if cliConfig.MaxBannerHeight != defaultConfig.MaxBannerHeight {
|
||||
c.MaxBannerHeight = cliConfig.MaxBannerHeight
|
||||
}
|
||||
|
||||
if cliConfig.MaxBannerWidth != defaultConfig.MaxBannerWidth {
|
||||
c.MaxBannerWidth = cliConfig.MaxBannerWidth
|
||||
}
|
||||
|
||||
if cliConfig.MaxImgHeight != defaultConfig.MaxImgHeight {
|
||||
c.MaxImgHeight = cliConfig.MaxImgHeight
|
||||
}
|
||||
@ -235,14 +259,6 @@ func (c *Config) setupConfig(cliConfig *Config) error {
|
||||
c.Port = cliConfig.Port
|
||||
}
|
||||
|
||||
if cliConfig.RSSFile != defaultConfig.RSSFile {
|
||||
c.RSSFile = cliConfig.RSSFile
|
||||
}
|
||||
c.RSSFile, err = mkFile(c.RSSFile, 0600, 0700)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error setting up file: %v", err)
|
||||
}
|
||||
|
||||
if cliConfig.Title != defaultConfig.Title {
|
||||
c.Title = cliConfig.Title
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package backend
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"image"
|
||||
"io"
|
||||
@ -13,7 +14,7 @@ import (
|
||||
|
||||
var ErrUnsupportedFormat error = image.ErrFormat // used internally by imaging
|
||||
|
||||
func SaveImage(c *Config, src io.Reader) (string, error) {
|
||||
func SaveImage(src io.Reader, maxHeight, maxWidth int, path string) (string, error) {
|
||||
img, err := imaging.Decode(src, imaging.AutoOrientation(true))
|
||||
if err != nil {
|
||||
if err == ErrUnsupportedFormat {
|
||||
@ -22,15 +23,15 @@ func SaveImage(c *Config, src io.Reader) (string, error) {
|
||||
return "", fmt.Errorf("error decoding image: %v", err)
|
||||
}
|
||||
|
||||
if img.Bounds().Dy() > c.MaxImgHeight {
|
||||
img = imaging.Resize(img, 0, c.MaxImgHeight, imaging.Lanczos)
|
||||
if img.Bounds().Dy() > maxHeight {
|
||||
img = imaging.Resize(img, 0, maxHeight, imaging.Lanczos)
|
||||
}
|
||||
if img.Bounds().Dx() > c.MaxImgWidth {
|
||||
img = imaging.Resize(img, c.MaxImgWidth, 0, imaging.Lanczos)
|
||||
if img.Bounds().Dx() > maxWidth {
|
||||
img = imaging.Resize(img, maxWidth, 0, imaging.Lanczos)
|
||||
}
|
||||
|
||||
filename := fmt.Sprint(uuid.New(), ".webp")
|
||||
file, err := os.Create(c.PicsDir + "/" + filename)
|
||||
file, err := os.Create(path + filename)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error creating new image file: %v", err)
|
||||
}
|
||||
@ -42,3 +43,20 @@ func SaveImage(c *Config, src io.Reader) (string, error) {
|
||||
|
||||
return filename, nil
|
||||
}
|
||||
|
||||
func ServeBase64Image(c *Config, filename string) (string, error) {
|
||||
file := c.PicsDir + "/" + filename
|
||||
|
||||
img, err := os.Open(file)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error opening file %v: %v", file, err)
|
||||
}
|
||||
defer img.Close()
|
||||
|
||||
imgBytes, err := io.ReadAll(img)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error turning %v into bytes: %v", file, err)
|
||||
}
|
||||
|
||||
return base64.StdEncoding.EncodeToString(imgBytes), nil
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ func GenerateRSS(c *Config, db *DB) (*string, error) {
|
||||
tagNames = append(tagNames, "autogenerated")
|
||||
}
|
||||
|
||||
user, err := db.GetUser(article.AuthorID)
|
||||
user, err := db.GetUser(c, article.AuthorID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting user user info for RSS feed: %v", err)
|
||||
}
|
||||
|
@ -2,9 +2,16 @@ package backend
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"database/sql"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
@ -18,24 +25,135 @@ const (
|
||||
)
|
||||
|
||||
type User struct {
|
||||
UserName string
|
||||
FirstName string
|
||||
LastName string
|
||||
ID int64
|
||||
Role int
|
||||
UserName string
|
||||
FirstName string
|
||||
LastName string
|
||||
Email string
|
||||
ProfilePicLink string
|
||||
ID int64
|
||||
Role int
|
||||
}
|
||||
|
||||
func (db *DB) AddUser(u *User, pass string) (int64, error) {
|
||||
func readKey(filename string) ([]byte, error) {
|
||||
key, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading from aes key file: %v", err)
|
||||
}
|
||||
|
||||
if len(key) != 44 {
|
||||
return nil, errors.New("key is not 32 bytes long")
|
||||
}
|
||||
|
||||
key, err = base64.StdEncoding.DecodeString(string(key))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error base64 decoding key: %v", err)
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
func key(c *Config) ([]byte, error) {
|
||||
key, err := readKey(c.AESKeyFile)
|
||||
if err != nil {
|
||||
key = make([]byte, 32)
|
||||
if _, err := rand.Read(key); err != nil {
|
||||
return nil, fmt.Errorf("error generating random key: %v", err)
|
||||
}
|
||||
|
||||
fileKey := make([]byte, 44)
|
||||
base64.StdEncoding.Encode(fileKey, key)
|
||||
if err = os.WriteFile(c.AESKeyFile, fileKey, 0600); err != nil {
|
||||
return nil, fmt.Errorf("error writing key to file: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
func aesEncrypt(c *Config, plaintext string) (string, error) {
|
||||
key, err := key(c)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error retrieving key: %v", err)
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error creating cipher block: %v", err)
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error creating new gcm: %v", err)
|
||||
}
|
||||
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return "", fmt.Errorf("error creating nonce: %v", err)
|
||||
}
|
||||
|
||||
cipherText := gcm.Seal(nonce, nonce, []byte(plaintext), nil)
|
||||
return base64.StdEncoding.EncodeToString(cipherText), nil
|
||||
}
|
||||
|
||||
func aesDecrypt(c *Config, ciphertext string) (string, error) {
|
||||
key, err := key(c)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error retrieving key: %v", err)
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error creating cipher block: %v", err)
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error creating new gcm: %v", err)
|
||||
}
|
||||
|
||||
data, err := base64.StdEncoding.DecodeString(ciphertext)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error base64 decoding ciphertext: %v", err)
|
||||
}
|
||||
|
||||
nonceSize := gcm.NonceSize()
|
||||
nonce, cipherText := data[:nonceSize], data[nonceSize:]
|
||||
|
||||
plaintext, err := gcm.Open(nil, nonce, cipherText, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error aes decoding ciphertext: %v", err)
|
||||
}
|
||||
|
||||
return string(plaintext), nil
|
||||
}
|
||||
|
||||
func (db *DB) AddUser(c *Config, u *User, pass string) (int64, error) {
|
||||
hashedPass, err := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error creating password hash: %v", err)
|
||||
}
|
||||
|
||||
aesFirstName, err := aesEncrypt(c, u.FirstName)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error encrypting first name: %v", err)
|
||||
}
|
||||
|
||||
aesLastName, err := aesEncrypt(c, u.LastName)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error encrypting last name: %v", err)
|
||||
}
|
||||
|
||||
aesEmail, err := aesEncrypt(c, u.Email)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error encrypting email: %v", err)
|
||||
}
|
||||
|
||||
query := `
|
||||
INSERT INTO users (username, password, first_name, last_name, role)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
INSERT INTO users (username, password, first_name, last_name, email, role)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
`
|
||||
result, err := db.Exec(query, u.UserName, string(hashedPass), u.FirstName, u.LastName, u.Role)
|
||||
|
||||
result, err := db.Exec(query, u.UserName, string(hashedPass), aesFirstName, aesLastName, aesEmail, u.Role)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error inserting new user %v into DB: %v", u.UserName, err)
|
||||
}
|
||||
@ -129,34 +247,44 @@ func (tx *Tx) ChangePassword(id int64, oldPass, newPass string) error {
|
||||
}
|
||||
|
||||
// TODO: No need for ID field in general
|
||||
func (db *DB) GetUser(id int64) (*User, error) {
|
||||
func (db *DB) GetUser(c *Config, id int64) (*User, error) {
|
||||
var aesFirstName, aesLastName, aesEmail string
|
||||
var err error
|
||||
|
||||
user := new(User)
|
||||
query := `
|
||||
SELECT id, username, first_name, last_name, role
|
||||
SELECT id, username, first_name, last_name, email, role
|
||||
FROM users
|
||||
WHERE id = ?
|
||||
`
|
||||
|
||||
row := db.QueryRow(query, id)
|
||||
if err := row.Scan(&user.ID, &user.UserName, &user.FirstName,
|
||||
&user.LastName, &user.Role); err != nil {
|
||||
if err := row.Scan(&user.ID, &user.UserName, &aesFirstName, &aesLastName, &aesEmail, &user.Role); err != nil {
|
||||
return nil, fmt.Errorf("error reading user information: %v", err)
|
||||
}
|
||||
|
||||
user.FirstName, err = aesDecrypt(c, aesFirstName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decrypting first name: %v", err)
|
||||
}
|
||||
|
||||
user.LastName, err = aesDecrypt(c, aesLastName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decrypting last name: %v", err)
|
||||
}
|
||||
|
||||
user.Email, err = aesDecrypt(c, aesEmail)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decrypting email: %v", err)
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (db *DB) UpdateOwnUserAttributes(id int64, user, first, last, oldPass, newPass, newPass2 string) error {
|
||||
passwordEmpty := true
|
||||
if len(newPass) > 0 || len(newPass2) > 0 {
|
||||
if newPass != newPass2 {
|
||||
return fmt.Errorf("error: passwords do not match")
|
||||
}
|
||||
passwordEmpty = false
|
||||
}
|
||||
|
||||
tx := new(Tx)
|
||||
func (db *DB) UpdateOwnUserAttributes(c *Config, id int64, userName, firstName, lastName, email, oldPass, newPass string) error {
|
||||
var err error
|
||||
tx := new(Tx)
|
||||
passwordEmpty := len(newPass) > 0
|
||||
|
||||
for i := 0; i < TxMaxRetries; i++ {
|
||||
err := func() error {
|
||||
@ -174,10 +302,35 @@ func (db *DB) UpdateOwnUserAttributes(id int64, user, first, last, oldPass, newP
|
||||
}
|
||||
}
|
||||
|
||||
aesFirstName, err := aesEncrypt(c, firstName)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
|
||||
}
|
||||
return fmt.Errorf("error encrypting first name: %v", err)
|
||||
}
|
||||
|
||||
aesLastName, err := aesEncrypt(c, lastName)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
|
||||
}
|
||||
return fmt.Errorf("error encrypting last name: %v", err)
|
||||
}
|
||||
|
||||
aesEmail, err := aesEncrypt(c, email)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
|
||||
}
|
||||
return fmt.Errorf("error encrypting email: %v", err)
|
||||
}
|
||||
|
||||
if err = tx.UpdateAttributes(
|
||||
&Attribute{Table: "users", ID: id, AttName: "username", Value: user},
|
||||
&Attribute{Table: "users", ID: id, AttName: "first_name", Value: first},
|
||||
&Attribute{Table: "users", ID: id, AttName: "last_name", Value: last},
|
||||
&Attribute{Table: "users", ID: id, AttName: "username", Value: userName},
|
||||
&Attribute{Table: "users", ID: id, AttName: "first_name", Value: aesFirstName},
|
||||
&Attribute{Table: "users", ID: id, AttName: "last_name", Value: aesLastName},
|
||||
&Attribute{Table: "users", ID: id, AttName: "email", Value: aesEmail},
|
||||
); err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
|
||||
@ -202,13 +355,13 @@ func (db *DB) UpdateOwnUserAttributes(id int64, user, first, last, oldPass, newP
|
||||
return fmt.Errorf("error: %v unsuccessful retries for DB operation, aborting", TxMaxRetries)
|
||||
}
|
||||
|
||||
func (db *DB) AddFirstUser(u *User, pass string) (int64, error) {
|
||||
func (db *DB) AddFirstUser(c *Config, u *User, pass string) (int64, error) {
|
||||
var numUsers int64
|
||||
txOptions := &sql.TxOptions{Isolation: sql.LevelSerializable}
|
||||
selectQuery := "SELECT COUNT(*) FROM users"
|
||||
insertQuery := `
|
||||
INSERT INTO users (username, password, first_name, last_name, role)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
INSERT INTO users (username, password, first_name, last_name, email, role)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
`
|
||||
|
||||
for i := 0; i < TxMaxRetries; i++ {
|
||||
@ -239,7 +392,31 @@ func (db *DB) AddFirstUser(u *User, pass string) (int64, error) {
|
||||
return 0, fmt.Errorf("error creating password hash: %v", err)
|
||||
}
|
||||
|
||||
result, err := tx.Exec(insertQuery, u.UserName, string(hashedPass), u.FirstName, u.LastName, u.Role)
|
||||
aesFirstName, err := aesEncrypt(c, u.FirstName)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
|
||||
}
|
||||
return 0, fmt.Errorf("error encrypting first name: %v", err)
|
||||
}
|
||||
|
||||
aesLastName, err := aesEncrypt(c, u.LastName)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
|
||||
}
|
||||
return 0, fmt.Errorf("error encrypting last name: %v", err)
|
||||
}
|
||||
|
||||
aesEmail, err := aesEncrypt(c, u.Email)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
|
||||
}
|
||||
return 0, fmt.Errorf("error encrypting email: %v", err)
|
||||
}
|
||||
|
||||
result, err := tx.Exec(insertQuery, u.UserName, string(hashedPass), aesFirstName, aesLastName, aesEmail, u.Role)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
|
||||
@ -270,8 +447,11 @@ func (db *DB) AddFirstUser(u *User, pass string) (int64, error) {
|
||||
return 0, fmt.Errorf("error: %v unsuccessful retries for DB operation, aborting", TxMaxRetries)
|
||||
}
|
||||
|
||||
func (db *DB) GetAllUsers() (map[int64]*User, error) {
|
||||
query := "SELECT id, username, first_name, last_name, role FROM users"
|
||||
func (db *DB) GetAllUsers(c *Config) (map[int64]*User, error) {
|
||||
var aesFirstName, aesLastName, aesEmail string
|
||||
var err error
|
||||
|
||||
query := "SELECT id, username, first_name, last_name, email, role FROM users"
|
||||
|
||||
rows, err := db.Query(query)
|
||||
if err != nil {
|
||||
@ -281,10 +461,25 @@ func (db *DB) GetAllUsers() (map[int64]*User, error) {
|
||||
users := make(map[int64]*User, 0)
|
||||
for rows.Next() {
|
||||
user := new(User)
|
||||
if err = rows.Scan(&user.ID, &user.UserName, &user.FirstName,
|
||||
&user.LastName, &user.Role); err != nil {
|
||||
if err = rows.Scan(&user.ID, &user.UserName, &aesFirstName, &aesLastName, &aesEmail, &user.Role); err != nil {
|
||||
return nil, fmt.Errorf("error getting user info: %v", err)
|
||||
}
|
||||
|
||||
user.FirstName, err = aesDecrypt(c, aesFirstName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decrypting first name: %v", err)
|
||||
}
|
||||
|
||||
user.LastName, err = aesDecrypt(c, aesLastName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decrypting last name: %v", err)
|
||||
}
|
||||
|
||||
user.Email, err = aesDecrypt(c, aesEmail)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decrypting email: %v", err)
|
||||
}
|
||||
|
||||
users[user.ID] = user
|
||||
}
|
||||
|
||||
@ -311,17 +506,10 @@ func (tx *Tx) SetPassword(id int64, newPass string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) UpdateUserAttributes(id int64, user, first, last, newPass, newPass2 string, role int) error {
|
||||
passwordEmpty := true
|
||||
if len(newPass) > 0 || len(newPass2) > 0 {
|
||||
if newPass != newPass2 {
|
||||
return fmt.Errorf("error: passwords do not match")
|
||||
}
|
||||
passwordEmpty = false
|
||||
}
|
||||
|
||||
tx := new(Tx)
|
||||
func (db *DB) UpdateUserAttributes(c *Config, id int64, userName, firstName, lastName, email, newPass string, role int) error {
|
||||
var err error
|
||||
tx := new(Tx)
|
||||
passwordEmpty := len(newPass) > 0
|
||||
|
||||
for i := 0; i < TxMaxRetries; i++ {
|
||||
err := func() error {
|
||||
@ -339,10 +527,35 @@ func (db *DB) UpdateUserAttributes(id int64, user, first, last, newPass, newPass
|
||||
}
|
||||
}
|
||||
|
||||
aesFirstName, err := aesEncrypt(c, firstName)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
|
||||
}
|
||||
return fmt.Errorf("error encrypting first name: %v", err)
|
||||
}
|
||||
|
||||
aesLastName, err := aesEncrypt(c, lastName)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
|
||||
}
|
||||
return fmt.Errorf("error encrypting last name: %v", err)
|
||||
}
|
||||
|
||||
aesEmail, err := aesEncrypt(c, email)
|
||||
if err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
log.Fatalf("transaction error: %v, rollback error: %v", err, rollbackErr)
|
||||
}
|
||||
return fmt.Errorf("error encrypting email: %v", err)
|
||||
}
|
||||
|
||||
if err = tx.UpdateAttributes(
|
||||
&Attribute{Table: "users", ID: id, AttName: "username", Value: user},
|
||||
&Attribute{Table: "users", ID: id, AttName: "first_name", Value: first},
|
||||
&Attribute{Table: "users", ID: id, AttName: "last_name", Value: last},
|
||||
&Attribute{Table: "users", ID: id, AttName: "username", Value: userName},
|
||||
&Attribute{Table: "users", ID: id, AttName: "first_name", Value: aesFirstName},
|
||||
&Attribute{Table: "users", ID: id, AttName: "last_name", Value: aesLastName},
|
||||
&Attribute{Table: "users", ID: id, AttName: "email", Value: aesEmail},
|
||||
&Attribute{Table: "users", ID: id, AttName: "role", Value: role},
|
||||
); err != nil {
|
||||
if rollbackErr := tx.Rollback(); rollbackErr != nil {
|
||||
|
26
cmd/calls/atom.go
Normal file
26
cmd/calls/atom.go
Normal file
@ -0,0 +1,26 @@
|
||||
package calls
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
b "streifling.com/jason/cpolis/cmd/backend"
|
||||
)
|
||||
|
||||
func ServeAtomFeed(c *b.Config) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if !tokenIsVerified(w, r, c) {
|
||||
return
|
||||
}
|
||||
|
||||
absFilepath, err := filepath.Abs(c.AtomFeed)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
http.ServeFile(w, r, absFilepath)
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ import (
|
||||
b "streifling.com/jason/cpolis/cmd/backend"
|
||||
)
|
||||
|
||||
func ServeImage(c *b.Config, s *b.CookieStore) http.HandlerFunc {
|
||||
func ServeImage(c *b.Config) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if !tokenIsVerified(w, r, c) {
|
||||
return
|
||||
|
@ -1,17 +0,0 @@
|
||||
package calls
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
b "streifling.com/jason/cpolis/cmd/backend"
|
||||
)
|
||||
|
||||
func ServeRSS(c *b.Config) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if !tokenIsVerified(w, r, c) {
|
||||
return
|
||||
}
|
||||
|
||||
http.ServeFile(w, r, c.RSSFile)
|
||||
}
|
||||
}
|
@ -146,9 +146,12 @@ func SubmitArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
tmpl = template.Must(tmpl, err)
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", session.Values["role"]); err != nil {
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -225,9 +228,12 @@ func ResubmitArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
tmpl = template.Must(tmpl, err)
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", session.Values["role"]); err != nil {
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -342,7 +348,7 @@ func ReviewRejectedArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.Handler
|
||||
|
||||
imgURL := strings.Split(data.Article.BannerLink, "/")
|
||||
imgFileName := imgURL[len(imgURL)-1]
|
||||
data.BannerImage, err = serveBase64Image(c, imgFileName)
|
||||
data.BannerImage, err = b.ServeBase64Image(c, imgFileName)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
@ -469,9 +475,12 @@ func PublishArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
tmpl = template.Must(tmpl, err)
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", session.Values["role"]); err != nil {
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -501,9 +510,12 @@ func RejectArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
tmpl = template.Must(tmpl, err)
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", session.Values["role"]); err != nil {
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -613,7 +625,7 @@ func ReviewArticle(c *b.Config, db *b.DB, s *b.CookieStore, action, title, butto
|
||||
|
||||
imgURL := strings.Split(article.BannerLink, "/")
|
||||
imgFileName := imgURL[len(imgURL)-1]
|
||||
data.BannerImage, err = serveBase64Image(c, imgFileName)
|
||||
data.BannerImage, err = b.ServeBase64Image(c, imgFileName)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
@ -698,9 +710,12 @@ func DeleteArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
tmpl = template.Must(tmpl, err)
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", session.Values["role"].(int)); err != nil {
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -755,8 +770,11 @@ func AllowEditArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl := template.Must(template.ParseFiles(c.WebDir + "/templates/hub.html"))
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", session.Values["role"].(int)); err != nil {
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -790,7 +808,7 @@ func EditArticle(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
|
||||
imgURL := strings.Split(data.Article.BannerLink, "/")
|
||||
imgFileName := imgURL[len(imgURL)-1]
|
||||
data.BannerImage, err = serveBase64Image(c, imgFileName)
|
||||
data.BannerImage, err = b.ServeBase64Image(c, imgFileName)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
|
@ -1,35 +1,14 @@
|
||||
package frontend
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
b "streifling.com/jason/cpolis/cmd/backend"
|
||||
)
|
||||
|
||||
func serveBase64Image(c *b.Config, filename string) (string, error) {
|
||||
file := c.PicsDir + "/" + filename
|
||||
|
||||
img, err := os.Open(file)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error opening file %v: %v", file, err)
|
||||
}
|
||||
defer img.Close()
|
||||
|
||||
imgBytes, err := io.ReadAll(img)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error turning %v into bytes: %v", file, err)
|
||||
}
|
||||
|
||||
return base64.StdEncoding.EncodeToString(imgBytes), nil
|
||||
}
|
||||
|
||||
func UploadImage(c *b.Config, s *b.CookieStore) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if _, err := getSession(w, r, c, s); err != nil {
|
||||
@ -52,7 +31,7 @@ func UploadImage(c *b.Config, s *b.CookieStore) http.HandlerFunc {
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
filename, err := b.SaveImage(c, file)
|
||||
filename, err := b.SaveImage(file, c.MaxImgHeight, c.MaxImgWidth, c.PicsDir+"/")
|
||||
if err != nil {
|
||||
if err == b.ErrUnsupportedFormat {
|
||||
http.Error(w, "Das Dateiformat wird nicht unterstützt.", http.StatusBadRequest)
|
||||
@ -69,51 +48,6 @@ func UploadImage(c *b.Config, s *b.CookieStore) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func UploadIssueImage(c *b.Config, s *b.CookieStore) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
session, err := getSession(w, r, c, s)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if err := r.ParseMultipartForm(10 << 20); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
file, _, err := r.FormFile("issue-image")
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
filename, err := b.SaveImage(c, file)
|
||||
if err != nil {
|
||||
if err == b.ErrUnsupportedFormat {
|
||||
http.Error(w, "Das Dateiformat wird nicht unterstützt.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
session.Values["issue-image"] = filename
|
||||
if err = session.Save(r, w); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func UploadBanner(c *b.Config, s *b.CookieStore, fileKey, htmlFile, htmlTemplate string) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if _, err := getSession(w, r, c, s); err != nil {
|
||||
@ -130,7 +64,7 @@ func UploadBanner(c *b.Config, s *b.CookieStore, fileKey, htmlFile, htmlTemplate
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
filename, err := b.SaveImage(c, file)
|
||||
filename, err := b.SaveImage(file, c.MaxBannerHeight, c.MaxBannerWidth, c.PicsDir+"/")
|
||||
if err != nil {
|
||||
if err == b.ErrUnsupportedFormat {
|
||||
http.Error(w, "Das Dateiformat wird nicht unterstützt.", http.StatusBadRequest)
|
||||
@ -141,7 +75,7 @@ func UploadBanner(c *b.Config, s *b.CookieStore, fileKey, htmlFile, htmlTemplate
|
||||
return
|
||||
}
|
||||
|
||||
base64Img, err := serveBase64Image(c, filename)
|
||||
base64Img, err := b.ServeBase64Image(c, filename)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
|
@ -110,9 +110,12 @@ func PublishLatestIssue(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFun
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
tmpl = template.Must(tmpl, err)
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", session.Values["role"]); err != nil {
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -56,12 +56,18 @@ func HomePage(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
data := new(struct {
|
||||
Version string
|
||||
Role int
|
||||
})
|
||||
data.Version = c.Version
|
||||
|
||||
files := make([]string, 2)
|
||||
files[0] = c.WebDir + "/templates/index.html"
|
||||
if numRows == 0 {
|
||||
files[1] = c.WebDir + "/templates/first-user.html"
|
||||
tmpl, err := template.ParseFiles(files...)
|
||||
if err = template.Must(tmpl, err).Execute(w, nil); err != nil {
|
||||
if err = template.Must(tmpl, err).Execute(w, data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -74,9 +80,10 @@ func HomePage(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
if auth, ok := session.Values["authenticated"].(bool); auth && ok {
|
||||
data.Role = session.Values["role"].(int)
|
||||
files[1] = c.WebDir + "/templates/hub.html"
|
||||
tmpl, err := template.ParseFiles(files...)
|
||||
if err = template.Must(tmpl, err).Execute(w, session.Values["role"]); err != nil {
|
||||
if err = template.Must(tmpl, err).Execute(w, data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -84,7 +91,7 @@ func HomePage(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
} else {
|
||||
files[1] = c.WebDir + "/templates/login.html"
|
||||
tmpl, err := template.ParseFiles(files...)
|
||||
if err = template.Must(tmpl, err).Execute(w, nil); err != nil {
|
||||
if err = template.Must(tmpl, err).Execute(w, data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -111,7 +118,7 @@ func Login(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := db.GetUser(id)
|
||||
user, err := db.GetUser(c, id)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
@ -125,7 +132,7 @@ func Login(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
}
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
if err = template.Must(tmpl, err).ExecuteTemplate(w, "page-content", user.Role); err != nil {
|
||||
if err = template.Must(tmpl, err).ExecuteTemplate(w, "page-content", user); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -174,8 +181,11 @@ func ShowHub(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
if err = template.Must(tmpl, err).ExecuteTemplate(w, "page-content", session.Values["role"].(int)); err != nil {
|
||||
if err = template.Must(tmpl, err).ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -41,9 +41,12 @@ func AddTag(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
}
|
||||
db.AddTag(tag)
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
tmpl = template.Must(tmpl, err)
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", session.Values["role"]); err != nil {
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -11,8 +11,8 @@ import (
|
||||
)
|
||||
|
||||
func checkUserStrings(user *b.User) (string, int, bool) {
|
||||
userLen := 15
|
||||
nameLen := 50
|
||||
userLen := 63 // max value for utf-8 at 255 bytes
|
||||
nameLen := 56 // max value when aes encrypting utf-8 at up to 255 bytes
|
||||
|
||||
if len(user.UserName) > userLen {
|
||||
return "Benutzername", userLen, false
|
||||
@ -55,12 +55,13 @@ func AddUser(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
UserName: r.PostFormValue("username"),
|
||||
FirstName: r.PostFormValue("first-name"),
|
||||
LastName: r.PostFormValue("last-name"),
|
||||
Email: r.PostFormValue("email"),
|
||||
}
|
||||
pass := r.PostFormValue("password")
|
||||
pass2 := r.PostFormValue("password2")
|
||||
email2 := r.PostFormValue("email2")
|
||||
|
||||
if len(user.UserName) == 0 || len(user.FirstName) == 0 ||
|
||||
len(user.LastName) == 0 || len(pass) == 0 || len(pass2) == 0 {
|
||||
if len(user.UserName) == 0 || len(user.FirstName) == 0 || len(user.LastName) == 0 || len(user.Email) == 0 || len(email2) == 0 || len(pass) == 0 || len(pass2) == 0 {
|
||||
http.Error(w, "Bitte alle Felder ausfüllen.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
@ -76,6 +77,11 @@ func AddUser(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
if user.Email != email2 {
|
||||
http.Error(w, "Die Emailadressen stimmen nicht überein.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if pass != pass2 {
|
||||
http.Error(w, "Die Passwörter stimmen nicht überein.", http.StatusBadRequest)
|
||||
return
|
||||
@ -94,16 +100,19 @@ func AddUser(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = db.AddUser(user, pass)
|
||||
_, err = db.AddUser(c, user, pass)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
tmpl = template.Must(tmpl, err)
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", session.Values["role"].(int)); err != nil {
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -120,7 +129,7 @@ func EditSelf(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := db.GetUser(session.Values["id"].(int64))
|
||||
user, err := db.GetUser(c, session.Values["id"].(int64))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
@ -150,11 +159,13 @@ func UpdateSelf(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
UserName: r.PostFormValue("username"),
|
||||
FirstName: r.PostFormValue("first-name"),
|
||||
LastName: r.PostFormValue("last-name"),
|
||||
Email: r.PostFormValue("email"),
|
||||
}
|
||||
|
||||
oldPass := r.PostFormValue("old-password")
|
||||
newPass := r.PostFormValue("password")
|
||||
newPass2 := r.PostFormValue("password2")
|
||||
email2 := r.PostFormValue("email2")
|
||||
|
||||
if len(user.UserName) == 0 {
|
||||
http.Error(w, "Bitte den Benutzernamen ausfüllen.", http.StatusBadRequest)
|
||||
@ -166,6 +177,20 @@ func UpdateSelf(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
if len(user.Email) == 0 || len(email2) == 0 {
|
||||
http.Error(w, "Bitte die Emailadresse ausfüllen.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if user.Email != email2 {
|
||||
http.Error(w, "Die Emailadressen stimmen nicht überein", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if newPass != newPass2 {
|
||||
http.Error(w, "Die Passwörter stimmen nicht überein", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
userString, stringLen, ok := checkUserStrings(user)
|
||||
if !ok {
|
||||
http.Error(w, fmt.Sprint(userString, " ist zu lang. Maximal ", stringLen, " Zeichen erlaubt."), http.StatusBadRequest)
|
||||
@ -177,15 +202,18 @@ func UpdateSelf(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
if err = db.UpdateOwnUserAttributes(user.ID, user.UserName, user.FirstName, user.LastName, oldPass, newPass, newPass2); err != nil {
|
||||
if err = db.UpdateOwnUserAttributes(c, user.ID, user.UserName, user.FirstName, user.LastName, user.Email, oldPass, newPass); err != nil {
|
||||
log.Println("error: user:", user.ID, err)
|
||||
http.Error(w, "Benutzerdaten konnten nicht aktualisiert werden.", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
tmpl = template.Must(tmpl, err)
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", session.Values["role"].(int)); err != nil {
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -200,13 +228,14 @@ func AddFirstUser(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
UserName: r.PostFormValue("username"),
|
||||
FirstName: r.PostFormValue("first-name"),
|
||||
LastName: r.PostFormValue("last-name"),
|
||||
Email: r.PostFormValue("email"),
|
||||
Role: b.Admin,
|
||||
}
|
||||
pass := r.PostFormValue("password")
|
||||
pass2 := r.PostFormValue("password2")
|
||||
email2 := r.PostFormValue("email2")
|
||||
|
||||
if len(user.UserName) == 0 || len(user.FirstName) == 0 ||
|
||||
len(user.LastName) == 0 || len(pass) == 0 || len(pass2) == 0 {
|
||||
if len(user.UserName) == 0 || len(user.FirstName) == 0 || len(user.LastName) == 0 || len(user.Email) == 0 || len(email2) == 0 || len(pass) == 0 || len(pass2) == 0 {
|
||||
http.Error(w, "Bitte alle Felder ausfüllen.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
@ -217,12 +246,17 @@ func AddFirstUser(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
if user.Email != email2 {
|
||||
http.Error(w, "Die Emailadressen stimmen nicht überein.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if pass != pass2 {
|
||||
http.Error(w, "Die Passwörter stimmen nicht überein.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
user.ID, err = db.AddFirstUser(user, pass)
|
||||
user.ID, err = db.AddFirstUser(c, user, pass)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
@ -245,8 +279,11 @@ func AddFirstUser(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = 0
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
if err = template.Must(tmpl, err).ExecuteTemplate(w, "page-content", 0); err != nil {
|
||||
if err = template.Must(tmpl, err).ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -269,7 +306,7 @@ func ShowAllUsers(c *b.Config, db *b.DB, s *b.CookieStore, action string) http.H
|
||||
})
|
||||
|
||||
data.Action = action
|
||||
data.Users, err = db.GetAllUsers()
|
||||
data.Users, err = db.GetAllUsers(c)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
@ -301,7 +338,7 @@ func EditUser(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := db.GetUser(id)
|
||||
user, err := db.GetUser(c, id)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
@ -343,19 +380,34 @@ func UpdateUser(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
|
||||
user.UserName = r.PostFormValue("username")
|
||||
if len(user.UserName) == 0 {
|
||||
http.Error(w, "Bitte den Benutzernamen ausfüllen.", http.StatusInternalServerError)
|
||||
http.Error(w, "Bitte den Benutzernamen ausfüllen.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
user.FirstName = r.PostFormValue("first-name")
|
||||
user.LastName = r.PostFormValue("last-name")
|
||||
if len(user.FirstName) == 0 || len(user.LastName) == 0 {
|
||||
http.Error(w, "Bitte den vollständigen Namen ausfüllen.", http.StatusInternalServerError)
|
||||
http.Error(w, "Bitte den vollständigen Namen ausfüllen.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
user.Email = r.PostFormValue("email")
|
||||
email2 := r.PostFormValue("email2")
|
||||
if len(user.Email) == 0 || len(email2) == 0 {
|
||||
http.Error(w, "Bitte die Emailadresse ausfüllen.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if user.Email != email2 {
|
||||
http.Error(w, "Die Emailadressen stimmen nicht überein.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
newPass := r.PostFormValue("password")
|
||||
newPass2 := r.PostFormValue("password2")
|
||||
if newPass != newPass2 {
|
||||
http.Error(w, "Die Passwörter stimmen nicht überein.", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
userString, stringLen, ok := checkUserStrings(user)
|
||||
if !ok {
|
||||
@ -368,14 +420,17 @@ func UpdateUser(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
if err = db.UpdateUserAttributes(user.ID, user.UserName, user.FirstName, user.LastName, newPass, newPass2, user.Role); err != nil {
|
||||
if err = db.UpdateUserAttributes(c, user.ID, user.UserName, user.FirstName, user.LastName, user.Email, newPass, user.Role); err != nil {
|
||||
log.Println("error: user:", user.ID, err)
|
||||
http.Error(w, "Benutzerdaten konnten nicht aktualisiert werden.", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl := template.Must(template.ParseFiles(c.WebDir + "/templates/hub.html"))
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", session.Values["role"].(int)); err != nil {
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
@ -405,9 +460,12 @@ func DeleteUser(c *b.Config, db *b.DB, s *b.CookieStore) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
data := new(struct{ Role int })
|
||||
data.Role = session.Values["role"].(int)
|
||||
|
||||
tmpl, err := template.ParseFiles(c.WebDir + "/templates/hub.html")
|
||||
tmpl = template.Must(tmpl, err)
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", session.Values["role"].(int)); err != nil {
|
||||
if err = tmpl.ExecuteTemplate(w, "page-content", data); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -34,13 +34,13 @@ func main() {
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
key, err := b.LoadKey(config.KeyFile)
|
||||
key, err := b.LoadKey(config.GOBKeyFile)
|
||||
if err != nil {
|
||||
key, err = b.NewKey()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
if err = b.SaveKey(key, config.KeyFile); err != nil {
|
||||
if err = b.SaveKey(key, config.GOBKeyFile); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
@ -66,13 +66,13 @@ func main() {
|
||||
mux.HandleFunc("GET /article/review-unpublished/{id}", f.ReviewArticle(config, db, store, "publish", "Artikel veröffentlichen", "Veröffentlichen"))
|
||||
mux.HandleFunc("GET /article/serve/{id}", c.ServeArticle(config, db))
|
||||
mux.HandleFunc("GET /article/write", f.WriteArticle(config, db, store))
|
||||
mux.HandleFunc("GET /atom/serve", c.ServeAtomFeed(config))
|
||||
mux.HandleFunc("GET /hub", f.ShowHub(config, db, store))
|
||||
mux.HandleFunc("GET /image/serve/{pic}", c.ServeImage(config, store))
|
||||
mux.HandleFunc("GET /image/serve/{pic}", c.ServeImage(config))
|
||||
mux.HandleFunc("GET /issue/this", f.ShowCurrentIssue(config, db, store))
|
||||
mux.HandleFunc("GET /logout", f.Logout(config, store))
|
||||
mux.HandleFunc("GET /pdf/get-list", c.ServePDFList(config))
|
||||
mux.HandleFunc("GET /pdf/serve/{id}", c.ServePDF(config))
|
||||
mux.HandleFunc("GET /rss/serve", c.ServeRSS(config))
|
||||
mux.HandleFunc("GET /tag/create", f.CreateTag(config, store))
|
||||
mux.HandleFunc("GET /user/create", f.CreateUser(config, store))
|
||||
mux.HandleFunc("GET /user/delete/{id}", f.DeleteUser(config, db, store))
|
||||
|
@ -5,12 +5,14 @@ DROP TABLE IF EXISTS issues;
|
||||
DROP TABLE IF EXISTS users;
|
||||
|
||||
CREATE TABLE users (
|
||||
id INT AUTO_INCREMENT,
|
||||
username VARCHAR(15) NOT NULL UNIQUE,
|
||||
password VARCHAR(60) NOT NULL,
|
||||
first_name VARCHAR(50) NOT NULL,
|
||||
last_name VARCHAR(50) NOT NULL,
|
||||
role INT NOT NULL,
|
||||
id INT AUTO_INCREMENT,
|
||||
username VARCHAR(255) NOT NULL UNIQUE,
|
||||
password VARCHAR(60) NOT NULL,
|
||||
first_name VARCHAR(255) NOT NULL,
|
||||
last_name VARCHAR(255) NOT NULL,
|
||||
email VARCHAR(255) NOT NULL,
|
||||
-- profile_pic_link VARCHAR(255) NOT NULL,
|
||||
role INT NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
@ -24,12 +26,9 @@ CREATE TABLE articles (
|
||||
id INT AUTO_INCREMENT,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
banner_link VARCHAR(255),
|
||||
banner_link VARCHAR(255) NOT NULL,
|
||||
summary TEXT NOT NULL,
|
||||
content_link VARCHAR(255),
|
||||
enc_url VARCHAR(255),
|
||||
enc_length INT,
|
||||
enc_type VARCHAR(255),
|
||||
content_link VARCHAR(255) NOT NULL,
|
||||
published BOOL NOT NULL,
|
||||
rejected BOOL NOT NULL,
|
||||
author_id INT NOT NULL,
|
||||
|
@ -32,11 +32,11 @@ textarea {
|
||||
}
|
||||
|
||||
.btn {
|
||||
@apply bg-slate-200 dark:bg-slate-800 hover:bg-slate-100 dark:hover:bg-slate-900 border border-slate-200 dark:border-slate-800 my-2 px-3 py-2 rounded-md w-full;
|
||||
@apply bg-slate-200 dark:bg-slate-800 hover:bg-slate-100 dark:hover:bg-slate-900 border border-slate-200 dark:border-slate-800 cursor-pointer my-2 px-3 py-2 rounded-md w-full;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
@apply bg-slate-800 dark:bg-slate-200 hover:bg-slate-700 dark:hover:bg-slate-300 my-2 px-3 py-2 rounded-md text-slate-50 dark:text-slate-950 w-full;
|
||||
@apply bg-slate-800 dark:bg-slate-200 hover:bg-slate-700 dark:hover:bg-slate-300 cursor-pointer my-2 px-3 py-2 rounded-md text-slate-50 dark:text-slate-950 w-full;
|
||||
}
|
||||
|
||||
.EasyMDEContainer .CodeMirror {
|
||||
|
@ -7,22 +7,36 @@
|
||||
<label for="username">Benutzername</label>
|
||||
<input class="w-full" required name="username" type="text" value="{{.UserName}}" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="password">Passwort</label>
|
||||
<input class="w-full" required name="password" placeholder="***" type="password" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="password2">Passwort wiederholen</label>
|
||||
<input class="w-full" required name="password2" placeholder="***" type="password" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="first-name">Vorname</label>
|
||||
<input class="w-full" required name="first-name" type="text" value="{{.FirstName}}" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="last-name">Nachname</label>
|
||||
<input class="w-full" required name="last-name" type="text" value="{{.LastName}}" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="email">Emailadresse</label>
|
||||
<input class="w-full" required name="email" type="text" value="{{.Email}}" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="email2">Emailadresse wiederholen</label>
|
||||
<input class="w-full" required name="email2" type="text" value="{{.Email}}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-4">
|
||||
|
@ -13,7 +13,7 @@
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 items-center">
|
||||
<label class="btn cursor-pointer text-center" for="issue-banner-upload">Titelbild</label>
|
||||
<label class="btn text-center" for="issue-banner-upload">Titelbild</label>
|
||||
<input class="hidden" id="issue-banner-upload" name="issue-banner" type="file" required
|
||||
hx-post="/issue/upload-banner" hx-target="#issue-banner-container" />
|
||||
</div>
|
||||
|
@ -32,6 +32,16 @@
|
||||
<label for="password2">Passwort wiederholen</label>
|
||||
<input class="w-full" name="password2" placeholder="***" type="password" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="email">Emailadresse</label>
|
||||
<input class="w-full" required name="email" type="text" value="{{.Email}}" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="email2">Emailadresse wiederholen</label>
|
||||
<input class="w-full" required name="email2" type="text" value="{{.Email}}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-area">
|
||||
|
@ -27,6 +27,16 @@
|
||||
<label for="password2">Passwort wiederholen</label>
|
||||
<input class="w-full" name="password2" placeholder="***" type="password" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="email">Emailadresse</label>
|
||||
<input class="w-full" required name="email" type="text" value="{{.Email}}" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="email2">Emailadresse wiederholen</label>
|
||||
<input class="w-full" required name="email2" type="text" value="{{.Email}}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-4">
|
||||
|
@ -15,7 +15,7 @@
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 items-center">
|
||||
<label class="btn cursor-pointer text-center" for="article-banner">Titelbild</label>
|
||||
<label class="btn text-center" for="article-banner">Titelbild</label>
|
||||
<input class="hidden" id="article-banner" name="article-banner" type="file" required
|
||||
hx-post="/article/upload-banner" hx-target="#article-banner-container" />
|
||||
</div>
|
||||
|
@ -5,23 +5,37 @@
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label for="username">Benutzername</label>
|
||||
<input class="w-full" required name="username" type="text" value="{{.UserName}}" />
|
||||
<input class="w-full" required name="username" type="text" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="password">Passwort</label>
|
||||
<input class="w-full" required name="password" placeholder="***" type="password" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="password2">Passwort wiederholen</label>
|
||||
<input class="w-full" required name="password2" placeholder="***" type="password" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="first-name">Vorname</label>
|
||||
<input class="w-full" required name="first-name" type="text" value="{{.FirstName}}" />
|
||||
<input class="w-full" required name="first-name" type="text" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="last-name">Nachname</label>
|
||||
<input class="w-full" required name="last-name" type="text" value="{{.LastName}}" />
|
||||
<input class="w-full" required name="last-name" type="text" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="email">Emailadresse</label>
|
||||
<input class="w-full" required name="email" type="text" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="email2">Emailadresse wiederholen</label>
|
||||
<input class="w-full" required name="email2" type="text" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -2,30 +2,31 @@
|
||||
<div class="flex flex-col gap-4">
|
||||
<button class="btn" hx-get="/logout" hx-target="#page-content">Abmelden</button>
|
||||
|
||||
{{if lt . 4}}
|
||||
{{if lt .Role 4}}
|
||||
<div class="mb-3">
|
||||
<h2>Artikel</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-2">
|
||||
<button class="btn" hx-get="/article/write" hx-target="#page-content">Artikel schreiben</button>
|
||||
<button class="btn" hx-get="/article/all-rejected" hx-target="#page-content">Artikel bearbeiten</button>
|
||||
{{if lt . 3}}<button class="btn" hx-get="/article/all-unpublished-unrejected-and-published-rejected"
|
||||
{{if lt .Role 3}}<button class="btn" hx-get="/article/all-unpublished-unrejected-and-published-rejected"
|
||||
hx-target="#page-content">Artikel veröffentlichen</button>{{end}}
|
||||
{{if lt . 2}}<button class="btn" hx-get="/article/all-published/delete" hx-target="#page-content">Artikel
|
||||
löschen</button>{{end}}
|
||||
{{if lt . 2}}<button class="btn" hx-get="/article/all-published/review-edit"
|
||||
{{if lt .Role 2}}<button class="btn" hx-get="/article/all-published/delete"
|
||||
hx-target="#page-content">Artikel löschen</button>{{end}}
|
||||
{{if lt .Role 2}}<button class="btn" hx-get="/article/all-published/review-edit"
|
||||
hx-target="#page-content">Artikel bearbeiten lassen</button>{{end}}
|
||||
{{if lt . 3}}<button class="btn" hx-get="/tag/create" hx-target="#page-content">Neuer Tag</button>{{end}}
|
||||
{{if lt .Role 3}}<button class="btn" hx-get="/tag/create" hx-target="#page-content">Neuer
|
||||
Tag</button>{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if lt . 2}}
|
||||
{{if lt .Role 2}}
|
||||
<div class="mb-3">
|
||||
<h2>Ausgabe</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-2">
|
||||
<button class="btn" hx-get="/issue/this" hx-target="#page-content">Diese Ausgabe</button>
|
||||
<form class="flex" hx-encoding="multipart/form-data">
|
||||
<label class="btn cursor-pointer text-center" for="pdf-upload">PDF hochladen</label>
|
||||
<label class="btn text-center" for="pdf-upload">PDF hochladen</label>
|
||||
<input accept=".pdf" class="hidden" id="pdf-upload" name="pdf-upload" type="file"
|
||||
hx-post="/pdf/upload" />
|
||||
</form>
|
||||
@ -33,16 +34,16 @@
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{if lt . 4}}
|
||||
{{if lt .Role 4}}
|
||||
<div class="mb-3">
|
||||
<h2>Benutzer</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-x-4 gap-y-2">
|
||||
<button class="btn" hx-get="/user/edit/self" hx-target="#page-content">Mein Profil bearbeiten</button>
|
||||
{{if eq . 0}}<button class="btn" hx-get="/user/create" hx-target="#page-content">Benutzer
|
||||
{{if eq .Role 0}}<button class="btn" hx-get="/user/create" hx-target="#page-content">Benutzer
|
||||
hinzufügen</button>{{end}}
|
||||
{{if eq . 0}}<button class="btn" hx-get="/user/show-all/edit" hx-target="#page-content">Benutzer
|
||||
{{if eq .Role 0}}<button class="btn" hx-get="/user/show-all/edit" hx-target="#page-content">Benutzer
|
||||
bearbeiten</button>{{end}}
|
||||
{{if eq . 0}}<button class="btn" hx-get="/user/show-all/delete" hx-target="#page-content">Benutzer
|
||||
{{if eq .Role 0}}<button class="btn" hx-get="/user/show-all/delete" hx-target="#page-content">Benutzer
|
||||
löschen</button>{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -39,7 +39,7 @@
|
||||
|
||||
<footer class="text-center text-gray-500 my-8">
|
||||
<p>© 2024 Jason Streifling. Alle Rechte vorbehalten.</p>
|
||||
<p>v0.13.0 - <strong>Alpha: Drastische Änderungen und Fehler vorbehalten.</strong></p>
|
||||
<p>{{.Version}} - <strong>Alpha: Drastische Änderungen und Fehler vorbehalten.</strong></p>
|
||||
</footer>
|
||||
|
||||
<script src="https://unpkg.com/htmx.org@2.0.2"></script>
|
||||
|
Reference in New Issue
Block a user