Core separation #13
|
|
@ -3,7 +3,6 @@ package lishwist
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"lishwist/core/internal/db"
|
"lishwist/core/internal/db"
|
||||||
"log"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
@ -26,12 +25,7 @@ func (g *Group) MemberIndex(userId string) int {
|
||||||
|
|
||||||
func queryManyGroups(query string, args ...any) ([]Group, error) {
|
func queryManyGroups(query string, args ...any) ([]Group, error) {
|
||||||
groups := []Group{}
|
groups := []Group{}
|
||||||
// PrintTables()
|
|
||||||
// PrintViews()
|
|
||||||
log.Println(query, args)
|
|
||||||
rows, err := db.Connection.Query(query, args...)
|
rows, err := db.Connection.Query(query, args...)
|
||||||
// PrintTables()
|
|
||||||
// PrintViews()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Query failed: %w", err)
|
return nil, fmt.Errorf("Query failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
@ -76,16 +70,16 @@ func queryManyGroupMembers(groupId string) ([]User, error) {
|
||||||
return members, nil
|
return members, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Admin) GetGroupByReference(reference string) (*Group, error) {
|
|
||||||
query := "SELECT [group].id, [group].name, [group].reference FROM [group] WHERE [group].reference = ?;"
|
|
||||||
return queryOneGroup(query, reference)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Session) GetGroupByReference(reference string) (*Group, error) {
|
func (s *Session) GetGroupByReference(reference string) (*Group, error) {
|
||||||
stmt := "SELECT [group].id, [group].name, [group].reference FROM [group] JOIN group_member ON [group].id == group_member.group_id WHERE [group].reference = ? AND group_member.user_id = ?;"
|
stmt := "SELECT [group].id, [group].name, [group].reference FROM [group] JOIN group_member ON [group].id == group_member.group_id WHERE [group].reference = ? AND group_member.user_id = ?;"
|
||||||
return queryOneGroup(stmt, reference, s.User.Id)
|
return queryOneGroup(stmt, reference, s.User.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetGroupByReference(reference string) (*Group, error) {
|
||||||
|
stmt := "SELECT [group].id, [group].name, [group].reference FROM [group] WHERE [group].reference = ?;"
|
||||||
|
return queryOneGroup(stmt, reference)
|
||||||
|
}
|
||||||
|
|
||||||
func (a *Admin) ListGroups() ([]Group, error) {
|
func (a *Admin) ListGroups() ([]Group, error) {
|
||||||
query := "SELECT id, name, reference FROM [group];"
|
query := "SELECT id, name, reference FROM [group];"
|
||||||
return queryManyGroups(query)
|
return queryManyGroups(query)
|
||||||
|
|
@ -128,3 +122,9 @@ func (a *Admin) RemoveUserFromGroup(userId, groupId string) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the groups the session user belongs to
|
||||||
|
func (u *Session) GetGroups() ([]Group, error) {
|
||||||
|
stmt := "SELECT [group].id, [group].name, [group].reference FROM [group] JOIN group_member ON group_member.group_id = [group].id JOIN v_user AS user ON user.id = group_member.user_id WHERE user.id = ?"
|
||||||
|
return queryManyGroups(stmt, u.Id)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,9 +37,8 @@ CREATE TABLE IF NOT EXISTS "group_member" (
|
||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS "session" (
|
CREATE TABLE IF NOT EXISTS "session" (
|
||||||
"id" INTEGER NOT NULL UNIQUE,
|
"id" INTEGER NOT NULL UNIQUE,
|
||||||
"user_id" INTEGER NOT NULL,
|
"value" TEXT NOT NULL,
|
||||||
PRIMARY KEY("id" AUTOINCREMENT),
|
PRIMARY KEY("id" AUTOINCREMENT)
|
||||||
FOREIGN KEY("user_id") REFERENCES "user"("id")
|
|
||||||
);
|
);
|
||||||
|
|
||||||
DROP VIEW IF EXISTS "v_user";
|
DROP VIEW IF EXISTS "v_user";
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package fixtures
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
lishwist "lishwist/core"
|
lishwist "lishwist/core"
|
||||||
|
|
||||||
|
|
@ -22,14 +21,12 @@ func Login(t *testing.T, username, password string) *lishwist.Session {
|
||||||
log.Fatalf("Failed to init db: %s\n", err)
|
log.Fatalf("Failed to init db: %s\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
lw := lishwist.NewSessionManager(time.Second*10, 32)
|
|
||||||
|
|
||||||
_, err = lishwist.Register(username, password)
|
_, err = lishwist.Register(username, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to register on login fixture: %s\n", err)
|
log.Fatalf("Failed to register on login fixture: %s\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
session, err := lw.Login(username, password)
|
session, err := lishwist.Login(username, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to login on fixture: %s\n", err)
|
log.Fatalf("Failed to login on fixture: %s\n", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,15 @@ import (
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (sm *SessionManager) Login(username, password string) (*Session, error) {
|
type ErrorInvalidCredentials error
|
||||||
|
|
||||||
|
func Login(username, password string) (*Session, error) {
|
||||||
user, err := getUserByName(username)
|
user, err := getUserByName(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Failed to fetch user: %w", err)
|
return nil, ErrorInvalidCredentials(fmt.Errorf("Failed to fetch user: %w", err))
|
||||||
}
|
}
|
||||||
if user == nil {
|
if user == nil {
|
||||||
return nil, fmt.Errorf("User not found by name: %s", username)
|
return nil, ErrorInvalidCredentials(fmt.Errorf("User not found by name: %s", username))
|
||||||
}
|
}
|
||||||
|
|
||||||
passHash, err := user.getPassHash()
|
passHash, err := user.getPassHash()
|
||||||
|
|
@ -22,13 +24,8 @@ func (sm *SessionManager) Login(username, password string) (*Session, error) {
|
||||||
|
|
||||||
err = bcrypt.CompareHashAndPassword(passHash, []byte(password))
|
err = bcrypt.CompareHashAndPassword(passHash, []byte(password))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, ErrorInvalidCredentials(fmt.Errorf("Password compare failed: %w", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
session, err := sm.createSession(user)
|
return &Session{*user}, nil
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Couldn't create session: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return session, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package lishwist_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
lishwist "lishwist/core"
|
lishwist "lishwist/core"
|
||||||
"lishwist/core/internal/fixtures"
|
"lishwist/core/internal/fixtures"
|
||||||
|
|
@ -14,14 +13,12 @@ func TestLogin(t *testing.T) {
|
||||||
t.Fatalf("Failed to init db: %s\n", err)
|
t.Fatalf("Failed to init db: %s\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
lw := lishwist.NewSessionManager(time.Second*10, 32)
|
|
||||||
|
|
||||||
_, err = lishwist.Register("thomas", "123")
|
_, err = lishwist.Register("thomas", "123")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to register: %s\n", err)
|
t.Fatalf("Failed to register: %s\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = lw.Login("thomas", "123")
|
_, err = lishwist.Login("thomas", "123")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to login: %s\n", err)
|
t.Fatalf("Failed to login: %s\n", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ import (
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ErrorUsernameTaken = errors.New("Username is taken")
|
||||||
|
|
||||||
func Register(username, newPassword string) (*User, error) {
|
func Register(username, newPassword string) (*User, error) {
|
||||||
if username == "" {
|
if username == "" {
|
||||||
return nil, errors.New("Username required")
|
return nil, errors.New("Username required")
|
||||||
|
|
@ -17,7 +19,7 @@ func Register(username, newPassword string) (*User, error) {
|
||||||
|
|
||||||
existingUser, _ := getUserByName(username)
|
existingUser, _ := getUserByName(username)
|
||||||
if existingUser != nil {
|
if existingUser != nil {
|
||||||
return nil, errors.New("Username is taken")
|
return nil, ErrorUsernameTaken
|
||||||
}
|
}
|
||||||
|
|
||||||
hashedPasswordBytes, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.MinCost)
|
hashedPasswordBytes, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.MinCost)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"__meta__": {
|
||||||
|
"about": "xh session file",
|
||||||
|
"xh": "0.24.1"
|
||||||
|
},
|
||||||
|
"auth": {
|
||||||
|
"type": null,
|
||||||
|
"raw_auth": null
|
||||||
|
},
|
||||||
|
"cookies": [
|
||||||
|
{
|
||||||
|
"name": "lishwist_user",
|
||||||
|
"value": "MTc1MDg2NDE2N3xCQXdBQVRjPXw8gaasdVy--TC-_fUb-3ZL58n8UVakTqDm_0_7c50cYA==",
|
||||||
|
"expires": 1750950567,
|
||||||
|
"path": "/lists",
|
||||||
|
"domain": "127.0.0.1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"headers": []
|
||||||
|
}
|
||||||
|
|
@ -1,64 +1,15 @@
|
||||||
package lishwist
|
package lishwist
|
||||||
|
|
||||||
import (
|
import "fmt"
|
||||||
"crypto/rand"
|
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
|
||||||
"lishwist/core/internal/db"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Session struct {
|
type Session struct {
|
||||||
Id string
|
User
|
||||||
Token string
|
|
||||||
User *User
|
|
||||||
ExpiresAt time.Time
|
|
||||||
CreatedAt time.Time
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type SessionManager struct {
|
func SessionFromUsername(username string) (*Session, error) {
|
||||||
sessionDuration time.Duration
|
user, err := getUserByName(username)
|
||||||
sessionTokenLength uint
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSessionManager(sessionDuration time.Duration, sessionTokenLength uint) SessionManager {
|
|
||||||
return SessionManager{
|
|
||||||
sessionDuration,
|
|
||||||
sessionTokenLength,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateSecureToken(size uint) (string, error) {
|
|
||||||
bytes := make([]byte, size)
|
|
||||||
if _, err := rand.Read(bytes); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return base64.URLEncoding.EncodeToString(bytes), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sm *SessionManager) createSession(user *User) (*Session, error) {
|
|
||||||
stmt := "INSERT INTO session (user_id) VALUES (?);"
|
|
||||||
result, err := db.Connection.Exec(stmt, user.Id)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Failed to execute query: %w", err)
|
return nil, fmt.Errorf("Failed to get user: %w", err)
|
||||||
}
|
}
|
||||||
id, err := result.LastInsertId()
|
return &Session{*user}, nil
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to get last insert id: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
token, err := generateSecureToken(sm.sessionTokenLength)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to generate secure token: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
session := Session{
|
|
||||||
Id: fmt.Sprintf("%d", id),
|
|
||||||
Token: token,
|
|
||||||
User: user,
|
|
||||||
ExpiresAt: time.Now().Add(sm.sessionDuration),
|
|
||||||
CreatedAt: time.Now(),
|
|
||||||
}
|
|
||||||
|
|
||||||
return &session, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
57
core/user.go
57
core/user.go
|
|
@ -10,7 +10,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
Id string
|
Id string
|
||||||
|
// TODO: rename to DisplayName
|
||||||
NormalName string
|
NormalName string
|
||||||
Name string
|
Name string
|
||||||
Reference string
|
Reference string
|
||||||
|
|
@ -94,6 +95,11 @@ func getUserByReference(reference string) (*User, error) {
|
||||||
return queryOneUser(stmt, reference)
|
return queryOneUser(stmt, reference)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getUserById(id string) (*User, error) {
|
||||||
|
stmt := "SELECT id, name, display_name, reference, is_admin, is_live FROM v_user WHERE id = ?"
|
||||||
|
return queryOneUser(stmt, id)
|
||||||
|
}
|
||||||
|
|
||||||
func hasUsers() (bool, error) {
|
func hasUsers() (bool, error) {
|
||||||
stmt := "SELECT COUNT(id) FROM v_user LIMIT 1"
|
stmt := "SELECT COUNT(id) FROM v_user LIMIT 1"
|
||||||
var userCount uint
|
var userCount uint
|
||||||
|
|
@ -108,3 +114,52 @@ func (*Admin) ListUsers() ([]User, error) {
|
||||||
stmt := "SELECT id, name, display_name, reference, is_admin, is_live FROM user"
|
stmt := "SELECT id, name, display_name, reference, is_admin, is_live FROM user"
|
||||||
return queryManyUsers(stmt)
|
return queryManyUsers(stmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*Admin) GetUser(id string) (*User, error) {
|
||||||
|
return getUserById(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetUserByReference(reference string) (*User, error) {
|
||||||
|
return getUserByReference(reference)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) GetTodo() ([]Wish, error) {
|
||||||
|
stmt := "SELECT wish.id, wish.name, wish.sent, recipient.name, recipient.reference FROM wish JOIN v_user AS user ON wish.claimant_id = user.id JOIN v_user AS recipient ON wish.recipient_id = recipient.id WHERE user.id = ? ORDER BY wish.sent ASC, wish.name"
|
||||||
|
rows, err := db.Connection.Query(stmt, u.Id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
wishes := []Wish{}
|
||||||
|
for rows.Next() {
|
||||||
|
var id string
|
||||||
|
var name string
|
||||||
|
var sent bool
|
||||||
|
var recipientName string
|
||||||
|
var recipientRef string
|
||||||
|
_ = rows.Scan(&id, &name, &sent, &recipientName, &recipientRef)
|
||||||
|
wish := Wish{
|
||||||
|
Id: id,
|
||||||
|
Name: name,
|
||||||
|
Sent: sent,
|
||||||
|
RecipientName: recipientName,
|
||||||
|
RecipientRef: recipientRef,
|
||||||
|
}
|
||||||
|
wishes = append(wishes, wish)
|
||||||
|
}
|
||||||
|
err = rows.Err()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return wishes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Admin) UserSetLive(userReference string, setting bool) error {
|
||||||
|
query := "UPDATE user SET is_live = ? WHERE reference = ?"
|
||||||
|
_, err := db.Connection.Exec(query, setting, userReference)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// u.IsLive = setting
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
|
||||||
188
core/wish.go
188
core/wish.go
|
|
@ -61,6 +61,47 @@ func (s *Session) MakeWish(name string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *Session) deleteWishes(tx *sql.Tx, ids []string) error {
|
||||||
|
stmt := "DELETE FROM wish WHERE wish.creator_id = ? AND wish.id = ?"
|
||||||
|
for _, id := range ids {
|
||||||
|
r, err := tx.Exec(stmt, u.Id, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rE, err := r.RowsAffected()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if rE < 1 {
|
||||||
|
return fmt.Errorf("Wish deletion failed for '%s'", id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Session) RevokeWishes(ids ...string) error {
|
||||||
|
if len(ids) < 1 {
|
||||||
|
return fmt.Errorf("Attempt to remove zero wishes")
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := db.Connection.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.deleteWishes(tx, ids)
|
||||||
|
if err != nil {
|
||||||
|
rollBackErr := tx.Rollback()
|
||||||
|
if rollBackErr != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Session) GetOthersWishes(userReference string) ([]Wish, error) {
|
func (s *Session) GetOthersWishes(userReference string) ([]Wish, error) {
|
||||||
otherUser, err := getUserByReference(userReference)
|
otherUser, err := getUserByReference(userReference)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -69,7 +110,7 @@ func (s *Session) GetOthersWishes(userReference string) ([]Wish, error) {
|
||||||
if otherUser.Id == s.User.Id {
|
if otherUser.Id == s.User.Id {
|
||||||
return nil, errors.New("Use (s *Session) GetWishes() to view your own wishes")
|
return nil, errors.New("Use (s *Session) GetWishes() to view your own wishes")
|
||||||
}
|
}
|
||||||
stmt := "SELECT gift.id, gift.name, claimant.id, claimant.name, gift.sent, gift.creator_id, creator.name, gift.recipient_id FROM gift JOIN v_user AS user ON gift.recipient_id = user.id LEFT JOIN v_user AS claimant ON gift.claimant_id = claimant.id LEFT JOIN v_user AS creator ON gift.creator_id = creator.id WHERE user.id = ?"
|
stmt := "SELECT wish.id, wish.name, claimant.id, claimant.name, wish.sent, wish.creator_id, creator.name, wish.recipient_id FROM wish JOIN v_user AS user ON wish.recipient_id = user.id LEFT JOIN v_user AS claimant ON wish.claimant_id = claimant.id LEFT JOIN v_user AS creator ON wish.creator_id = creator.id WHERE user.id = ?"
|
||||||
rows, err := db.Connection.Query(stmt, otherUser.Id)
|
rows, err := db.Connection.Query(stmt, otherUser.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Failed to execute query: %w", err)
|
return nil, fmt.Errorf("Failed to execute query: %w", err)
|
||||||
|
|
@ -107,3 +148,148 @@ func (s *Session) GetOthersWishes(userReference string) ([]Wish, error) {
|
||||||
}
|
}
|
||||||
return wishes, nil
|
return wishes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: This could just be a field on the user... but only if we get this often
|
||||||
|
func (u *User) WishCount() (int, error) {
|
||||||
|
stmt := "SELECT COUNT(wish.id) AS wish_count FROM wish WHERE wish.creator_id = ?1 AND wish.recipient_id = ?1"
|
||||||
|
var wishCount int
|
||||||
|
err := db.Connection.QueryRow(stmt, u.Id).Scan(&wishCount)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return wishCount, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Session) executeClaims(tx *sql.Tx, claims, unclaims []string) error {
|
||||||
|
claimStmt := "UPDATE wish SET claimant_id = ? WHERE id = ?"
|
||||||
|
unclaimStmt := "UPDATE wish SET claimant_id = NULL WHERE id = ?"
|
||||||
|
for _, id := range claims {
|
||||||
|
r, err := tx.Exec(claimStmt, s.Id, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rE, err := r.RowsAffected()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if rE < 1 {
|
||||||
|
return fmt.Errorf("Wish claim failed for '%s'", id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, id := range unclaims {
|
||||||
|
r, err := tx.Exec(unclaimStmt, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rE, err := r.RowsAffected()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if rE < 1 {
|
||||||
|
return fmt.Errorf("Wish unclaim failed for '%s'", id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Undertake or abandon wishes made by other users
|
||||||
|
func (s *Session) ClaimWishes(claims, unclaims []string) error {
|
||||||
|
if len(claims) < 1 && len(unclaims) < 1 {
|
||||||
|
return fmt.Errorf("Attempt to claim/unclaim zero wishes")
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := db.Connection.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.executeClaims(tx, claims, unclaims)
|
||||||
|
if err != nil {
|
||||||
|
rollBackErr := tx.Rollback()
|
||||||
|
if rollBackErr != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func executeCompletions(tx *sql.Tx, claims []string) error {
|
||||||
|
claimStmt := "UPDATE wish SET sent = 1 WHERE id = ?"
|
||||||
|
for _, id := range claims {
|
||||||
|
r, err := tx.Exec(claimStmt, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rE, err := r.RowsAffected()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if rE < 1 {
|
||||||
|
return fmt.Errorf("Wish completion failed for '%s'", id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: User ought not be able to interact with wishes outside their group network
|
||||||
|
func (s *Session) CompleteWishes(claims []string) error {
|
||||||
|
if len(claims) < 1 {
|
||||||
|
return fmt.Errorf("Attempt to complete zero wishes")
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := db.Connection.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = executeCompletions(tx, claims)
|
||||||
|
if err != nil {
|
||||||
|
rollBackErr := tx.Rollback()
|
||||||
|
if rollBackErr != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Session) SuggestWishForUser(otherUserReference string, wishName string) error {
|
||||||
|
otherUser, err := GetUserByReference(otherUserReference)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
stmt := "INSERT INTO wish (name, recipient_id, creator_id) VALUES (?, ?, ?)"
|
||||||
|
_, err = db.Connection.Exec(stmt, wishName, otherUser.Id, u.Id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Session) RecindWishesForUser(ids ...string) error {
|
||||||
|
if len(ids) < 1 {
|
||||||
|
return fmt.Errorf("Attempt to remove zero wishes")
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := db.Connection.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.deleteWishes(tx, ids)
|
||||||
|
if err != nil {
|
||||||
|
rollBackErr := tx.Rollback()
|
||||||
|
if rollBackErr != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Commit()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc=
|
||||||
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||||
|
github.com/ncruces/sort v0.1.5/go.mod h1:obJToO4rYr6VWP0Uw5FYymgYGt3Br4RXcs/JdKaXAPk=
|
||||||
|
github.com/psanford/httpreadat v0.1.0/go.mod h1:Zg7P+TlBm3bYbyHTKv/EdtSJZn3qwbPwpfZ/I9GKCRE=
|
||||||
|
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||||
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
|
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
|
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||||
|
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||||
|
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||||
|
lukechampine.com/adiantum v1.1.1/go.mod h1:LrAYVnTYLnUtE/yMp5bQr0HstAf060YUF8nM0B6+rUw=
|
||||||
|
lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||||
|
modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y=
|
||||||
|
modernc.org/ccgo/v3 v3.16.15/go.mod h1:yT7B+/E2m43tmMOT51GMoM98/MtHIcQQSleGnddkUNI=
|
||||||
|
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
|
||||||
|
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||||
|
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||||
|
modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c=
|
||||||
|
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||||
|
modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE=
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
//go:generate go run gen_init_sql.go
|
|
||||||
|
|
||||||
package db
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
|
||||||
"lishwist/http/env"
|
|
||||||
|
|
||||||
"github.com/Teajey/sqlstore"
|
|
||||||
_ "github.com/glebarez/go-sqlite"
|
|
||||||
)
|
|
||||||
|
|
||||||
var database *sql.DB
|
|
||||||
|
|
||||||
func Open() error {
|
|
||||||
db, err := sql.Open("sqlite", env.DatabaseFile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
database = db
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Init() error {
|
|
||||||
_, err := database.Exec(initQuery)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSessionStore() (*sqlstore.Store, error) {
|
|
||||||
deleteStmt, err := database.Prepare("DELETE FROM session WHERE id = ?;")
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to prepare delete statement: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
insertStmt, err := database.Prepare("INSERT INTO session (value) VALUES (?);")
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to prepare insert statement: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
selectStmt, err := database.Prepare("SELECT value FROM session WHERE id = ?;")
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to prepare select statement: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
updateStmt, err := database.Prepare("UPDATE session SET value = ?2 WHERE id = ?1;")
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to prepare update statement: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
s := sqlstore.NewSqlStore(database, sqlstore.Statements{
|
|
||||||
Delete: deleteStmt,
|
|
||||||
Insert: insertStmt,
|
|
||||||
Select: selectStmt,
|
|
||||||
Update: updateStmt,
|
|
||||||
}, []byte(env.SessionSecret))
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
//go:build ignore
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"text/template"
|
|
||||||
)
|
|
||||||
|
|
||||||
var initTemplate = template.Must(template.New("").Parse("// Code generated DO NOT EDIT.\n" +
|
|
||||||
"package db\n" +
|
|
||||||
"\n" +
|
|
||||||
"const initQuery = `{{.}}`\n",
|
|
||||||
))
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
initStmt, err := os.ReadFile("./init.sql")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err := os.Create("./init_sql.go")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
initTemplate.Execute(f, string(initStmt))
|
|
||||||
}
|
|
||||||
|
|
@ -1,117 +0,0 @@
|
||||||
package db
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"lishwist/http/normalize"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Group struct {
|
|
||||||
Id string
|
|
||||||
Name string
|
|
||||||
Reference string
|
|
||||||
Members []User
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *Group) MemberIndex(userId string) int {
|
|
||||||
for i, u := range g.Members {
|
|
||||||
if u.Id == userId {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
func queryManyGroups(query string, args ...any) ([]Group, error) {
|
|
||||||
groups := []Group{}
|
|
||||||
rows, err := database.Query(query, args...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Query failed: %w", err)
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
for rows.Next() {
|
|
||||||
var group Group
|
|
||||||
err := rows.Scan(&group.Id, &group.Name, &group.Reference)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to scan row: %w", err)
|
|
||||||
}
|
|
||||||
members, err := queryManyGroupMembers(group.Id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to query for group members: %w", err)
|
|
||||||
}
|
|
||||||
group.Members = members
|
|
||||||
groups = append(groups, group)
|
|
||||||
}
|
|
||||||
err = rows.Err()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Rows error: %w", err)
|
|
||||||
}
|
|
||||||
return groups, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func queryOneGroup(query string, args ...any) (*Group, error) {
|
|
||||||
groups, err := queryManyGroups(query, args...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(groups) < 1 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return &groups[0], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func queryManyGroupMembers(groupId string) ([]User, error) {
|
|
||||||
query := "SELECT user.id, user.name, user.display_name, user.reference, user.is_admin, user.is_live FROM v_user AS user JOIN group_member ON group_member.user_id = user.id JOIN [group] ON [group].id = group_member.group_id WHERE [group].id = ? ORDER BY group_member.user_id"
|
|
||||||
members, err := queryManyUsers(query, groupId)
|
|
||||||
if err != nil {
|
|
||||||
return members, fmt.Errorf("Failed to get members: %w", err)
|
|
||||||
}
|
|
||||||
return members, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetGroupByReference(reference string) (*Group, error) {
|
|
||||||
query := "SELECT [group].id, [group].name, [group].reference FROM [group] WHERE [group].reference = ?"
|
|
||||||
return queryOneGroup(query, reference)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetAllGroups() ([]Group, error) {
|
|
||||||
query := "SELECT id, name, reference FROM [group];"
|
|
||||||
return queryManyGroups(query)
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateGroup(name string, reference string) (*Group, error) {
|
|
||||||
name = normalize.Trim(name)
|
|
||||||
stmt := "INSERT INTO [group] (name, reference) VALUES (?, ?)"
|
|
||||||
result, err := database.Exec(stmt, name, reference)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
id, err := result.LastInsertId()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
group := Group{
|
|
||||||
Id: strconv.FormatInt(id, 10),
|
|
||||||
Name: name,
|
|
||||||
Reference: reference,
|
|
||||||
}
|
|
||||||
return &group, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *Group) AddUser(userId string) error {
|
|
||||||
stmt := "INSERT INTO group_member (group_id, user_id) VALUES (?, ?)"
|
|
||||||
_, err := database.Exec(stmt, g.Id, userId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *Group) RemoveUser(userId string) error {
|
|
||||||
stmt := "DELETE FROM group_member WHERE group_id = ? AND user_id = ?"
|
|
||||||
_, err := database.Exec(stmt, g.Id, userId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
BEGIN TRANSACTION;
|
|
||||||
CREATE TABLE IF NOT EXISTS "user" (
|
|
||||||
"id" INTEGER NOT NULL UNIQUE,
|
|
||||||
"name" TEXT NOT NULL UNIQUE,
|
|
||||||
"display_name" TEXT NOT NULL UNIQUE,
|
|
||||||
"reference" TEXT NOT NULL UNIQUE,
|
|
||||||
"motto" TEXT NOT NULL DEFAULT "",
|
|
||||||
"password_hash" TEXT NOT NULL,
|
|
||||||
"is_admin" INTEGER NOT NULL DEFAULT 0,
|
|
||||||
"is_live" INTEGER NOT NULL DEFAULT 1,
|
|
||||||
PRIMARY KEY("id" AUTOINCREMENT)
|
|
||||||
);
|
|
||||||
CREATE TABLE IF NOT EXISTS "gift" (
|
|
||||||
"id" INTEGER NOT NULL UNIQUE,
|
|
||||||
"name" TEXT NOT NULL,
|
|
||||||
"recipient_id" INTEGER NOT NULL,
|
|
||||||
"claimant_id" INTEGER,
|
|
||||||
"creator_id" INTEGER NOT NULL,
|
|
||||||
"sent" INTEGER NOT NULL DEFAULT 0,
|
|
||||||
PRIMARY KEY("id" AUTOINCREMENT),
|
|
||||||
FOREIGN KEY("recipient_id") REFERENCES "user"("id"),
|
|
||||||
FOREIGN KEY("creator_id") REFERENCES "user"("id"),
|
|
||||||
FOREIGN KEY("claimant_id") REFERENCES "user"("id")
|
|
||||||
);
|
|
||||||
CREATE TABLE IF NOT EXISTS "group" (
|
|
||||||
"id" INTEGER NOT NULL UNIQUE,
|
|
||||||
"name" TEXT NOT NULL UNIQUE,
|
|
||||||
"reference" TEXT NOT NULL UNIQUE,
|
|
||||||
PRIMARY KEY("id" AUTOINCREMENT)
|
|
||||||
);
|
|
||||||
CREATE TABLE IF NOT EXISTS "group_member" (
|
|
||||||
"group_id" INTEGER NOT NULL,
|
|
||||||
"user_id" INTEGER NOT NULL,
|
|
||||||
UNIQUE("user_id","group_id"),
|
|
||||||
FOREIGN KEY("group_id") REFERENCES "group"("id"),
|
|
||||||
FOREIGN KEY("user_id") REFERENCES "user"("id")
|
|
||||||
);
|
|
||||||
CREATE TABLE IF NOT EXISTS "session" (
|
|
||||||
"id" INTEGER NOT NULL UNIQUE,
|
|
||||||
"value" TEXT NOT NULL,
|
|
||||||
PRIMARY KEY("id" AUTOINCREMENT)
|
|
||||||
);
|
|
||||||
|
|
||||||
DROP VIEW IF EXISTS "v_user";
|
|
||||||
CREATE VIEW "v_user"
|
|
||||||
AS
|
|
||||||
SELECT * FROM user WHERE user.is_live = 1;
|
|
||||||
|
|
||||||
-- DROP VIEW IF EXISTS "v_wish";
|
|
||||||
-- CREATE VIEW "v_wish"
|
|
||||||
-- AS
|
|
||||||
-- SELECT gift.id, gift.name, gift.sent FROM gift JOIN user AS recipient;
|
|
||||||
|
|
||||||
COMMIT;
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
BEGIN TRANSACTION;
|
|
||||||
|
|
||||||
ALTER TABLE user ADD COLUMN "is_live" INTEGER NOT NULL DEFAULT 1;
|
|
||||||
|
|
||||||
ALTER TABLE user RENAME TO old_user;
|
|
||||||
|
|
||||||
CREATE TABLE "user" (
|
|
||||||
"id" INTEGER NOT NULL UNIQUE,
|
|
||||||
"name" TEXT NOT NULL UNIQUE,
|
|
||||||
"reference" TEXT NOT NULL UNIQUE,
|
|
||||||
"motto" TEXT NOT NULL DEFAULT "",
|
|
||||||
"password_hash" TEXT NOT NULL,
|
|
||||||
"is_admin" INTEGER NOT NULL DEFAULT 0,
|
|
||||||
"is_live" INTEGER NOT NULL DEFAULT 1,
|
|
||||||
PRIMARY KEY("id" AUTOINCREMENT)
|
|
||||||
);
|
|
||||||
|
|
||||||
INSERT INTO user SELECT * FROM old_user;
|
|
||||||
|
|
||||||
DROP TABLE "old_user";
|
|
||||||
|
|
||||||
COMMIT;
|
|
||||||
|
|
@ -1,419 +0,0 @@
|
||||||
package db
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"fmt"
|
|
||||||
"lishwist/http/normalize"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
type User struct {
|
|
||||||
Id string
|
|
||||||
NormalName string
|
|
||||||
Name string
|
|
||||||
Reference string
|
|
||||||
IsAdmin bool
|
|
||||||
IsLive bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type Gift struct {
|
|
||||||
Id string
|
|
||||||
Name string
|
|
||||||
ClaimantId string `json:",omitempty"`
|
|
||||||
ClaimantName string `json:",omitempty"`
|
|
||||||
Sent bool
|
|
||||||
RecipientId string `json:",omitempty"`
|
|
||||||
RecipientName string `json:",omitempty"`
|
|
||||||
RecipientRef string `json:",omitempty"`
|
|
||||||
CreatorId string `json:",omitempty"`
|
|
||||||
CreatorName string `json:",omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func queryManyUsers(query string, args ...any) ([]User, error) {
|
|
||||||
rows, err := database.Query(query, args...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
users := []User{}
|
|
||||||
for rows.Next() {
|
|
||||||
var u User
|
|
||||||
err = rows.Scan(&u.Id, &u.NormalName, &u.Name, &u.Reference, &u.IsAdmin, &u.IsLive)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
users = append(users, u)
|
|
||||||
}
|
|
||||||
err = rows.Err()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return users, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func queryOneUser(query string, args ...any) (*User, error) {
|
|
||||||
users, err := queryManyUsers(query, args...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(users) < 1 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return &users[0], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetAllUsers() ([]User, error) {
|
|
||||||
stmt := "SELECT id, name, display_name, reference, is_admin, is_live FROM user"
|
|
||||||
return queryManyUsers(stmt)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetUser(id string) (*User, error) {
|
|
||||||
stmt := "SELECT id, name, display_name, reference, is_admin, is_live FROM v_user WHERE id = ?"
|
|
||||||
return queryOneUser(stmt, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetUserByName(username string) (*User, error) {
|
|
||||||
username = normalize.Name(username)
|
|
||||||
stmt := "SELECT id, name, display_name, reference, is_admin, is_live FROM v_user WHERE name = ?"
|
|
||||||
return queryOneUser(stmt, username)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetUserByReference(reference string) (*User, error) {
|
|
||||||
stmt := "SELECT id, name, display_name, reference, is_admin, is_live FROM v_user WHERE reference = ?"
|
|
||||||
return queryOneUser(stmt, reference)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetAnyUserByReference(reference string) (*User, error) {
|
|
||||||
stmt := "SELECT id, name, display_name, reference, is_admin, is_live FROM user WHERE reference = ?"
|
|
||||||
return queryOneUser(stmt, reference)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) SetLive(setting bool) error {
|
|
||||||
query := "UPDATE user SET is_live = ? WHERE reference = ?"
|
|
||||||
_, err := database.Exec(query, setting, u.Reference)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
u.IsLive = setting
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func CreateUser(name string, passHash []byte) (*User, error) {
|
|
||||||
username := normalize.Name(name)
|
|
||||||
stmt := "INSERT INTO user (name, display_name, reference, password_hash) VALUES (?, ?, ?, ?)"
|
|
||||||
reference, err := uuid.NewRandom()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
result, err := database.Exec(stmt, username, name, reference, passHash)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
id, err := result.LastInsertId()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
user := User{
|
|
||||||
Id: fmt.Sprintf("%d", id),
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
return &user, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) GetPassHash() ([]byte, error) {
|
|
||||||
stmt := "SELECT password_hash FROM v_user WHERE id = ?"
|
|
||||||
var passHash string
|
|
||||||
err := database.QueryRow(stmt, u.Id).Scan(&passHash)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return []byte(passHash), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) CountGifts() (int, error) {
|
|
||||||
stmt := "SELECT COUNT(gift.id) AS gift_count FROM gift WHERE gift.creator_id = ?1 AND gift.recipient_id = ?1"
|
|
||||||
var giftCount int
|
|
||||||
err := database.QueryRow(stmt, u.Id).Scan(&giftCount)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return giftCount, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) GetGifts() ([]Gift, error) {
|
|
||||||
stmt := "SELECT gift.id, gift.name, gift.sent FROM gift WHERE gift.creator_id = ?1 AND gift.recipient_id = ?1"
|
|
||||||
rows, err := database.Query(stmt, u.Id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
gifts := []Gift{}
|
|
||||||
for rows.Next() {
|
|
||||||
var id string
|
|
||||||
var name string
|
|
||||||
var sent bool
|
|
||||||
err = rows.Scan(&id, &name, &sent)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
gift := Gift{
|
|
||||||
Id: id,
|
|
||||||
Name: name,
|
|
||||||
Sent: sent,
|
|
||||||
}
|
|
||||||
gifts = append(gifts, gift)
|
|
||||||
}
|
|
||||||
err = rows.Err()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return gifts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) GetOtherUserGifts(otherUserReference string) ([]Gift, error) {
|
|
||||||
otherUser, err := GetUserByReference(otherUserReference)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to get other user: %w", err)
|
|
||||||
}
|
|
||||||
if otherUser.Id == u.Id {
|
|
||||||
return nil, fmt.Errorf("Not allowed to view own foreign wishlist")
|
|
||||||
}
|
|
||||||
stmt := "SELECT gift.id, gift.name, claimant.id, claimant.name, gift.sent, gift.creator_id, creator.name, gift.recipient_id FROM gift JOIN v_user AS user ON gift.recipient_id = user.id LEFT JOIN v_user AS claimant ON gift.claimant_id = claimant.id LEFT JOIN v_user AS creator ON gift.creator_id = creator.id WHERE user.id = ?"
|
|
||||||
rows, err := database.Query(stmt, otherUser.Id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to execute query: %w", err)
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
gifts := []Gift{}
|
|
||||||
for rows.Next() {
|
|
||||||
var id string
|
|
||||||
var name string
|
|
||||||
var claimantId sql.NullString
|
|
||||||
var claimantName sql.NullString
|
|
||||||
var sent bool
|
|
||||||
var creatorId string
|
|
||||||
var creatorName string
|
|
||||||
var recipientId string
|
|
||||||
err = rows.Scan(&id, &name, &claimantId, &claimantName, &sent, &creatorId, &creatorName, &recipientId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to scan row: %w", err)
|
|
||||||
}
|
|
||||||
gift := Gift{
|
|
||||||
Id: id,
|
|
||||||
Name: name,
|
|
||||||
ClaimantId: claimantId.String,
|
|
||||||
ClaimantName: claimantName.String,
|
|
||||||
Sent: sent,
|
|
||||||
CreatorId: creatorId,
|
|
||||||
CreatorName: creatorName,
|
|
||||||
RecipientId: recipientId,
|
|
||||||
}
|
|
||||||
gifts = append(gifts, gift)
|
|
||||||
}
|
|
||||||
err = rows.Err()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Rows returned an error: %w", err)
|
|
||||||
}
|
|
||||||
return gifts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) GetTodo() ([]Gift, error) {
|
|
||||||
stmt := "SELECT gift.id, gift.name, gift.sent, recipient.name, recipient.reference FROM gift JOIN v_user AS user ON gift.claimant_id = user.id JOIN v_user AS recipient ON gift.recipient_id = recipient.id WHERE user.id = ? ORDER BY gift.sent ASC, gift.name"
|
|
||||||
rows, err := database.Query(stmt, u.Id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
gifts := []Gift{}
|
|
||||||
for rows.Next() {
|
|
||||||
var id string
|
|
||||||
var name string
|
|
||||||
var sent bool
|
|
||||||
var recipientName string
|
|
||||||
var recipientRef string
|
|
||||||
_ = rows.Scan(&id, &name, &sent, &recipientName, &recipientRef)
|
|
||||||
gift := Gift{
|
|
||||||
Id: id,
|
|
||||||
Name: name,
|
|
||||||
Sent: sent,
|
|
||||||
RecipientName: recipientName,
|
|
||||||
RecipientRef: recipientRef,
|
|
||||||
}
|
|
||||||
gifts = append(gifts, gift)
|
|
||||||
}
|
|
||||||
err = rows.Err()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return gifts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) AddGift(name string) error {
|
|
||||||
stmt := "INSERT INTO gift (name, recipient_id, creator_id) VALUES (?, ?, ?)"
|
|
||||||
_, err := database.Exec(stmt, name, u.Id, u.Id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) deleteGifts(tx *sql.Tx, ids []string) error {
|
|
||||||
stmt := "DELETE FROM gift WHERE gift.creator_id = ? AND gift.id = ?"
|
|
||||||
for _, id := range ids {
|
|
||||||
r, err := tx.Exec(stmt, u.Id, id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rE, err := r.RowsAffected()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if rE < 1 {
|
|
||||||
return fmt.Errorf("Gift deletion failed for '%s'", id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) RemoveGifts(ids ...string) error {
|
|
||||||
if len(ids) < 1 {
|
|
||||||
return fmt.Errorf("Attempt to remove zero gifts")
|
|
||||||
}
|
|
||||||
|
|
||||||
tx, err := database.Begin()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = u.deleteGifts(tx, ids)
|
|
||||||
if err != nil {
|
|
||||||
rollBackErr := tx.Rollback()
|
|
||||||
if rollBackErr != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = tx.Commit()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) executeClaims(tx *sql.Tx, claims, unclaims []string) error {
|
|
||||||
claimStmt := "UPDATE gift SET claimant_id = ? WHERE id = ?"
|
|
||||||
unclaimStmt := "UPDATE gift SET claimant_id = NULL WHERE id = ?"
|
|
||||||
for _, id := range claims {
|
|
||||||
r, err := tx.Exec(claimStmt, u.Id, id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rE, err := r.RowsAffected()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if rE < 1 {
|
|
||||||
return fmt.Errorf("Gift claim failed for '%s'", id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, id := range unclaims {
|
|
||||||
r, err := tx.Exec(unclaimStmt, id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rE, err := r.RowsAffected()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if rE < 1 {
|
|
||||||
return fmt.Errorf("Gift unclaim failed for '%s'", id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) ClaimGifts(claims, unclaims []string) error {
|
|
||||||
if len(claims) < 1 && len(unclaims) < 1 {
|
|
||||||
return fmt.Errorf("Attempt to claim/unclaim zero gifts")
|
|
||||||
}
|
|
||||||
|
|
||||||
tx, err := database.Begin()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = u.executeClaims(tx, claims, unclaims)
|
|
||||||
if err != nil {
|
|
||||||
rollBackErr := tx.Rollback()
|
|
||||||
if rollBackErr != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = tx.Commit()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) executeCompletions(tx *sql.Tx, claims []string) error {
|
|
||||||
claimStmt := "UPDATE gift SET sent = 1 WHERE id = ?"
|
|
||||||
for _, id := range claims {
|
|
||||||
r, err := tx.Exec(claimStmt, id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rE, err := r.RowsAffected()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if rE < 1 {
|
|
||||||
return fmt.Errorf("Gift completion failed for '%s'", id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) CompleteGifts(claims []string) error {
|
|
||||||
if len(claims) < 1 {
|
|
||||||
return fmt.Errorf("Attempt to complete zero gifts")
|
|
||||||
}
|
|
||||||
|
|
||||||
tx, err := database.Begin()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = u.executeCompletions(tx, claims)
|
|
||||||
if err != nil {
|
|
||||||
rollBackErr := tx.Rollback()
|
|
||||||
if rollBackErr != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = tx.Commit()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) AddGiftToUser(otherUserReference string, giftName string) error {
|
|
||||||
otherUser, err := GetUserByReference(otherUserReference)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
stmt := "INSERT INTO gift (name, recipient_id, creator_id) VALUES (?, ?, ?)"
|
|
||||||
_, err = database.Exec(stmt, giftName, otherUser.Id, u.Id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) GetGroups() ([]Group, error) {
|
|
||||||
stmt := "SELECT [group].id, [group].name, [group].reference FROM [group] JOIN group_member ON group_member.group_id = [group].id JOIN v_user AS user ON user.id = group_member.user_id WHERE user.id = ?"
|
|
||||||
return queryManyGroups(stmt, u.Id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *User) GetGroupByReference(reference string) (*Group, error) {
|
|
||||||
stmt := "SELECT [group].id, [group].name, [group].reference FROM [group] JOIN group_member ON [group].id == group_member.group_id WHERE [group].reference = ? AND group_member.user_id = ?"
|
|
||||||
return queryOneGroup(stmt, reference, u.Id)
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"lishwist/http/api/db"
|
|
||||||
"lishwist/http/templates"
|
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
lishwist "lishwist/core"
|
||||||
|
"lishwist/http/templates"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LoginProps struct {
|
type LoginProps struct {
|
||||||
|
|
@ -55,31 +54,19 @@ func Login(username, password string) *LoginProps {
|
||||||
return props
|
return props
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := db.GetUserByName(username)
|
_, err := lishwist.Login(props.Username.Value, props.Password.Value)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
log.Printf("Failed to fetch user: %s\n", err)
|
return nil
|
||||||
props.GeneralError = "Username or password invalid"
|
|
||||||
return props
|
|
||||||
}
|
|
||||||
if user == nil {
|
|
||||||
log.Printf("User not found by name: %q\n", username)
|
|
||||||
props.GeneralError = "Username or password invalid"
|
|
||||||
return props
|
|
||||||
}
|
}
|
||||||
|
|
||||||
passHash, err := user.GetPassHash()
|
switch err.(type) {
|
||||||
if err != nil {
|
case lishwist.ErrorInvalidCredentials:
|
||||||
log.Println("Failed to get password hash: " + err.Error())
|
log.Printf("Invalid credentials: %w\n", err)
|
||||||
props.GeneralError = "Something went wrong. Error code: Momo"
|
|
||||||
return props
|
|
||||||
}
|
|
||||||
|
|
||||||
err = bcrypt.CompareHashAndPassword(passHash, []byte(password))
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Username or password invalid: " + err.Error())
|
|
||||||
props.GeneralError = "Username or password invalid"
|
props.GeneralError = "Username or password invalid"
|
||||||
return props
|
return props
|
||||||
|
default:
|
||||||
|
log.Printf("Login error: %w\n", err)
|
||||||
|
props.GeneralError = "Something went wrong."
|
||||||
|
return props
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,7 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
|
|
||||||
"lishwist/http/api/db"
|
|
||||||
"lishwist/http/templates"
|
"lishwist/http/templates"
|
||||||
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type RegisterProps struct {
|
type RegisterProps struct {
|
||||||
|
|
@ -64,37 +59,37 @@ func NewRegisterProps(usernameVal, passwordVal, confirmPassVal string) *Register
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Register(username, newPassword, confirmPassword string) *RegisterProps {
|
// func Register(username, newPassword, confirmPassword string) *RegisterProps {
|
||||||
props := NewRegisterProps(username, newPassword, confirmPassword)
|
// props := NewRegisterProps(username, newPassword, confirmPassword)
|
||||||
|
|
||||||
valid := props.Validate()
|
// valid := props.Validate()
|
||||||
props.Password.Value = ""
|
// props.Password.Value = ""
|
||||||
props.ConfirmPassword.Value = ""
|
// props.ConfirmPassword.Value = ""
|
||||||
if !valid {
|
// if !valid {
|
||||||
log.Printf("Invalid props: %#v\n", props)
|
// log.Printf("Invalid props: %#v\n", props)
|
||||||
return props
|
// return props
|
||||||
}
|
// }
|
||||||
|
|
||||||
existingUser, _ := db.GetUserByName(username)
|
// existingUser, _ := db.GetUserByName(username)
|
||||||
if existingUser != nil {
|
// if existingUser != nil {
|
||||||
log.Printf("Username is taken: %q\n", existingUser.NormalName)
|
// log.Printf("Username is taken: %q\n", existingUser.NormalName)
|
||||||
props.Username.Error = "Username is taken"
|
// props.Username.Error = "Username is taken"
|
||||||
return props
|
// return props
|
||||||
}
|
// }
|
||||||
|
|
||||||
hashedPasswordBytes, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.MinCost)
|
// hashedPasswordBytes, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.MinCost)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Printf("Failed to hash password: %s\n", err)
|
// log.Printf("Failed to hash password: %s\n", err)
|
||||||
props.GeneralError = "Something went wrong. Error code: Aang"
|
// props.GeneralError = "Something went wrong. Error code: Aang"
|
||||||
return props
|
// return props
|
||||||
}
|
// }
|
||||||
|
|
||||||
_, err = db.CreateUser(username, hashedPasswordBytes)
|
// _, err = db.CreateUser(username, hashedPasswordBytes)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
log.Printf("Failed to create user: %s\n", err)
|
// log.Printf("Failed to create user: %s\n", err)
|
||||||
props.GeneralError = "Something went wrong. Error code: Ozai"
|
// props.GeneralError = "Something went wrong. Error code: Ozai"
|
||||||
return props
|
// return props
|
||||||
}
|
// }
|
||||||
|
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
|
|
|
||||||
40
http/main.go
40
http/main.go
|
|
@ -5,9 +5,9 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
lishwist "lishwist/core"
|
||||||
|
"lishwist/core/session"
|
||||||
"lishwist/http/api"
|
"lishwist/http/api"
|
||||||
// TODO: lishwist/http/api/db ought not to be used outside lishwist/http/api
|
|
||||||
"lishwist/http/api/db"
|
|
||||||
"lishwist/http/env"
|
"lishwist/http/env"
|
||||||
"lishwist/http/router"
|
"lishwist/http/router"
|
||||||
"lishwist/http/routing"
|
"lishwist/http/routing"
|
||||||
|
|
@ -17,16 +17,12 @@ func main() {
|
||||||
gob.Register(&api.RegisterProps{})
|
gob.Register(&api.RegisterProps{})
|
||||||
gob.Register(&api.LoginProps{})
|
gob.Register(&api.LoginProps{})
|
||||||
|
|
||||||
err := db.Open()
|
err := lishwist.Init(env.DatabaseFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to open DB: %s\n", err)
|
log.Fatalf("Failed to init Lishwist: %s\n", err)
|
||||||
}
|
|
||||||
err = db.Init()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to init DB: %s\n", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
store, err := db.NewSessionStore()
|
store, err := session.NewStore([]byte(env.SessionSecret))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to initialize session store: %s\n", err)
|
log.Fatalf("Failed to initialize session store: %s\n", err)
|
||||||
}
|
}
|
||||||
|
|
@ -38,27 +34,29 @@ func main() {
|
||||||
|
|
||||||
r.Public.HandleFunc("GET /", routing.Login)
|
r.Public.HandleFunc("GET /", routing.Login)
|
||||||
r.Public.HandleFunc("GET /groups/{groupReference}", routing.PublicGroup)
|
r.Public.HandleFunc("GET /groups/{groupReference}", routing.PublicGroup)
|
||||||
r.Public.HandleFunc("GET /list/{userReference}", routing.PublicWishlist)
|
r.Public.HandleFunc("GET /lists/{userReference}", routing.PublicWishlist)
|
||||||
r.Public.HandleFunc("GET /register", routing.Register)
|
r.Public.HandleFunc("GET /register", routing.Register)
|
||||||
r.Public.HandleFunc("POST /", routing.LoginPost)
|
r.Public.HandleFunc("POST /", routing.LoginPost)
|
||||||
r.Public.HandleFunc("POST /register", routing.RegisterPost)
|
r.Public.HandleFunc("POST /register", routing.RegisterPost)
|
||||||
|
|
||||||
r.Private.HandleFunc("GET /", routing.NotFound)
|
r.Private.HandleFunc("GET /", routing.NotFound)
|
||||||
r.Private.HandleFunc("GET /groups", routing.ExpectUser(routing.Groups))
|
r.Private.HandleFunc("GET /groups", routing.ExpectAppSession(routing.Groups))
|
||||||
r.Private.HandleFunc("GET /groups/{groupReference}", routing.ExpectUser(routing.Group))
|
r.Private.HandleFunc("GET /groups/{groupReference}", routing.ExpectAppSession(routing.Group))
|
||||||
r.Private.HandleFunc("GET /list/{userReference}", routing.ExpectUser(routing.ForeignWishlist))
|
r.Private.HandleFunc("GET /lists/{userReference}", routing.ExpectAppSession(routing.ForeignWishlist))
|
||||||
r.Private.HandleFunc("GET /users", routing.ExpectUser(routing.Users))
|
r.Private.HandleFunc("GET /users", routing.ExpectAppSession(routing.Users))
|
||||||
r.Private.HandleFunc("GET /users/{userReference}", routing.ExpectUser(routing.User))
|
r.Private.HandleFunc("GET /users/{userReference}", routing.ExpectAppSession(routing.User))
|
||||||
r.Private.HandleFunc("GET /{$}", routing.ExpectUser(routing.Home))
|
r.Private.HandleFunc("GET /{$}", routing.ExpectAppSession(routing.Home))
|
||||||
r.Private.HandleFunc("POST /groups/{groupReference}", routing.ExpectUser(routing.GroupPost))
|
r.Private.HandleFunc("POST /groups/{groupReference}", routing.ExpectAppSession(routing.GroupPost))
|
||||||
r.Private.HandleFunc("POST /list/{userReference}", routing.ExpectUser(routing.ForeignWishlistPost))
|
r.Private.HandleFunc("POST /list/{userReference}", routing.ExpectAppSession(routing.ForeignWishlistPost))
|
||||||
r.Private.HandleFunc("POST /logout", routing.LogoutPost)
|
r.Private.HandleFunc("POST /logout", routing.LogoutPost)
|
||||||
r.Private.HandleFunc("POST /users/{userReference}", routing.ExpectUser(routing.UserPost))
|
r.Private.HandleFunc("POST /users/{userReference}", routing.ExpectAppSession(routing.UserPost))
|
||||||
r.Private.HandleFunc("POST /{$}", routing.ExpectUser(routing.HomePost))
|
r.Private.HandleFunc("POST /{$}", routing.ExpectAppSession(routing.HomePost))
|
||||||
|
|
||||||
// Deprecated
|
// Deprecated
|
||||||
|
r.Private.HandleFunc("GET /group/{groupReference}", routing.ExpectAppSession(routing.Group))
|
||||||
|
r.Private.HandleFunc("GET /list/{userReference}", routing.ExpectAppSession(routing.ForeignWishlist))
|
||||||
r.Public.HandleFunc("GET /group/{groupReference}", routing.PublicGroup)
|
r.Public.HandleFunc("GET /group/{groupReference}", routing.PublicGroup)
|
||||||
r.Private.HandleFunc("GET /group/{groupReference}", routing.ExpectUser(routing.Group))
|
r.Public.HandleFunc("GET /list/{userReference}", routing.PublicWishlist)
|
||||||
|
|
||||||
http.Handle("/", r)
|
http.Handle("/", r)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
package routing
|
package routing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"lishwist/http/api/db"
|
lishwist "lishwist/core"
|
||||||
"lishwist/http/rsvp"
|
"lishwist/http/rsvp"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ExpectUser(next func(*db.User, http.Header, *rsvp.Request) rsvp.Response) rsvp.HandlerFunc {
|
func ExpectAppSession(next func(*lishwist.Session, http.Header, *rsvp.Request) rsvp.Response) rsvp.HandlerFunc {
|
||||||
return func(w http.Header, r *rsvp.Request) rsvp.Response {
|
return func(w http.Header, r *rsvp.Request) rsvp.Response {
|
||||||
session := r.GetSession()
|
session := r.GetSession()
|
||||||
username, ok := session.GetValue("username").(string)
|
username, ok := session.GetValue("username").(string)
|
||||||
|
|
@ -14,11 +14,11 @@ func ExpectUser(next func(*db.User, http.Header, *rsvp.Request) rsvp.Response) r
|
||||||
return rsvp.Error(http.StatusInternalServerError, "Something went wrong.").Log("Failed to get username from session")
|
return rsvp.Error(http.StatusInternalServerError, "Something went wrong.").Log("Failed to get username from session")
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := db.GetUserByName(username)
|
appSession, err := lishwist.SessionFromUsername(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "Something went wrong.").Log("Failed to get user %q: %s", username, err)
|
return rsvp.Error(http.StatusInternalServerError, "Something went wrong.").Log("Failed to get session by username %q: %s", username, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return next(user, w, r)
|
return next(appSession, w, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package routing
|
package routing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"lishwist/http/api/db"
|
lishwist "lishwist/core"
|
||||||
"lishwist/http/rsvp"
|
"lishwist/http/rsvp"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
@ -10,26 +10,26 @@ type foreignWishlistProps struct {
|
||||||
CurrentUserId string
|
CurrentUserId string
|
||||||
CurrentUserName string
|
CurrentUserName string
|
||||||
Username string
|
Username string
|
||||||
Gifts []db.Gift
|
Gifts []lishwist.Wish
|
||||||
}
|
}
|
||||||
|
|
||||||
func ForeignWishlist(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
|
func ForeignWishlist(app *lishwist.Session, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||||
userReference := r.PathValue("userReference")
|
userReference := r.PathValue("userReference")
|
||||||
if currentUser.Reference == userReference {
|
if app.User.Reference == userReference {
|
||||||
return rsvp.SeeOther("/")
|
return rsvp.SeeOther("/")
|
||||||
}
|
}
|
||||||
otherUser, err := db.GetUserByReference(userReference)
|
otherUser, err := lishwist.GetUserByReference(userReference)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching this user :(").Log("Couldn't get user by reference %q: %s", userReference, err)
|
return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching this user :(").Log("Couldn't get user by reference %q: %s", userReference, err)
|
||||||
}
|
}
|
||||||
if otherUser == nil {
|
if otherUser == nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "User not found")
|
return rsvp.Error(http.StatusInternalServerError, "User not found")
|
||||||
}
|
}
|
||||||
gifts, err := currentUser.GetOtherUserGifts(userReference)
|
wishes, err := app.GetOthersWishes(userReference)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching this user :(").Log("%q couldn't get wishes of other user %q: %s", currentUser.Name, otherUser.Name, err)
|
return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching this user :(").Log("%q couldn't get wishes of other user %q: %s", app.User.Name, otherUser.Name, err)
|
||||||
}
|
}
|
||||||
p := foreignWishlistProps{CurrentUserId: currentUser.Id, CurrentUserName: currentUser.Name, Username: otherUser.Name, Gifts: gifts}
|
p := foreignWishlistProps{CurrentUserId: app.User.Id, CurrentUserName: app.User.Name, Username: otherUser.Name, Gifts: wishes}
|
||||||
return rsvp.Data("foreign_wishlist.gotmpl", p)
|
return rsvp.Data("foreign_wishlist.gotmpl", p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,14 +40,14 @@ type publicWishlistProps struct {
|
||||||
|
|
||||||
func PublicWishlist(h http.Header, r *rsvp.Request) rsvp.Response {
|
func PublicWishlist(h http.Header, r *rsvp.Request) rsvp.Response {
|
||||||
userReference := r.PathValue("userReference")
|
userReference := r.PathValue("userReference")
|
||||||
otherUser, err := db.GetUserByReference(userReference)
|
otherUser, err := lishwist.GetUserByReference(userReference)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching this user :(").Log("Couldn't get user by reference %q on public wishlist: %s", userReference, err)
|
return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching this user :(").Log("Couldn't get user by reference %q on public wishlist: %s", userReference, err)
|
||||||
}
|
}
|
||||||
if otherUser == nil {
|
if otherUser == nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "User not found")
|
return rsvp.Error(http.StatusInternalServerError, "User not found")
|
||||||
}
|
}
|
||||||
giftCount, err := otherUser.CountGifts()
|
giftCount, err := otherUser.WishCount()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching this user :(").Log("Couldn't get wishes of user %q on public wishlist: %s", otherUser.Name, err)
|
return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching this user :(").Log("Couldn't get wishes of user %q on public wishlist: %s", otherUser.Name, err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,59 +4,59 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
"lishwist/http/api/db"
|
lishwist "lishwist/core"
|
||||||
"lishwist/http/rsvp"
|
"lishwist/http/rsvp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GroupProps struct {
|
type GroupProps struct {
|
||||||
Group *db.Group
|
Group *lishwist.Group
|
||||||
CurrentUsername string
|
CurrentUsername string
|
||||||
}
|
}
|
||||||
|
|
||||||
func AdminGroup(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
|
func AdminGroup(app *lishwist.Session, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||||
reference := r.PathValue("groupReference")
|
reference := r.PathValue("groupReference")
|
||||||
group, err := db.GetGroupByReference(reference)
|
group, err := app.GetGroupByReference(reference)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "Couldn't get group: %s", err)
|
return rsvp.Error(http.StatusInternalServerError, "Couldn't get group: %s", err)
|
||||||
}
|
}
|
||||||
if group == nil {
|
if group == nil {
|
||||||
return rsvp.Error(http.StatusNotFound, "Group not found")
|
return rsvp.Error(http.StatusNotFound, "Group not found")
|
||||||
}
|
}
|
||||||
if !currentUser.IsAdmin {
|
if !app.User.IsAdmin {
|
||||||
index := group.MemberIndex(currentUser.Id)
|
index := group.MemberIndex(app.User.Id)
|
||||||
group.Members = slices.Delete(group.Members, index, index+1)
|
group.Members = slices.Delete(group.Members, index, index+1)
|
||||||
}
|
}
|
||||||
p := GroupProps{
|
p := GroupProps{
|
||||||
Group: group,
|
Group: group,
|
||||||
CurrentUsername: currentUser.Name,
|
CurrentUsername: app.User.Name,
|
||||||
}
|
}
|
||||||
return rsvp.Data("group_page.gotmpl", p)
|
return rsvp.Data("group_page.gotmpl", p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Group(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
|
func Group(app *lishwist.Session, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||||
if currentUser.IsAdmin {
|
if app.User.IsAdmin {
|
||||||
return AdminGroup(currentUser, h, r)
|
return AdminGroup(app, h, r)
|
||||||
}
|
}
|
||||||
groupReference := r.PathValue("groupReference")
|
groupReference := r.PathValue("groupReference")
|
||||||
group, err := currentUser.GetGroupByReference(groupReference)
|
group, err := app.GetGroupByReference(groupReference)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching this group :(").Log("Couldn't get group: %s", err)
|
return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching this group :(").Log("Couldn't get group: %s", err)
|
||||||
}
|
}
|
||||||
if group == nil {
|
if group == nil {
|
||||||
return rsvp.Error(http.StatusNotFound, "Group not found. (It might be because you're not a member)")
|
return rsvp.Error(http.StatusNotFound, "Group not found. (It might be because you're not a member)")
|
||||||
}
|
}
|
||||||
index := group.MemberIndex(currentUser.Id)
|
index := group.MemberIndex(app.User.Id)
|
||||||
group.Members = slices.Delete(group.Members, index, index+1)
|
group.Members = slices.Delete(group.Members, index, index+1)
|
||||||
p := GroupProps{
|
p := GroupProps{
|
||||||
Group: group,
|
Group: group,
|
||||||
CurrentUsername: currentUser.Name,
|
CurrentUsername: app.User.Name,
|
||||||
}
|
}
|
||||||
return rsvp.Data("group_page.gotmpl", p)
|
return rsvp.Data("group_page.gotmpl", p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func PublicGroup(h http.Header, r *rsvp.Request) rsvp.Response {
|
func PublicGroup(h http.Header, r *rsvp.Request) rsvp.Response {
|
||||||
groupReference := r.PathValue("groupReference")
|
groupReference := r.PathValue("groupReference")
|
||||||
group, err := db.GetGroupByReference(groupReference)
|
group, err := lishwist.GetGroupByReference(groupReference)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching this group :(").Log("Couldn't get group: %s", err)
|
return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching this group :(").Log("Couldn't get group: %s", err)
|
||||||
}
|
}
|
||||||
|
|
@ -66,13 +66,14 @@ func PublicGroup(h http.Header, r *rsvp.Request) rsvp.Response {
|
||||||
return rsvp.Data("public_group_page.gotmpl", p)
|
return rsvp.Data("public_group_page.gotmpl", p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GroupPost(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
|
func GroupPost(app *lishwist.Session, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||||
if !currentUser.IsAdmin {
|
admin := app.Admin()
|
||||||
|
if admin == nil {
|
||||||
return NotFound(h, r)
|
return NotFound(h, r)
|
||||||
}
|
}
|
||||||
form := r.ParseForm()
|
form := r.ParseForm()
|
||||||
|
|
||||||
var group *db.Group
|
var group *lishwist.Group
|
||||||
|
|
||||||
reference := r.PathValue("groupReference")
|
reference := r.PathValue("groupReference")
|
||||||
name := form.Get("name")
|
name := form.Get("name")
|
||||||
|
|
@ -80,13 +81,13 @@ func GroupPost(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Respon
|
||||||
removeUsers := form["removeUser"]
|
removeUsers := form["removeUser"]
|
||||||
|
|
||||||
if name != "" {
|
if name != "" {
|
||||||
createdGroup, err := db.CreateGroup(name, reference)
|
createdGroup, err := admin.CreateGroup(name, reference)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "Failed to create group: %s", err)
|
return rsvp.Error(http.StatusInternalServerError, "Failed to create group: %s", err)
|
||||||
}
|
}
|
||||||
group = createdGroup
|
group = createdGroup
|
||||||
} else {
|
} else {
|
||||||
existingGroup, err := db.GetGroupByReference(reference)
|
existingGroup, err := lishwist.GetGroupByReference(reference)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "Failed to get group: %s", err)
|
return rsvp.Error(http.StatusInternalServerError, "Failed to get group: %s", err)
|
||||||
}
|
}
|
||||||
|
|
@ -100,7 +101,7 @@ func GroupPost(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Respon
|
||||||
if index == -1 {
|
if index == -1 {
|
||||||
return rsvp.Error(http.StatusBadRequest, "Group %q does not contain a user with id %s", reference, userId)
|
return rsvp.Error(http.StatusBadRequest, "Group %q does not contain a user with id %s", reference, userId)
|
||||||
}
|
}
|
||||||
err = group.RemoveUser(userId)
|
err = admin.RemoveUserFromGroup(userId, group.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "On group %q failed to remove user with id %s: %s", reference, userId, err)
|
return rsvp.Error(http.StatusInternalServerError, "On group %q failed to remove user with id %s: %s", reference, userId, err)
|
||||||
}
|
}
|
||||||
|
|
@ -109,14 +110,14 @@ func GroupPost(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Respon
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, userId := range addUsers {
|
for _, userId := range addUsers {
|
||||||
user, err := db.GetUser(userId)
|
user, err := admin.GetUser(userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "Groups exists, but a user with id %s could not be fetched: %s", userId, err)
|
return rsvp.Error(http.StatusInternalServerError, "Groups exists, but a user with id %s could not be fetched: %s", userId, err)
|
||||||
}
|
}
|
||||||
if user == nil {
|
if user == nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "Groups exists, but a user with id %s does not exist", userId)
|
return rsvp.Error(http.StatusInternalServerError, "Groups exists, but a user with id %s does not exist", userId)
|
||||||
}
|
}
|
||||||
err = group.AddUser(user.Id)
|
err = admin.AddUserToGroup(user.Id, group.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "Groups exists, but failed to add user with id %s: %s", userId, err)
|
return rsvp.Error(http.StatusInternalServerError, "Groups exists, but failed to add user with id %s: %s", userId, err)
|
||||||
}
|
}
|
||||||
|
|
@ -126,12 +127,13 @@ func GroupPost(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Respon
|
||||||
return rsvp.Data("", group)
|
return rsvp.Data("", group)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Groups(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
|
func Groups(app *lishwist.Session, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||||
if !currentUser.IsAdmin {
|
admin := app.Admin()
|
||||||
|
if admin == nil {
|
||||||
return NotFound(h, r)
|
return NotFound(h, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
groups, err := db.GetAllGroups()
|
groups, err := admin.ListGroups()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "Failed to get groups: %s", err)
|
return rsvp.Error(http.StatusInternalServerError, "Failed to get groups: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,45 +3,45 @@ package routing
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"lishwist/http/api/db"
|
lishwist "lishwist/core"
|
||||||
"lishwist/http/env"
|
"lishwist/http/env"
|
||||||
"lishwist/http/rsvp"
|
"lishwist/http/rsvp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HomeProps struct {
|
type HomeProps struct {
|
||||||
Username string
|
Username string
|
||||||
Gifts []db.Gift
|
Gifts []lishwist.Wish
|
||||||
Todo []db.Gift
|
Todo []lishwist.Wish
|
||||||
Reference string
|
Reference string
|
||||||
HostUrl string
|
HostUrl string
|
||||||
Groups []db.Group
|
Groups []lishwist.Group
|
||||||
}
|
}
|
||||||
|
|
||||||
func Home(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
|
func Home(app *lishwist.Session, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||||
gifts, err := currentUser.GetGifts()
|
gifts, err := app.GetWishes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching your wishlist :(").Log("Failed to get gifts: %s", err)
|
return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching your wishlist :(").Log("Failed to get gifts: %s", err)
|
||||||
}
|
}
|
||||||
todo, err := currentUser.GetTodo()
|
todo, err := app.GetTodo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching your wishlist :(").Log("Failed to get todo: %s", err)
|
return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching your wishlist :(").Log("Failed to get todo: %s", err)
|
||||||
}
|
}
|
||||||
groups, err := currentUser.GetGroups()
|
groups, err := app.GetGroups()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching your wishlist :(").Log("Failed to get groups: %s", err)
|
return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching your wishlist :(").Log("Failed to get groups: %s", err)
|
||||||
}
|
}
|
||||||
p := HomeProps{Username: currentUser.Name, Gifts: gifts, Todo: todo, Reference: currentUser.Reference, HostUrl: env.HostUrl.String(), Groups: groups}
|
p := HomeProps{Username: app.User.Name, Gifts: gifts, Todo: todo, Reference: app.User.Reference, HostUrl: env.HostUrl.String(), Groups: groups}
|
||||||
return rsvp.Data("home.gotmpl", p)
|
return rsvp.Data("home.gotmpl", p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func HomePost(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
|
func HomePost(app *lishwist.Session, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||||
form := r.ParseForm()
|
form := r.ParseForm()
|
||||||
switch form.Get("intent") {
|
switch form.Get("intent") {
|
||||||
case "add_idea":
|
case "add_idea":
|
||||||
return WishlistAdd(currentUser, h, r)
|
return WishlistAdd(app, h, r)
|
||||||
case "delete_idea":
|
case "delete_idea":
|
||||||
return WishlistDelete(currentUser, h, r)
|
return WishlistDelete(app, h, r)
|
||||||
default:
|
default:
|
||||||
return TodoUpdate(currentUser, h, r)
|
return TodoUpdate(app, h, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package routing
|
package routing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
lishwist "lishwist/core"
|
||||||
"lishwist/http/api"
|
"lishwist/http/api"
|
||||||
"lishwist/http/rsvp"
|
"lishwist/http/rsvp"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
@ -37,15 +38,32 @@ func LoginPost(h http.Header, r *rsvp.Request) rsvp.Response {
|
||||||
username := form.Get("username")
|
username := form.Get("username")
|
||||||
password := form.Get("password")
|
password := form.Get("password")
|
||||||
|
|
||||||
props := api.Login(username, password)
|
props := api.NewLoginProps(username, password)
|
||||||
if props != nil {
|
|
||||||
|
valid := props.Validate()
|
||||||
|
props.Password.Value = ""
|
||||||
|
if !valid {
|
||||||
session.FlashSet(&props)
|
session.FlashSet(&props)
|
||||||
return rsvp.SeeOther("/").SaveSession(session)
|
return rsvp.SeeOther("/").SaveSession(session).Log("Invalid props: %#v\n", props)
|
||||||
|
}
|
||||||
|
|
||||||
|
app, err := lishwist.Login(username, password)
|
||||||
|
if err != nil {
|
||||||
|
switch err.(type) {
|
||||||
|
case lishwist.ErrorInvalidCredentials:
|
||||||
|
props.GeneralError = "Username or password invalid"
|
||||||
|
session.FlashSet(&props)
|
||||||
|
return rsvp.SeeOther("/").SaveSession(session).Log("Invalid credentials: %#v\n", props)
|
||||||
|
default:
|
||||||
|
props.GeneralError = "Something went wrong."
|
||||||
|
session.FlashSet(&props)
|
||||||
|
return rsvp.SeeOther("/").SaveSession(session).Log("Login error: %s\n", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
session.SetID("")
|
session.SetID("")
|
||||||
session.SetValue("authorized", true)
|
session.SetValue("authorized", true)
|
||||||
session.SetValue("username", username)
|
session.SetValue("username", app.User.Name)
|
||||||
|
|
||||||
return rsvp.SeeOther(r.URL().Path).SaveSession(session)
|
return rsvp.SeeOther(r.URL().Path).SaveSession(session)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package routing
|
package routing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
lishwist "lishwist/core"
|
||||||
"lishwist/http/api"
|
"lishwist/http/api"
|
||||||
"lishwist/http/rsvp"
|
"lishwist/http/rsvp"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
@ -26,18 +28,31 @@ func Register(h http.Header, r *rsvp.Request) rsvp.Response {
|
||||||
|
|
||||||
func RegisterPost(h http.Header, r *rsvp.Request) rsvp.Response {
|
func RegisterPost(h http.Header, r *rsvp.Request) rsvp.Response {
|
||||||
form := r.ParseForm()
|
form := r.ParseForm()
|
||||||
|
s := r.GetSession()
|
||||||
|
|
||||||
username := form.Get("username")
|
username := form.Get("username")
|
||||||
newPassword := form.Get("newPassword")
|
newPassword := form.Get("newPassword")
|
||||||
confirmPassword := form.Get("confirmPassword")
|
confirmPassword := form.Get("confirmPassword")
|
||||||
|
|
||||||
props := api.Register(username, newPassword, confirmPassword)
|
props := api.NewRegisterProps(username, newPassword, confirmPassword)
|
||||||
|
|
||||||
s := r.GetSession()
|
valid := props.Validate()
|
||||||
|
props.Password.Value = ""
|
||||||
if props != nil {
|
props.ConfirmPassword.Value = ""
|
||||||
|
if !valid {
|
||||||
s.FlashSet(&props)
|
s.FlashSet(&props)
|
||||||
return rsvp.SeeOther("/register").SaveSession(s)
|
return rsvp.SeeOther("/").SaveSession(s).Log("Invalid props: %#v\n", props)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := lishwist.Register(username, newPassword)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, lishwist.ErrorUsernameTaken) {
|
||||||
|
props.Username.Error = "Username is taken"
|
||||||
|
} else {
|
||||||
|
props.GeneralError = "Something went wrong."
|
||||||
|
}
|
||||||
|
s.FlashSet(&props)
|
||||||
|
return rsvp.SeeOther("/register").SaveSession(s).Log("Registration failed: %s\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.FlashSet(true)
|
s.FlashSet(true)
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,24 @@
|
||||||
package routing
|
package routing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"lishwist/http/api/db"
|
lishwist "lishwist/core"
|
||||||
"lishwist/http/rsvp"
|
"lishwist/http/rsvp"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TodoUpdate(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
|
func TodoUpdate(app *lishwist.Session, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||||
form := r.ParseForm()
|
form := r.ParseForm()
|
||||||
|
|
||||||
switch form.Get("intent") {
|
switch form.Get("intent") {
|
||||||
case "unclaim_todo":
|
case "unclaim_todo":
|
||||||
unclaims := form["gift"]
|
unclaims := form["gift"]
|
||||||
err := currentUser.ClaimGifts([]string{}, unclaims)
|
err := app.ClaimWishes([]string{}, unclaims)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "Failed to update claim...").LogError(err)
|
return rsvp.Error(http.StatusInternalServerError, "Failed to update claim...").LogError(err)
|
||||||
}
|
}
|
||||||
case "complete_todo":
|
case "complete_todo":
|
||||||
claims := form["gift"]
|
claims := form["gift"]
|
||||||
err := currentUser.CompleteGifts(claims)
|
err := app.CompleteWishes(claims)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "Failed to complete gifts...").LogError(err)
|
return rsvp.Error(http.StatusInternalServerError, "Failed to complete gifts...").LogError(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,18 @@
|
||||||
package routing
|
package routing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"lishwist/http/api/db"
|
lishwist "lishwist/core"
|
||||||
"lishwist/http/rsvp"
|
"lishwist/http/rsvp"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Users(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
|
func Users(app *lishwist.Session, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||||
if !currentUser.IsAdmin {
|
admin := app.Admin()
|
||||||
|
if admin == nil {
|
||||||
return NotFound(h, r)
|
return NotFound(h, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
users, err := db.GetAllUsers()
|
users, err := admin.ListUsers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "Failed to get users: %s", err)
|
return rsvp.Error(http.StatusInternalServerError, "Failed to get users: %s", err)
|
||||||
}
|
}
|
||||||
|
|
@ -19,14 +20,15 @@ func Users(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||||
return rsvp.Data("", users)
|
return rsvp.Data("", users)
|
||||||
}
|
}
|
||||||
|
|
||||||
func User(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
|
func User(app *lishwist.Session, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||||
if !currentUser.IsAdmin {
|
admin := app.Admin()
|
||||||
|
if admin == nil {
|
||||||
return NotFound(h, r)
|
return NotFound(h, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
reference := r.PathValue("userReference")
|
reference := r.PathValue("userReference")
|
||||||
|
|
||||||
user, err := db.GetUserByReference(reference)
|
user, err := lishwist.GetUserByReference(reference)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "Failed to get user: %s", err)
|
return rsvp.Error(http.StatusInternalServerError, "Failed to get user: %s", err)
|
||||||
}
|
}
|
||||||
|
|
@ -37,19 +39,20 @@ func User(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||||
return rsvp.Data("", user)
|
return rsvp.Data("", user)
|
||||||
}
|
}
|
||||||
|
|
||||||
func UserPost(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
|
func UserPost(app *lishwist.Session, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||||
if !currentUser.IsAdmin {
|
admin := app.Admin()
|
||||||
|
if admin == nil {
|
||||||
return NotFound(h, r)
|
return NotFound(h, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
form := r.ParseForm()
|
form := r.ParseForm()
|
||||||
|
|
||||||
reference := r.PathValue("userReference")
|
reference := r.PathValue("userReference")
|
||||||
if reference == currentUser.Reference {
|
if reference == app.User.Reference {
|
||||||
return rsvp.Error(http.StatusForbidden, "You cannot delete yourself.")
|
return rsvp.Error(http.StatusForbidden, "You cannot delete yourself.")
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := db.GetAnyUserByReference(reference)
|
user, err := lishwist.GetUserByReference(reference)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "Failed to get user: %s", err)
|
return rsvp.Error(http.StatusInternalServerError, "Failed to get user: %s", err)
|
||||||
}
|
}
|
||||||
|
|
@ -60,7 +63,7 @@ func UserPost(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Respons
|
||||||
intent := form.Get("intent")
|
intent := form.Get("intent")
|
||||||
|
|
||||||
if intent != "" {
|
if intent != "" {
|
||||||
err = user.SetLive(intent != "delete")
|
err = admin.UserSetLive(reference, intent != "delete")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "Failed to delete user: %s", err)
|
return rsvp.Error(http.StatusInternalServerError, "Failed to delete user: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,32 @@
|
||||||
package routing
|
package routing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"lishwist/http/api/db"
|
lishwist "lishwist/core"
|
||||||
"lishwist/http/rsvp"
|
"lishwist/http/rsvp"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func WishlistAdd(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
|
func WishlistAdd(app *lishwist.Session, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||||
form := r.ParseForm()
|
form := r.ParseForm()
|
||||||
newGiftName := form.Get("gift_name")
|
newGiftName := form.Get("gift_name")
|
||||||
err := currentUser.AddGift(newGiftName)
|
err := app.MakeWish(newGiftName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "Failed to add gift.").LogError(err)
|
return rsvp.Error(http.StatusInternalServerError, "Failed to add gift.").LogError(err)
|
||||||
}
|
}
|
||||||
return rsvp.SeeOther("/")
|
return rsvp.SeeOther("/")
|
||||||
}
|
}
|
||||||
|
|
||||||
func WishlistDelete(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
|
func WishlistDelete(app *lishwist.Session, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||||
form := r.ParseForm()
|
form := r.ParseForm()
|
||||||
targets := form["gift"]
|
targets := form["gift"]
|
||||||
err := currentUser.RemoveGifts(targets...)
|
err := app.RevokeWishes(targets...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "Failed to remove gifts.").LogError(err)
|
return rsvp.Error(http.StatusInternalServerError, "Failed to remove gifts.").LogError(err)
|
||||||
}
|
}
|
||||||
return rsvp.SeeOther("/")
|
return rsvp.SeeOther("/")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ForeignWishlistPost(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
|
func ForeignWishlistPost(app *lishwist.Session, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||||
form := r.ParseForm()
|
form := r.ParseForm()
|
||||||
userReference := r.PathValue("userReference")
|
userReference := r.PathValue("userReference")
|
||||||
intent := form.Get("intent")
|
intent := form.Get("intent")
|
||||||
|
|
@ -34,22 +34,22 @@ func ForeignWishlistPost(currentUser *db.User, h http.Header, r *rsvp.Request) r
|
||||||
case "claim":
|
case "claim":
|
||||||
claims := form["unclaimed"]
|
claims := form["unclaimed"]
|
||||||
unclaims := form["claimed"]
|
unclaims := form["claimed"]
|
||||||
err := currentUser.ClaimGifts(claims, unclaims)
|
err := app.ClaimWishes(claims, unclaims)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "Failed to update claim...").LogError(err)
|
return rsvp.Error(http.StatusInternalServerError, "Failed to update claim...").LogError(err)
|
||||||
}
|
}
|
||||||
case "complete":
|
case "complete":
|
||||||
claims := form["claimed"]
|
claims := form["claimed"]
|
||||||
err := currentUser.CompleteGifts(claims)
|
err := app.CompleteWishes(claims)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "Failed to complete gifts...").LogError(err)
|
return rsvp.Error(http.StatusInternalServerError, "Failed to complete gifts...").LogError(err)
|
||||||
}
|
}
|
||||||
case "add":
|
case "add":
|
||||||
giftName := form.Get("gift_name")
|
wishName := form.Get("gift_name")
|
||||||
if giftName == "" {
|
if wishName == "" {
|
||||||
return rsvp.Error(http.StatusBadRequest, "Gift name not provided")
|
return rsvp.Error(http.StatusBadRequest, "Gift name not provided")
|
||||||
}
|
}
|
||||||
err := currentUser.AddGiftToUser(userReference, giftName)
|
err := app.SuggestWishForUser(userReference, wishName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "Failed to add gift idea to other user...").LogError(err)
|
return rsvp.Error(http.StatusInternalServerError, "Failed to add gift idea to other user...").LogError(err)
|
||||||
}
|
}
|
||||||
|
|
@ -57,7 +57,7 @@ func ForeignWishlistPost(currentUser *db.User, h http.Header, r *rsvp.Request) r
|
||||||
claims := form["unclaimed"]
|
claims := form["unclaimed"]
|
||||||
unclaims := form["claimed"]
|
unclaims := form["claimed"]
|
||||||
gifts := append(claims, unclaims...)
|
gifts := append(claims, unclaims...)
|
||||||
err := currentUser.RemoveGifts(gifts...)
|
err := app.RecindWishesForUser(gifts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rsvp.Error(http.StatusInternalServerError, "Failed to remove gift idea for other user...").LogError(err)
|
return rsvp.Error(http.StatusInternalServerError, "Failed to remove gift idea for other user...").LogError(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue