Merge branch 'dev' into feature/blog
THAT WAS PAINFUL!
This commit is contained in:
commit
3e5ecb9372
99 changed files with 2029 additions and 1010 deletions
|
|
@ -29,6 +29,11 @@ func GetAllArtists(db *sqlx.DB) ([]*model.Artist, error) {
|
|||
|
||||
return artists, nil
|
||||
}
|
||||
func GetArtistCount(db *sqlx.DB) (int, error) {
|
||||
var count int
|
||||
err := db.Get(&count, "SELECT count(*) FROM artist")
|
||||
return count, err
|
||||
}
|
||||
|
||||
func GetArtistsNotOnRelease(db *sqlx.DB, releaseID string) ([]*model.Artist, error) {
|
||||
var artists = []*model.Artist{}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package controller
|
|||
import (
|
||||
"arimelody-web/model"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
|
@ -32,7 +31,7 @@ func FetchThreadViewPost(actorID string, postID string) (*model.ThreadViewPost,
|
|||
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, errors.New(fmt.Sprintf("Failed to call Bluesky API: %v", err))
|
||||
return nil, fmt.Errorf("Failed to call Bluesky API: %v", err)
|
||||
}
|
||||
|
||||
type Data struct {
|
||||
|
|
@ -41,7 +40,7 @@ func FetchThreadViewPost(actorID string, postID string) (*model.ThreadViewPost,
|
|||
data := Data{}
|
||||
err = json.NewDecoder(res.Body).Decode(&data)
|
||||
if err != nil {
|
||||
return nil, errors.New(fmt.Sprintf("Invalid response from server: %v", err))
|
||||
return nil, fmt.Errorf("Invalid response from server: %v", err)
|
||||
}
|
||||
|
||||
return &data.Thread, nil
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import "math/rand"
|
|||
func GenerateAlnumString(length int) []byte {
|
||||
const CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
res := []byte{}
|
||||
for i := 0; i < length; i++ {
|
||||
for range length {
|
||||
res = append(res, CHARS[rand.Intn(len(CHARS))])
|
||||
}
|
||||
return res
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
"embed"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
const DB_VERSION int = 5
|
||||
|
|
@ -59,10 +60,13 @@ func CheckDBVersionAndMigrate(db *sqlx.DB) {
|
|||
fmt.Printf("Database schema up to date.\n")
|
||||
}
|
||||
|
||||
//go:embed "schema-migration"
|
||||
var schemaFS embed.FS
|
||||
|
||||
func ApplyMigration(db *sqlx.DB, scriptFile string) {
|
||||
fmt.Printf("Applying schema migration %s...\n", scriptFile)
|
||||
|
||||
bytes, err := os.ReadFile("schema-migration/" + scriptFile + ".sql")
|
||||
bytes, err := schemaFS.ReadFile("schema-migration/" + scriptFile + ".sql")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Failed to open schema file \"%s\": %v\n", scriptFile, err)
|
||||
os.Exit(1)
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ package controller
|
|||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"image"
|
||||
"image/color"
|
||||
// "image"
|
||||
// "image/color"
|
||||
|
||||
"github.com/skip2/go-qrcode"
|
||||
)
|
||||
|
|
@ -18,36 +18,35 @@ func GenerateQRCode(data string) (string, error) {
|
|||
}
|
||||
|
||||
// vvv DEPRECATED vvv
|
||||
|
||||
const margin = 4
|
||||
|
||||
type QRCodeECCLevel int64
|
||||
const (
|
||||
LOW QRCodeECCLevel = iota
|
||||
MEDIUM
|
||||
QUARTILE
|
||||
HIGH
|
||||
)
|
||||
|
||||
func drawLargeAlignmentSquare(x int, y int, img *image.Gray) {
|
||||
for yi := range 7 {
|
||||
for xi := range 7 {
|
||||
if (xi == 0 || xi == 6) || (yi == 0 || yi == 6) {
|
||||
img.Set(x + xi, y + yi, color.Black)
|
||||
} else if (xi > 1 && xi < 5) && (yi > 1 && yi < 5) {
|
||||
img.Set(x + xi, y + yi, color.Black)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func drawSmallAlignmentSquare(x int, y int, img *image.Gray) {
|
||||
for yi := range 5 {
|
||||
for xi := range 5 {
|
||||
if (xi == 0 || xi == 4) || (yi == 0 || yi == 4) {
|
||||
img.Set(x + xi, y + yi, color.Black)
|
||||
}
|
||||
}
|
||||
}
|
||||
img.Set(x + 2, y + 2, color.Black)
|
||||
}
|
||||
// const margin = 4
|
||||
//
|
||||
// type QRCodeECCLevel int64
|
||||
// const (
|
||||
// LOW QRCodeECCLevel = iota
|
||||
// MEDIUM
|
||||
// QUARTILE
|
||||
// HIGH
|
||||
// )
|
||||
//
|
||||
// func drawLargeAlignmentSquare(x int, y int, img *image.Gray) {
|
||||
// for yi := range 7 {
|
||||
// for xi := range 7 {
|
||||
// if (xi == 0 || xi == 6) || (yi == 0 || yi == 6) {
|
||||
// img.Set(x + xi, y + yi, color.Black)
|
||||
// } else if (xi > 1 && xi < 5) && (yi > 1 && yi < 5) {
|
||||
// img.Set(x + xi, y + yi, color.Black)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// func drawSmallAlignmentSquare(x int, y int, img *image.Gray) {
|
||||
// for yi := range 5 {
|
||||
// for xi := range 5 {
|
||||
// if (xi == 0 || xi == 4) || (yi == 0 || yi == 4) {
|
||||
// img.Set(x + xi, y + yi, color.Black)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// img.Set(x + 2, y + 2, color.Black)
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
|
|
@ -22,7 +21,7 @@ func GetRelease(db *sqlx.DB, id string, full bool) (*model.Release, error) {
|
|||
// get credits
|
||||
credits, err := GetReleaseCredits(db, id)
|
||||
if err != nil {
|
||||
return nil, errors.New(fmt.Sprintf("Credits: %s", err))
|
||||
return nil, fmt.Errorf("Credits: %s", err)
|
||||
}
|
||||
for _, credit := range credits {
|
||||
release.Credits = append(release.Credits, credit)
|
||||
|
|
@ -31,7 +30,7 @@ func GetRelease(db *sqlx.DB, id string, full bool) (*model.Release, error) {
|
|||
// get tracks
|
||||
tracks, err := GetReleaseTracks(db, id)
|
||||
if err != nil {
|
||||
return nil, errors.New(fmt.Sprintf("Tracks: %s", err))
|
||||
return nil, fmt.Errorf("Tracks: %s", err)
|
||||
}
|
||||
for _, track := range tracks {
|
||||
release.Tracks = append(release.Tracks, track)
|
||||
|
|
@ -40,7 +39,7 @@ func GetRelease(db *sqlx.DB, id string, full bool) (*model.Release, error) {
|
|||
// get links
|
||||
links, err := GetReleaseLinks(db, id)
|
||||
if err != nil {
|
||||
return nil, errors.New(fmt.Sprintf("Links: %s", err))
|
||||
return nil, fmt.Errorf("Links: %s", err)
|
||||
}
|
||||
for _, link := range links {
|
||||
release.Links = append(release.Links, link)
|
||||
|
|
@ -72,7 +71,7 @@ func GetAllReleases(db *sqlx.DB, onlyVisible bool, limit int, full bool) ([]*mod
|
|||
// get credits
|
||||
credits, err := GetReleaseCredits(db, release.ID)
|
||||
if err != nil {
|
||||
return nil, errors.New(fmt.Sprintf("Credits: %s", err))
|
||||
return nil, fmt.Errorf("Credits: %s", err)
|
||||
}
|
||||
for _, credit := range credits {
|
||||
release.Credits = append(release.Credits, credit)
|
||||
|
|
@ -82,7 +81,7 @@ func GetAllReleases(db *sqlx.DB, onlyVisible bool, limit int, full bool) ([]*mod
|
|||
// get tracks
|
||||
tracks, err := GetReleaseTracks(db, release.ID)
|
||||
if err != nil {
|
||||
return nil, errors.New(fmt.Sprintf("Tracks: %s", err))
|
||||
return nil, fmt.Errorf("Tracks: %s", err)
|
||||
}
|
||||
for _, track := range tracks {
|
||||
release.Tracks = append(release.Tracks, track)
|
||||
|
|
@ -91,7 +90,7 @@ func GetAllReleases(db *sqlx.DB, onlyVisible bool, limit int, full bool) ([]*mod
|
|||
// get links
|
||||
links, err := GetReleaseLinks(db, release.ID)
|
||||
if err != nil {
|
||||
return nil, errors.New(fmt.Sprintf("Links: %s", err))
|
||||
return nil, fmt.Errorf("Links: %s", err)
|
||||
}
|
||||
for _, link := range links {
|
||||
release.Links = append(release.Links, link)
|
||||
|
|
@ -101,6 +100,17 @@ func GetAllReleases(db *sqlx.DB, onlyVisible bool, limit int, full bool) ([]*mod
|
|||
|
||||
return releases, nil
|
||||
}
|
||||
func GetReleaseCount(db *sqlx.DB, onlyVisible bool) (int, error) {
|
||||
query := "SELECT count(*) FROM musicrelease"
|
||||
if onlyVisible {
|
||||
query += " WHERE visible=true"
|
||||
}
|
||||
|
||||
var count int
|
||||
err := db.Get(&count, query)
|
||||
|
||||
return count, err
|
||||
}
|
||||
|
||||
func GetLatestRelease(db *sqlx.DB) (*model.Release, error) {
|
||||
var release = model.Release{}
|
||||
|
|
@ -116,7 +126,7 @@ func GetLatestRelease(db *sqlx.DB) (*model.Release, error) {
|
|||
// get credits
|
||||
credits, err := GetReleaseCredits(db, release.ID)
|
||||
if err != nil {
|
||||
return nil, errors.New(fmt.Sprintf("Credits: %s", err))
|
||||
return nil, fmt.Errorf("Credits: %s", err)
|
||||
}
|
||||
for _, credit := range credits {
|
||||
release.Credits = append(release.Credits, credit)
|
||||
|
|
@ -125,7 +135,7 @@ func GetLatestRelease(db *sqlx.DB) (*model.Release, error) {
|
|||
// get tracks
|
||||
tracks, err := GetReleaseTracks(db, release.ID)
|
||||
if err != nil {
|
||||
return nil, errors.New(fmt.Sprintf("Tracks: %s", err))
|
||||
return nil, fmt.Errorf("Tracks: %s", err)
|
||||
}
|
||||
for _, track := range tracks {
|
||||
release.Tracks = append(release.Tracks, track)
|
||||
|
|
@ -134,7 +144,7 @@ func GetLatestRelease(db *sqlx.DB) (*model.Release, error) {
|
|||
// get links
|
||||
links, err := GetReleaseLinks(db, release.ID)
|
||||
if err != nil {
|
||||
return nil, errors.New(fmt.Sprintf("Links: %s", err))
|
||||
return nil, fmt.Errorf("Links: %s", err)
|
||||
}
|
||||
for _, link := range links {
|
||||
release.Links = append(release.Links, link)
|
||||
|
|
|
|||
165
controller/schema-migration/000-init.sql
Normal file
165
controller/schema-migration/000-init.sql
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
--
|
||||
-- Tables
|
||||
--
|
||||
|
||||
-- Audit logs
|
||||
CREATE TABLE arimelody.auditlog (
|
||||
id UUID DEFAULT gen_random_uuid(),
|
||||
level int NOT NULL DEFAULT 0,
|
||||
type TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT current_timestamp
|
||||
);
|
||||
|
||||
-- Accounts
|
||||
CREATE TABLE arimelody.account (
|
||||
id UUID DEFAULT gen_random_uuid(),
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
password TEXT NOT NULL,
|
||||
email TEXT,
|
||||
avatar_url TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
fail_attempts INT NOT NULL DEFAULT 0,
|
||||
locked BOOLEAN DEFAULT false
|
||||
);
|
||||
ALTER TABLE arimelody.account ADD CONSTRAINT account_pk PRIMARY KEY (id);
|
||||
|
||||
-- Privilege
|
||||
CREATE TABLE arimelody.privilege (
|
||||
account UUID NOT NULL,
|
||||
privilege TEXT NOT NULL
|
||||
);
|
||||
ALTER TABLE arimelody.privilege ADD CONSTRAINT privilege_pk PRIMARY KEY (account, privilege);
|
||||
|
||||
-- Invites
|
||||
CREATE TABLE arimelody.invite (
|
||||
code text NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
expires_at TIMESTAMP NOT NULL
|
||||
);
|
||||
ALTER TABLE arimelody.invite ADD CONSTRAINT invite_pk PRIMARY KEY (code);
|
||||
|
||||
-- Sessions
|
||||
CREATE TABLE arimelody.session (
|
||||
token TEXT,
|
||||
user_agent TEXT NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
expires_at TIMESTAMP DEFAULT NULL,
|
||||
account UUID,
|
||||
attempt_account UUID,
|
||||
message TEXT,
|
||||
error TEXT
|
||||
);
|
||||
ALTER TABLE arimelody.session ADD CONSTRAINT session_pk PRIMARY KEY (token);
|
||||
|
||||
-- TOTP methods
|
||||
CREATE TABLE arimelody.totp (
|
||||
name TEXT NOT NULL,
|
||||
account UUID NOT NULL,
|
||||
secret TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
confirmed BOOLEAN DEFAULT false
|
||||
);
|
||||
ALTER TABLE arimelody.totp ADD CONSTRAINT totp_pk PRIMARY KEY (account, name);
|
||||
|
||||
|
||||
|
||||
-- Artists (should be applicable to all art)
|
||||
CREATE TABLE arimelody.artist (
|
||||
id character varying(64),
|
||||
name text NOT NULL,
|
||||
website text,
|
||||
avatar text
|
||||
);
|
||||
ALTER TABLE arimelody.artist ADD CONSTRAINT artist_pk PRIMARY KEY (id);
|
||||
|
||||
-- Music releases
|
||||
CREATE TABLE arimelody.musicrelease (
|
||||
id character varying(64) NOT NULL,
|
||||
visible bool DEFAULT false,
|
||||
title text NOT NULL,
|
||||
description text,
|
||||
type text,
|
||||
release_date TIMESTAMP NOT NULL,
|
||||
artwork text,
|
||||
buyname text,
|
||||
buylink text,
|
||||
copyright text,
|
||||
copyrightURL text,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT current_timestamp
|
||||
);
|
||||
ALTER TABLE arimelody.musicrelease ADD CONSTRAINT musicrelease_pk PRIMARY KEY (id);
|
||||
|
||||
-- Music links (external platform links under a release)
|
||||
CREATE TABLE arimelody.musiclink (
|
||||
release character varying(64) NOT NULL,
|
||||
name text NOT NULL,
|
||||
url text NOT NULL
|
||||
);
|
||||
ALTER TABLE arimelody.musiclink ADD CONSTRAINT musiclink_pk PRIMARY KEY (release, name);
|
||||
|
||||
-- Music credits (artist credits under a release)
|
||||
CREATE TABLE arimelody.musiccredit (
|
||||
release character varying(64) NOT NULL,
|
||||
artist character varying(64) NOT NULL,
|
||||
role text NOT NULL,
|
||||
is_primary boolean DEFAULT false
|
||||
);
|
||||
ALTER TABLE arimelody.musiccredit ADD CONSTRAINT musiccredit_pk PRIMARY KEY (release, artist);
|
||||
|
||||
-- Music tracks (tracks under a release)
|
||||
CREATE TABLE arimelody.musictrack (
|
||||
id uuid DEFAULT gen_random_uuid(),
|
||||
title text NOT NULL,
|
||||
description text,
|
||||
lyrics text,
|
||||
preview_url text
|
||||
);
|
||||
ALTER TABLE arimelody.musictrack ADD CONSTRAINT musictrack_pk PRIMARY KEY (id);
|
||||
|
||||
-- Music release/track pairs
|
||||
CREATE TABLE arimelody.musicreleasetrack (
|
||||
release character varying(64) NOT NULL,
|
||||
track uuid NOT NULL,
|
||||
number integer NOT NULL
|
||||
);
|
||||
ALTER TABLE arimelody.musicreleasetrack ADD CONSTRAINT musicreleasetrack_pk PRIMARY KEY (release, track);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE arimelody.blogpost (
|
||||
id TEXT NOT NULL UNIQUE,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
visible BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
modified_at TIMESTAMP,
|
||||
author UUID NOT NULL,
|
||||
markdown TEXT NOT NULL,
|
||||
html TEXT NOT NULL,
|
||||
bluesky_actor TEXT,
|
||||
bluesky_post TEXT
|
||||
);
|
||||
ALTER TABLE arimelody.blogpost ADD CONSTRAINT blogpost_pk PRIMARY KEY (id);
|
||||
|
||||
|
||||
|
||||
--
|
||||
-- Foreign keys
|
||||
--
|
||||
|
||||
-- Account
|
||||
ALTER TABLE arimelody.privilege ADD CONSTRAINT privilege_account_fk FOREIGN KEY (account) REFERENCES account(id) ON DELETE CASCADE;
|
||||
ALTER TABLE arimelody.session ADD CONSTRAINT session_account_fk FOREIGN KEY (account) REFERENCES account(id) ON DELETE CASCADE;
|
||||
ALTER TABLE arimelody.session ADD CONSTRAINT session_attempt_account_fk FOREIGN KEY (account) REFERENCES account(id) ON DELETE CASCADE;
|
||||
ALTER TABLE arimelody.totp ADD CONSTRAINT totp_account_fk FOREIGN KEY (account) REFERENCES account(id) ON DELETE CASCADE;
|
||||
|
||||
-- Music
|
||||
ALTER TABLE arimelody.musiccredit ADD CONSTRAINT musiccredit_artist_fk FOREIGN KEY (artist) REFERENCES artist(id) ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE arimelody.musiccredit ADD CONSTRAINT musiccredit_release_fk FOREIGN KEY (release) REFERENCES musicrelease(id) ON DELETE CASCADE;
|
||||
ALTER TABLE arimelody.musiclink ADD CONSTRAINT musiclink_release_fk FOREIGN KEY (release) REFERENCES musicrelease(id) ON UPDATE CASCADE ON DELETE CASCADE;
|
||||
ALTER TABLE arimelody.musicreleasetrack ADD CONSTRAINT music_pair_trackref_fk FOREIGN KEY (release) REFERENCES musicrelease(id) ON DELETE CASCADE;
|
||||
ALTER TABLE arimelody.musicreleasetrack ADD CONSTRAINT music_pair_releaseref_fk FOREIGN KEY (track) REFERENCES musictrack(id) ON DELETE CASCADE;
|
||||
|
||||
-- Blog
|
||||
ALTER TABLE arimelody.blogpost ADD CONSTRAINT blogpost_author_fk FOREIGN KEY (author) REFERENCES account(id) ON DELETE CASCADE;
|
||||
58
controller/schema-migration/001-pre-versioning.sql
Normal file
58
controller/schema-migration/001-pre-versioning.sql
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
--
|
||||
-- New items
|
||||
--
|
||||
|
||||
-- Accounts
|
||||
CREATE TABLE arimelody.account (
|
||||
id UUID DEFAULT gen_random_uuid(),
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
password TEXT NOT NULL,
|
||||
email TEXT,
|
||||
avatar_url TEXT,
|
||||
created_at TIMESTAMP DEFAULT current_timestamp
|
||||
);
|
||||
ALTER TABLE arimelody.account ADD CONSTRAINT account_pk PRIMARY KEY (id);
|
||||
|
||||
-- Privilege
|
||||
CREATE TABLE arimelody.privilege (
|
||||
account UUID NOT NULL,
|
||||
privilege TEXT NOT NULL
|
||||
);
|
||||
ALTER TABLE arimelody.privilege ADD CONSTRAINT privilege_pk PRIMARY KEY (account, privilege);
|
||||
|
||||
-- Invites
|
||||
CREATE TABLE arimelody.invite (
|
||||
code text NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
expires_at TIMESTAMP NOT NULL
|
||||
);
|
||||
ALTER TABLE arimelody.invite ADD CONSTRAINT invite_pk PRIMARY KEY (code);
|
||||
|
||||
-- Sessions
|
||||
CREATE TABLE arimelody.session (
|
||||
token TEXT,
|
||||
user_agent TEXT NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
expires_at TIMESTAMP DEFAULT NULL,
|
||||
account UUID,
|
||||
attempt_account UUID,
|
||||
message TEXT,
|
||||
error TEXT
|
||||
);
|
||||
ALTER TABLE arimelody.session ADD CONSTRAINT session_pk PRIMARY KEY (token);
|
||||
|
||||
-- TOTP methods
|
||||
CREATE TABLE arimelody.totp (
|
||||
name TEXT NOT NULL,
|
||||
account UUID NOT NULL,
|
||||
secret TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
confirmed BOOLEAN DEFAULT false
|
||||
);
|
||||
ALTER TABLE arimelody.totp ADD CONSTRAINT totp_pk PRIMARY KEY (account, name);
|
||||
|
||||
-- Foreign keys
|
||||
ALTER TABLE arimelody.privilege ADD CONSTRAINT privilege_account_fk FOREIGN KEY (account) REFERENCES account(id) ON DELETE CASCADE;
|
||||
ALTER TABLE arimelody.session ADD CONSTRAINT session_account_fk FOREIGN KEY (account) REFERENCES account(id) ON DELETE CASCADE;
|
||||
ALTER TABLE arimelody.session ADD CONSTRAINT session_attempt_account_fk FOREIGN KEY (account) REFERENCES account(id) ON DELETE CASCADE;
|
||||
ALTER TABLE arimelody.totp ADD CONSTRAINT totp_account_fk FOREIGN KEY (account) REFERENCES account(id) ON DELETE CASCADE;
|
||||
12
controller/schema-migration/002-audit-logs.sql
Normal file
12
controller/schema-migration/002-audit-logs.sql
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
-- Audit logs
|
||||
CREATE TABLE arimelody.auditlog (
|
||||
id UUID DEFAULT gen_random_uuid(),
|
||||
level int NOT NULL DEFAULT 0,
|
||||
type TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT current_timestamp
|
||||
);
|
||||
|
||||
-- Need moar timestamps
|
||||
ALTER TABLE arimelody.musicrelease ADD COLUMN created_at TIMESTAMP NOT NULL DEFAULT current_timestamp;
|
||||
ALTER TABLE arimelody.account ALTER COLUMN created_at SET NOT NULL;
|
||||
3
controller/schema-migration/003-fail-lock.sql
Normal file
3
controller/schema-migration/003-fail-lock.sql
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
-- it would be nice to prevent brute-forcing
|
||||
ALTER TABLE arimelody.account ADD COLUMN fail_attempts INT NOT NULL DEFAULT 0;
|
||||
ALTER TABLE arimelody.account ADD COLUMN locked BOOLEAN DEFAULT false;
|
||||
15
controller/schema-migration/004-blog.sql
Normal file
15
controller/schema-migration/004-blog.sql
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
CREATE TABLE arimelody.blogpost (
|
||||
id TEXT NOT NULL UNIQUE,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
visible BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT current_timestamp,
|
||||
modified_at TIMESTAMP,
|
||||
author UUID NOT NULL,
|
||||
markdown TEXT NOT NULL,
|
||||
html TEXT NOT NULL,
|
||||
bluesky_actor TEXT,
|
||||
bluesky_post TEXT
|
||||
);
|
||||
ALTER TABLE arimelody.blogpost ADD CONSTRAINT blogpost_pk PRIMARY KEY (id);
|
||||
ALTER TABLE arimelody.blogpost ADD CONSTRAINT blogpost_author_fk FOREIGN KEY (author) REFERENCES account(id) ON DELETE CASCADE;
|
||||
|
|
@ -2,7 +2,6 @@ package controller
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
|
@ -19,7 +18,7 @@ const TOKEN_LEN = 64
|
|||
func GetSessionFromRequest(app *model.AppState, r *http.Request) (*model.Session, error) {
|
||||
sessionCookie, err := r.Cookie(model.COOKIE_TOKEN)
|
||||
if err != nil && err != http.ErrNoCookie {
|
||||
return nil, errors.New(fmt.Sprintf("Failed to retrieve session cookie: %v", err))
|
||||
return nil, fmt.Errorf("Failed to retrieve session cookie: %v", err)
|
||||
}
|
||||
|
||||
var session *model.Session
|
||||
|
|
@ -29,7 +28,7 @@ func GetSessionFromRequest(app *model.AppState, r *http.Request) (*model.Session
|
|||
session, err = GetSession(app.DB, sessionCookie.Value)
|
||||
|
||||
if err != nil && !strings.Contains(err.Error(), "no rows") {
|
||||
return nil, errors.New(fmt.Sprintf("Failed to retrieve session: %v", err))
|
||||
return nil, fmt.Errorf("Failed to retrieve session: %v", err)
|
||||
}
|
||||
|
||||
if session != nil {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,11 @@ func GetAllTracks(db *sqlx.DB) ([]*model.Track, error) {
|
|||
|
||||
return tracks, nil
|
||||
}
|
||||
func GetTrackCount(db *sqlx.DB) (int, error) {
|
||||
var count int
|
||||
err := db.Get(&count, "SELECT count(*) FROM musictrack")
|
||||
return count, err
|
||||
}
|
||||
|
||||
func GetOrphanTracks(db *sqlx.DB) ([]*model.Track, error) {
|
||||
var tracks = []*model.Track{}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue