town/invites/invites.go

155 lines
2.5 KiB
Go
Raw Normal View History

2023-02-24 20:58:38 +00:00
package invites
import (
2023-03-10 03:21:26 +00:00
"crypto/rand"
"database/sql"
2023-02-26 23:02:51 +00:00
"encoding/base64"
2023-03-06 20:44:19 +00:00
"errors"
2023-03-10 03:21:26 +00:00
"math/big"
2023-02-26 23:02:51 +00:00
"strings"
"time"
_ "github.com/mattn/go-sqlite3"
)
2023-02-26 23:02:51 +00:00
const (
dsn = "/town/var/invites/invites.db?mode=rw"
codeLen = 32
)
2023-02-28 05:50:55 +00:00
type Invite struct {
ID int64
Created time.Time
Code string
Email string
Used bool
}
2023-03-09 06:33:31 +00:00
func (i *Invite) Insert(db *sql.DB) error {
stmt, err := db.Prepare(`
INSERT INTO invites (code, email) VALUES (?, ?)
`)
if err != nil {
return err
}
2023-03-09 06:39:20 +00:00
i.Code = generateCode(i.Email)
_, err = stmt.Exec(i.Code, i.Email)
2023-03-09 06:33:31 +00:00
if err != nil {
return err
}
defer stmt.Close()
return nil
}
func ConnectDB() (*sql.DB, error) {
db, err := sql.Open("sqlite3", dsn)
if err != nil {
return nil, err
}
return db, nil
}
2023-02-26 23:02:51 +00:00
func generateCode(email string) string {
charset := "abcdefghijklmnopqrztuvwxyz"
charset += strings.ToUpper(charset)
charset += "0123456789"
charset += "`~!@#$%^&*()-=_+[]{}|;:,./<>?"
code := []byte{}
2023-03-10 03:21:26 +00:00
max := big.NewInt(int64(len(charset)))
2023-02-26 23:02:51 +00:00
for len(code) < codeLen {
2023-03-10 03:21:26 +00:00
ix, err := rand.Int(rand.Reader, max)
if err != nil {
// TODO this is bad but I'm just kind of hoping it doesn't happen...often
panic(err)
}
code = append(code, charset[ix.Int64()])
2023-02-26 23:02:51 +00:00
}
code = append(code, ' ')
eb := []byte(email)
for x := 0; x < len(eb); x++ {
code = append(code, eb[x])
}
return base64.StdEncoding.EncodeToString(code)
}
func Decode(code string) ([]string, error) {
decoded, err := base64.StdEncoding.DecodeString(code)
if err != nil {
return nil, err
}
return strings.Split(string(decoded), " "), nil
}
2023-02-28 05:50:55 +00:00
func Get(db *sql.DB, code string) (*Invite, error) {
inv := &Invite{
Code: code,
}
2023-02-28 19:30:07 +00:00
var created string
2023-02-28 05:50:55 +00:00
var used int
stmt, err := db.Prepare(`
SELECT id, created, email, used
FROM invites WHERE code = ?`)
if err != nil {
return nil, err
}
row := stmt.QueryRow(code)
if err != nil {
return nil, err
}
defer stmt.Close()
err = row.Scan(
&inv.ID,
&created,
&inv.Email,
&used,
)
if err != nil {
return nil, err
}
2023-02-28 19:30:07 +00:00
inv.Created, err = time.Parse("2006-01-02T15:04", created)
if err != nil {
return inv, err
}
2023-02-28 05:50:55 +00:00
inv.Used = used > 0
return inv, nil
}
2023-03-06 20:44:19 +00:00
func (i *Invite) MarkUsed(db *sql.DB) (err error) {
var stmt *sql.Stmt
var result sql.Result
var rowsAffected int64
if stmt, err = db.Prepare(`UPDATE invites SET used = 1 WHERE id = ?`); err != nil {
return
}
2023-02-28 05:50:55 +00:00
2023-03-06 20:44:19 +00:00
if result, err = stmt.Exec(i.ID); err != nil {
return
}
if rowsAffected, err = result.RowsAffected(); err != nil {
return
}
if rowsAffected == 0 {
err = errors.New("no rows affected")
}
return
}