package towndb import ( "database/sql" "fmt" "time" _ "github.com/mattn/go-sqlite3" ) const dsn = "/town/var/town.db?mode=rw" type UserState string const ( StateActive = "active" StateTempBan = "temp_banned" StateBan = "banned" StateDeleted = "deleted" // some users request deletion ) type AdminNote struct { ID int64 Created time.Time AuthorID int64 Content string UserID int64 } func (n *AdminNote) Insert(db *sql.DB) error { var ( err error stmt *sql.Stmt result sql.Result liid int64 ) tx, err := db.Begin() if err != nil { return err } defer func() { if err != nil { tx.Rollback() } }() stmt, err = tx.Prepare(` INSERT INTO notes (created, author, content) VALUES (?, ?, ?)`) if err != nil { return err } result, err = stmt.Exec( n.Created.Unix(), n.AuthorID, n.Content) if err != nil { return err } liid, err = result.LastInsertId() if err != nil { return err } n.ID = liid stmt, err = tx.Prepare(` INSERT INTO user_notes (noteid, userid) VALUES (?, ?)`) if err != nil { return err } _, err = stmt.Exec(n.ID, n.UserID) if err != nil { return err } return tx.Commit() } type TownUser struct { ID int64 Created time.Time Emails []string Username string Notes []AdminNote State UserState IsAdmin bool } func (u *TownUser) Insert(db *sql.DB) (err error) { var tx *sql.Tx var stmt *sql.Stmt var result sql.Result var liid int64 if tx, err = db.Begin(); err != nil { return err } defer func() { if err != nil { tx.Rollback() } }() if stmt, err = tx.Prepare(` INSERT INTO users (created, username, state, admin) VALUES (?, ?, ?, ?)`); err != nil { return err } if result, err = stmt.Exec( u.Created.Unix(), u.Username, u.State, u.IsAdmin); err != nil { return err } if liid, err = result.LastInsertId(); err != nil { return err } u.ID = liid if len(u.Emails) > 0 { for _, e := range u.Emails { if stmt, err = tx.Prepare(` INSERT INTO emails (address, userid) VALUES (?, ?)`); err != nil { return err } if result, err = stmt.Exec(e, u.ID); err != nil { return err } } } return tx.Commit() } // TODO: really we should have like GetUser or something. but i don't want // to have to populate the struct to do this operation for now ~equa func RenameUser(db *sql.DB, old_name string, new_name string) (err error) { stmt, err := db.Prepare(`UPDATE users SET username = ? WHERE username = ?`) if err != nil { return err } result, err := stmt.Exec(new_name, old_name) if err != nil { return err } rows, err := result.RowsAffected() if err != nil { return err } if rows == 0 { return fmt.Errorf("couldn't find user") } return nil } func ConnectDB() (*sql.DB, error) { db, err := sql.Open("sqlite3", dsn) if err != nil { return nil, err } return db, nil }