feat: use core library
This commit is contained in:
parent
b2e9bab55d
commit
cc0409d1dc
|
|
@ -3,7 +3,6 @@ package lishwist
|
|||
import (
|
||||
"fmt"
|
||||
"lishwist/core/internal/db"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
|
@ -26,12 +25,7 @@ func (g *Group) MemberIndex(userId string) int {
|
|||
|
||||
func queryManyGroups(query string, args ...any) ([]Group, error) {
|
||||
groups := []Group{}
|
||||
// PrintTables()
|
||||
// PrintViews()
|
||||
log.Println(query, args)
|
||||
rows, err := db.Connection.Query(query, args...)
|
||||
// PrintTables()
|
||||
// PrintViews()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Query failed: %w", err)
|
||||
}
|
||||
|
|
@ -76,16 +70,16 @@ func queryManyGroupMembers(groupId string) ([]User, error) {
|
|||
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) {
|
||||
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)
|
||||
}
|
||||
|
||||
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) {
|
||||
query := "SELECT id, name, reference FROM [group];"
|
||||
return queryManyGroups(query)
|
||||
|
|
@ -128,3 +122,9 @@ func (a *Admin) RemoveUserFromGroup(userId, groupId string) error {
|
|||
}
|
||||
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" (
|
||||
"id" INTEGER NOT NULL UNIQUE,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
PRIMARY KEY("id" AUTOINCREMENT),
|
||||
FOREIGN KEY("user_id") REFERENCES "user"("id")
|
||||
"value" TEXT NOT NULL,
|
||||
PRIMARY KEY("id" AUTOINCREMENT)
|
||||
);
|
||||
|
||||
DROP VIEW IF EXISTS "v_user";
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package fixtures
|
|||
import (
|
||||
"log"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
lw := lishwist.NewSessionManager(time.Second*10, 32)
|
||||
|
||||
_, err = lishwist.Register(username, password)
|
||||
if err != nil {
|
||||
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 {
|
||||
log.Fatalf("Failed to login on fixture: %s\n", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,13 +6,15 @@ import (
|
|||
"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)
|
||||
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 {
|
||||
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()
|
||||
|
|
@ -22,13 +24,8 @@ func (sm *SessionManager) Login(username, password string) (*Session, error) {
|
|||
|
||||
err = bcrypt.CompareHashAndPassword(passHash, []byte(password))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, ErrorInvalidCredentials(fmt.Errorf("Password compare failed: %w", err))
|
||||
}
|
||||
|
||||
session, err := sm.createSession(user)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Couldn't create session: %w", err)
|
||||
}
|
||||
|
||||
return session, nil
|
||||
return &Session{*user}, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package lishwist_test
|
|||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
lishwist "lishwist/core"
|
||||
"lishwist/core/internal/fixtures"
|
||||
|
|
@ -14,14 +13,12 @@ func TestLogin(t *testing.T) {
|
|||
t.Fatalf("Failed to init db: %s\n", err)
|
||||
}
|
||||
|
||||
lw := lishwist.NewSessionManager(time.Second*10, 32)
|
||||
|
||||
_, err = lishwist.Register("thomas", "123")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to register: %s\n", err)
|
||||
}
|
||||
|
||||
_, err = lw.Login("thomas", "123")
|
||||
_, err = lishwist.Login("thomas", "123")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to login: %s\n", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import (
|
|||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
var ErrorUsernameTaken = errors.New("Username is taken")
|
||||
|
||||
func Register(username, newPassword string) (*User, error) {
|
||||
if username == "" {
|
||||
return nil, errors.New("Username required")
|
||||
|
|
@ -17,7 +19,7 @@ func Register(username, newPassword string) (*User, error) {
|
|||
|
||||
existingUser, _ := getUserByName(username)
|
||||
if existingUser != nil {
|
||||
return nil, errors.New("Username is taken")
|
||||
return nil, ErrorUsernameTaken
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"lishwist/core/internal/db"
|
||||
"time"
|
||||
)
|
||||
import "fmt"
|
||||
|
||||
type Session struct {
|
||||
Id string
|
||||
Token string
|
||||
User *User
|
||||
ExpiresAt time.Time
|
||||
CreatedAt time.Time
|
||||
User
|
||||
}
|
||||
|
||||
type SessionManager struct {
|
||||
sessionDuration time.Duration
|
||||
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)
|
||||
func SessionFromUsername(username string) (*Session, error) {
|
||||
user, err := getUserByName(username)
|
||||
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()
|
||||
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
|
||||
return &Session{*user}, nil
|
||||
}
|
||||
|
|
|
|||
57
core/user.go
57
core/user.go
|
|
@ -10,7 +10,8 @@ import (
|
|||
)
|
||||
|
||||
type User struct {
|
||||
Id string
|
||||
Id string
|
||||
// TODO: rename to DisplayName
|
||||
NormalName string
|
||||
Name string
|
||||
Reference string
|
||||
|
|
@ -94,6 +95,11 @@ func getUserByReference(reference string) (*User, error) {
|
|||
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) {
|
||||
stmt := "SELECT COUNT(id) FROM v_user LIMIT 1"
|
||||
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"
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
otherUser, err := getUserByReference(userReference)
|
||||
if err != nil {
|
||||
|
|
@ -69,7 +110,7 @@ func (s *Session) GetOthersWishes(userReference string) ([]Wish, error) {
|
|||
if otherUser.Id == s.User.Id {
|
||||
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)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
import (
|
||||
"lishwist/http/api/db"
|
||||
"lishwist/http/templates"
|
||||
"log"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
lishwist "lishwist/core"
|
||||
"lishwist/http/templates"
|
||||
)
|
||||
|
||||
type LoginProps struct {
|
||||
|
|
@ -55,31 +54,19 @@ func Login(username, password string) *LoginProps {
|
|||
return props
|
||||
}
|
||||
|
||||
user, err := db.GetUserByName(username)
|
||||
if err != nil {
|
||||
log.Printf("Failed to fetch user: %s\n", err)
|
||||
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
|
||||
_, err := lishwist.Login(props.Username.Value, props.Password.Value)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
passHash, err := user.GetPassHash()
|
||||
if err != nil {
|
||||
log.Println("Failed to get password hash: " + err.Error())
|
||||
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())
|
||||
switch err.(type) {
|
||||
case lishwist.ErrorInvalidCredentials:
|
||||
log.Printf("Invalid credentials: %w\n", err)
|
||||
props.GeneralError = "Username or password invalid"
|
||||
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
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"lishwist/http/api/db"
|
||||
"lishwist/http/templates"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type RegisterProps struct {
|
||||
|
|
@ -64,37 +59,37 @@ func NewRegisterProps(usernameVal, passwordVal, confirmPassVal string) *Register
|
|||
}
|
||||
}
|
||||
|
||||
func Register(username, newPassword, confirmPassword string) *RegisterProps {
|
||||
props := NewRegisterProps(username, newPassword, confirmPassword)
|
||||
// func Register(username, newPassword, confirmPassword string) *RegisterProps {
|
||||
// props := NewRegisterProps(username, newPassword, confirmPassword)
|
||||
|
||||
valid := props.Validate()
|
||||
props.Password.Value = ""
|
||||
props.ConfirmPassword.Value = ""
|
||||
if !valid {
|
||||
log.Printf("Invalid props: %#v\n", props)
|
||||
return props
|
||||
}
|
||||
// valid := props.Validate()
|
||||
// props.Password.Value = ""
|
||||
// props.ConfirmPassword.Value = ""
|
||||
// if !valid {
|
||||
// log.Printf("Invalid props: %#v\n", props)
|
||||
// return props
|
||||
// }
|
||||
|
||||
existingUser, _ := db.GetUserByName(username)
|
||||
if existingUser != nil {
|
||||
log.Printf("Username is taken: %q\n", existingUser.NormalName)
|
||||
props.Username.Error = "Username is taken"
|
||||
return props
|
||||
}
|
||||
// existingUser, _ := db.GetUserByName(username)
|
||||
// if existingUser != nil {
|
||||
// log.Printf("Username is taken: %q\n", existingUser.NormalName)
|
||||
// props.Username.Error = "Username is taken"
|
||||
// return props
|
||||
// }
|
||||
|
||||
hashedPasswordBytes, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.MinCost)
|
||||
if err != nil {
|
||||
log.Printf("Failed to hash password: %s\n", err)
|
||||
props.GeneralError = "Something went wrong. Error code: Aang"
|
||||
return props
|
||||
}
|
||||
// hashedPasswordBytes, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.MinCost)
|
||||
// if err != nil {
|
||||
// log.Printf("Failed to hash password: %s\n", err)
|
||||
// props.GeneralError = "Something went wrong. Error code: Aang"
|
||||
// return props
|
||||
// }
|
||||
|
||||
_, err = db.CreateUser(username, hashedPasswordBytes)
|
||||
if err != nil {
|
||||
log.Printf("Failed to create user: %s\n", err)
|
||||
props.GeneralError = "Something went wrong. Error code: Ozai"
|
||||
return props
|
||||
}
|
||||
// _, err = db.CreateUser(username, hashedPasswordBytes)
|
||||
// if err != nil {
|
||||
// log.Printf("Failed to create user: %s\n", err)
|
||||
// props.GeneralError = "Something went wrong. Error code: Ozai"
|
||||
// return props
|
||||
// }
|
||||
|
||||
return nil
|
||||
}
|
||||
// return nil
|
||||
// }
|
||||
|
|
|
|||
40
http/main.go
40
http/main.go
|
|
@ -5,9 +5,9 @@ import (
|
|||
"log"
|
||||
"net/http"
|
||||
|
||||
lishwist "lishwist/core"
|
||||
"lishwist/core/session"
|
||||
"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/router"
|
||||
"lishwist/http/routing"
|
||||
|
|
@ -17,16 +17,12 @@ func main() {
|
|||
gob.Register(&api.RegisterProps{})
|
||||
gob.Register(&api.LoginProps{})
|
||||
|
||||
err := db.Open()
|
||||
err := lishwist.Init(env.DatabaseFile)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open DB: %s\n", err)
|
||||
}
|
||||
err = db.Init()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to init DB: %s\n", err)
|
||||
log.Fatalf("Failed to init Lishwist: %s\n", err)
|
||||
}
|
||||
|
||||
store, err := db.NewSessionStore()
|
||||
store, err := session.NewStore([]byte(env.SessionSecret))
|
||||
if err != nil {
|
||||
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 /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("POST /", routing.LoginPost)
|
||||
r.Public.HandleFunc("POST /register", routing.RegisterPost)
|
||||
|
||||
r.Private.HandleFunc("GET /", routing.NotFound)
|
||||
r.Private.HandleFunc("GET /groups", routing.ExpectUser(routing.Groups))
|
||||
r.Private.HandleFunc("GET /groups/{groupReference}", routing.ExpectUser(routing.Group))
|
||||
r.Private.HandleFunc("GET /list/{userReference}", routing.ExpectUser(routing.ForeignWishlist))
|
||||
r.Private.HandleFunc("GET /users", routing.ExpectUser(routing.Users))
|
||||
r.Private.HandleFunc("GET /users/{userReference}", routing.ExpectUser(routing.User))
|
||||
r.Private.HandleFunc("GET /{$}", routing.ExpectUser(routing.Home))
|
||||
r.Private.HandleFunc("POST /groups/{groupReference}", routing.ExpectUser(routing.GroupPost))
|
||||
r.Private.HandleFunc("POST /list/{userReference}", routing.ExpectUser(routing.ForeignWishlistPost))
|
||||
r.Private.HandleFunc("GET /groups", routing.ExpectAppSession(routing.Groups))
|
||||
r.Private.HandleFunc("GET /groups/{groupReference}", routing.ExpectAppSession(routing.Group))
|
||||
r.Private.HandleFunc("GET /lists/{userReference}", routing.ExpectAppSession(routing.ForeignWishlist))
|
||||
r.Private.HandleFunc("GET /users", routing.ExpectAppSession(routing.Users))
|
||||
r.Private.HandleFunc("GET /users/{userReference}", routing.ExpectAppSession(routing.User))
|
||||
r.Private.HandleFunc("GET /{$}", routing.ExpectAppSession(routing.Home))
|
||||
r.Private.HandleFunc("POST /groups/{groupReference}", routing.ExpectAppSession(routing.GroupPost))
|
||||
r.Private.HandleFunc("POST /list/{userReference}", routing.ExpectAppSession(routing.ForeignWishlistPost))
|
||||
r.Private.HandleFunc("POST /logout", routing.LogoutPost)
|
||||
r.Private.HandleFunc("POST /users/{userReference}", routing.ExpectUser(routing.UserPost))
|
||||
r.Private.HandleFunc("POST /{$}", routing.ExpectUser(routing.HomePost))
|
||||
r.Private.HandleFunc("POST /users/{userReference}", routing.ExpectAppSession(routing.UserPost))
|
||||
r.Private.HandleFunc("POST /{$}", routing.ExpectAppSession(routing.HomePost))
|
||||
|
||||
// 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.Private.HandleFunc("GET /group/{groupReference}", routing.ExpectUser(routing.Group))
|
||||
r.Public.HandleFunc("GET /list/{userReference}", routing.PublicWishlist)
|
||||
|
||||
http.Handle("/", r)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
package routing
|
||||
|
||||
import (
|
||||
"lishwist/http/api/db"
|
||||
lishwist "lishwist/core"
|
||||
"lishwist/http/rsvp"
|
||||
"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 {
|
||||
session := r.GetSession()
|
||||
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")
|
||||
}
|
||||
|
||||
user, err := db.GetUserByName(username)
|
||||
appSession, err := lishwist.SessionFromUsername(username)
|
||||
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
|
||||
|
||||
import (
|
||||
"lishwist/http/api/db"
|
||||
lishwist "lishwist/core"
|
||||
"lishwist/http/rsvp"
|
||||
"net/http"
|
||||
)
|
||||
|
|
@ -10,26 +10,26 @@ type foreignWishlistProps struct {
|
|||
CurrentUserId string
|
||||
CurrentUserName 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")
|
||||
if currentUser.Reference == userReference {
|
||||
if app.User.Reference == userReference {
|
||||
return rsvp.SeeOther("/")
|
||||
}
|
||||
otherUser, err := db.GetUserByReference(userReference)
|
||||
otherUser, err := lishwist.GetUserByReference(userReference)
|
||||
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)
|
||||
}
|
||||
if otherUser == nil {
|
||||
return rsvp.Error(http.StatusInternalServerError, "User not found")
|
||||
}
|
||||
gifts, err := currentUser.GetOtherUserGifts(userReference)
|
||||
wishes, err := app.GetOthersWishes(userReference)
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
@ -40,14 +40,14 @@ type publicWishlistProps struct {
|
|||
|
||||
func PublicWishlist(h http.Header, r *rsvp.Request) rsvp.Response {
|
||||
userReference := r.PathValue("userReference")
|
||||
otherUser, err := db.GetUserByReference(userReference)
|
||||
otherUser, err := lishwist.GetUserByReference(userReference)
|
||||
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)
|
||||
}
|
||||
if otherUser == nil {
|
||||
return rsvp.Error(http.StatusInternalServerError, "User not found")
|
||||
}
|
||||
giftCount, err := otherUser.CountGifts()
|
||||
giftCount, err := otherUser.WishCount()
|
||||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,59 +4,59 @@ import (
|
|||
"net/http"
|
||||
"slices"
|
||||
|
||||
"lishwist/http/api/db"
|
||||
lishwist "lishwist/core"
|
||||
"lishwist/http/rsvp"
|
||||
)
|
||||
|
||||
type GroupProps struct {
|
||||
Group *db.Group
|
||||
Group *lishwist.Group
|
||||
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")
|
||||
group, err := db.GetGroupByReference(reference)
|
||||
group, err := app.GetGroupByReference(reference)
|
||||
if err != nil {
|
||||
return rsvp.Error(http.StatusInternalServerError, "Couldn't get group: %s", err)
|
||||
}
|
||||
if group == nil {
|
||||
return rsvp.Error(http.StatusNotFound, "Group not found")
|
||||
}
|
||||
if !currentUser.IsAdmin {
|
||||
index := group.MemberIndex(currentUser.Id)
|
||||
if !app.User.IsAdmin {
|
||||
index := group.MemberIndex(app.User.Id)
|
||||
group.Members = slices.Delete(group.Members, index, index+1)
|
||||
}
|
||||
p := GroupProps{
|
||||
Group: group,
|
||||
CurrentUsername: currentUser.Name,
|
||||
CurrentUsername: app.User.Name,
|
||||
}
|
||||
return rsvp.Data("group_page.gotmpl", p)
|
||||
}
|
||||
|
||||
func Group(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||
if currentUser.IsAdmin {
|
||||
return AdminGroup(currentUser, h, r)
|
||||
func Group(app *lishwist.Session, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||
if app.User.IsAdmin {
|
||||
return AdminGroup(app, h, r)
|
||||
}
|
||||
groupReference := r.PathValue("groupReference")
|
||||
group, err := currentUser.GetGroupByReference(groupReference)
|
||||
group, err := app.GetGroupByReference(groupReference)
|
||||
if err != nil {
|
||||
return rsvp.Error(http.StatusInternalServerError, "An error occurred while fetching this group :(").Log("Couldn't get group: %s", err)
|
||||
}
|
||||
if group == nil {
|
||||
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)
|
||||
p := GroupProps{
|
||||
Group: group,
|
||||
CurrentUsername: currentUser.Name,
|
||||
CurrentUsername: app.User.Name,
|
||||
}
|
||||
return rsvp.Data("group_page.gotmpl", p)
|
||||
}
|
||||
|
||||
func PublicGroup(h http.Header, r *rsvp.Request) rsvp.Response {
|
||||
groupReference := r.PathValue("groupReference")
|
||||
group, err := db.GetGroupByReference(groupReference)
|
||||
group, err := lishwist.GetGroupByReference(groupReference)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
func GroupPost(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||
if !currentUser.IsAdmin {
|
||||
func GroupPost(app *lishwist.Session, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||
admin := app.Admin()
|
||||
if admin == nil {
|
||||
return NotFound(h, r)
|
||||
}
|
||||
form := r.ParseForm()
|
||||
|
||||
var group *db.Group
|
||||
var group *lishwist.Group
|
||||
|
||||
reference := r.PathValue("groupReference")
|
||||
name := form.Get("name")
|
||||
|
|
@ -80,13 +81,13 @@ func GroupPost(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Respon
|
|||
removeUsers := form["removeUser"]
|
||||
|
||||
if name != "" {
|
||||
createdGroup, err := db.CreateGroup(name, reference)
|
||||
createdGroup, err := admin.CreateGroup(name, reference)
|
||||
if err != nil {
|
||||
return rsvp.Error(http.StatusInternalServerError, "Failed to create group: %s", err)
|
||||
}
|
||||
group = createdGroup
|
||||
} else {
|
||||
existingGroup, err := db.GetGroupByReference(reference)
|
||||
existingGroup, err := lishwist.GetGroupByReference(reference)
|
||||
if err != nil {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
user, err := db.GetUser(userId)
|
||||
user, err := admin.GetUser(userId)
|
||||
if err != nil {
|
||||
return rsvp.Error(http.StatusInternalServerError, "Groups exists, but a user with id %s could not be fetched: %s", userId, err)
|
||||
}
|
||||
if user == nil {
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
|
||||
func Groups(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||
if !currentUser.IsAdmin {
|
||||
func Groups(app *lishwist.Session, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||
admin := app.Admin()
|
||||
if admin == nil {
|
||||
return NotFound(h, r)
|
||||
}
|
||||
|
||||
groups, err := db.GetAllGroups()
|
||||
groups, err := admin.ListGroups()
|
||||
if err != nil {
|
||||
return rsvp.Error(http.StatusInternalServerError, "Failed to get groups: %s", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,45 +3,45 @@ package routing
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"lishwist/http/api/db"
|
||||
lishwist "lishwist/core"
|
||||
"lishwist/http/env"
|
||||
"lishwist/http/rsvp"
|
||||
)
|
||||
|
||||
type HomeProps struct {
|
||||
Username string
|
||||
Gifts []db.Gift
|
||||
Todo []db.Gift
|
||||
Gifts []lishwist.Wish
|
||||
Todo []lishwist.Wish
|
||||
Reference string
|
||||
HostUrl string
|
||||
Groups []db.Group
|
||||
Groups []lishwist.Group
|
||||
}
|
||||
|
||||
func Home(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||
gifts, err := currentUser.GetGifts()
|
||||
func Home(app *lishwist.Session, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||
gifts, err := app.GetWishes()
|
||||
if err != nil {
|
||||
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 {
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
|
||||
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()
|
||||
switch form.Get("intent") {
|
||||
case "add_idea":
|
||||
return WishlistAdd(currentUser, h, r)
|
||||
return WishlistAdd(app, h, r)
|
||||
case "delete_idea":
|
||||
return WishlistDelete(currentUser, h, r)
|
||||
return WishlistDelete(app, h, r)
|
||||
default:
|
||||
return TodoUpdate(currentUser, h, r)
|
||||
return TodoUpdate(app, h, r)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package routing
|
||||
|
||||
import (
|
||||
lishwist "lishwist/core"
|
||||
"lishwist/http/api"
|
||||
"lishwist/http/rsvp"
|
||||
"net/http"
|
||||
|
|
@ -37,15 +38,32 @@ func LoginPost(h http.Header, r *rsvp.Request) rsvp.Response {
|
|||
username := form.Get("username")
|
||||
password := form.Get("password")
|
||||
|
||||
props := api.Login(username, password)
|
||||
if props != nil {
|
||||
props := api.NewLoginProps(username, password)
|
||||
|
||||
valid := props.Validate()
|
||||
props.Password.Value = ""
|
||||
if !valid {
|
||||
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.SetValue("authorized", true)
|
||||
session.SetValue("username", username)
|
||||
session.SetValue("username", app.User.Name)
|
||||
|
||||
return rsvp.SeeOther(r.URL().Path).SaveSession(session)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package routing
|
||||
|
||||
import (
|
||||
"errors"
|
||||
lishwist "lishwist/core"
|
||||
"lishwist/http/api"
|
||||
"lishwist/http/rsvp"
|
||||
"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 {
|
||||
form := r.ParseForm()
|
||||
s := r.GetSession()
|
||||
|
||||
username := form.Get("username")
|
||||
newPassword := form.Get("newPassword")
|
||||
confirmPassword := form.Get("confirmPassword")
|
||||
|
||||
props := api.Register(username, newPassword, confirmPassword)
|
||||
props := api.NewRegisterProps(username, newPassword, confirmPassword)
|
||||
|
||||
s := r.GetSession()
|
||||
|
||||
if props != nil {
|
||||
valid := props.Validate()
|
||||
props.Password.Value = ""
|
||||
props.ConfirmPassword.Value = ""
|
||||
if !valid {
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
package routing
|
||||
|
||||
import (
|
||||
"lishwist/http/api/db"
|
||||
lishwist "lishwist/core"
|
||||
"lishwist/http/rsvp"
|
||||
"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()
|
||||
|
||||
switch form.Get("intent") {
|
||||
case "unclaim_todo":
|
||||
unclaims := form["gift"]
|
||||
err := currentUser.ClaimGifts([]string{}, unclaims)
|
||||
err := app.ClaimWishes([]string{}, unclaims)
|
||||
if err != nil {
|
||||
return rsvp.Error(http.StatusInternalServerError, "Failed to update claim...").LogError(err)
|
||||
}
|
||||
case "complete_todo":
|
||||
claims := form["gift"]
|
||||
err := currentUser.CompleteGifts(claims)
|
||||
err := app.CompleteWishes(claims)
|
||||
if err != nil {
|
||||
return rsvp.Error(http.StatusInternalServerError, "Failed to complete gifts...").LogError(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
package routing
|
||||
|
||||
import (
|
||||
"lishwist/http/api/db"
|
||||
lishwist "lishwist/core"
|
||||
"lishwist/http/rsvp"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func Users(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||
if !currentUser.IsAdmin {
|
||||
func Users(app *lishwist.Session, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||
admin := app.Admin()
|
||||
if admin == nil {
|
||||
return NotFound(h, r)
|
||||
}
|
||||
|
||||
users, err := db.GetAllUsers()
|
||||
users, err := admin.ListUsers()
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
func User(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||
if !currentUser.IsAdmin {
|
||||
func User(app *lishwist.Session, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||
admin := app.Admin()
|
||||
if admin == nil {
|
||||
return NotFound(h, r)
|
||||
}
|
||||
|
||||
reference := r.PathValue("userReference")
|
||||
|
||||
user, err := db.GetUserByReference(reference)
|
||||
user, err := lishwist.GetUserByReference(reference)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
func UserPost(currentUser *db.User, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||
if !currentUser.IsAdmin {
|
||||
func UserPost(app *lishwist.Session, h http.Header, r *rsvp.Request) rsvp.Response {
|
||||
admin := app.Admin()
|
||||
if admin == nil {
|
||||
return NotFound(h, r)
|
||||
}
|
||||
|
||||
form := r.ParseForm()
|
||||
|
||||
reference := r.PathValue("userReference")
|
||||
if reference == currentUser.Reference {
|
||||
if reference == app.User.Reference {
|
||||
return rsvp.Error(http.StatusForbidden, "You cannot delete yourself.")
|
||||
}
|
||||
|
||||
user, err := db.GetAnyUserByReference(reference)
|
||||
user, err := lishwist.GetUserByReference(reference)
|
||||
if err != nil {
|
||||
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")
|
||||
|
||||
if intent != "" {
|
||||
err = user.SetLive(intent != "delete")
|
||||
err = admin.UserSetLive(reference, intent != "delete")
|
||||
if err != nil {
|
||||
return rsvp.Error(http.StatusInternalServerError, "Failed to delete user: %s", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,32 @@
|
|||
package routing
|
||||
|
||||
import (
|
||||
"lishwist/http/api/db"
|
||||
lishwist "lishwist/core"
|
||||
"lishwist/http/rsvp"
|
||||
"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()
|
||||
newGiftName := form.Get("gift_name")
|
||||
err := currentUser.AddGift(newGiftName)
|
||||
err := app.MakeWish(newGiftName)
|
||||
if err != nil {
|
||||
return rsvp.Error(http.StatusInternalServerError, "Failed to add gift.").LogError(err)
|
||||
}
|
||||
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()
|
||||
targets := form["gift"]
|
||||
err := currentUser.RemoveGifts(targets...)
|
||||
err := app.RevokeWishes(targets...)
|
||||
if err != nil {
|
||||
return rsvp.Error(http.StatusInternalServerError, "Failed to remove gifts.").LogError(err)
|
||||
}
|
||||
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()
|
||||
userReference := r.PathValue("userReference")
|
||||
intent := form.Get("intent")
|
||||
|
|
@ -34,22 +34,22 @@ func ForeignWishlistPost(currentUser *db.User, h http.Header, r *rsvp.Request) r
|
|||
case "claim":
|
||||
claims := form["unclaimed"]
|
||||
unclaims := form["claimed"]
|
||||
err := currentUser.ClaimGifts(claims, unclaims)
|
||||
err := app.ClaimWishes(claims, unclaims)
|
||||
if err != nil {
|
||||
return rsvp.Error(http.StatusInternalServerError, "Failed to update claim...").LogError(err)
|
||||
}
|
||||
case "complete":
|
||||
claims := form["claimed"]
|
||||
err := currentUser.CompleteGifts(claims)
|
||||
err := app.CompleteWishes(claims)
|
||||
if err != nil {
|
||||
return rsvp.Error(http.StatusInternalServerError, "Failed to complete gifts...").LogError(err)
|
||||
}
|
||||
case "add":
|
||||
giftName := form.Get("gift_name")
|
||||
if giftName == "" {
|
||||
wishName := form.Get("gift_name")
|
||||
if wishName == "" {
|
||||
return rsvp.Error(http.StatusBadRequest, "Gift name not provided")
|
||||
}
|
||||
err := currentUser.AddGiftToUser(userReference, giftName)
|
||||
err := app.SuggestWishForUser(userReference, wishName)
|
||||
if err != nil {
|
||||
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"]
|
||||
unclaims := form["claimed"]
|
||||
gifts := append(claims, unclaims...)
|
||||
err := currentUser.RemoveGifts(gifts...)
|
||||
err := app.RecindWishesForUser(gifts...)
|
||||
if err != nil {
|
||||
return rsvp.Error(http.StatusInternalServerError, "Failed to remove gift idea for other user...").LogError(err)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue