From fbe0863d3634b2d6d50e3c5044a7ffc721fde5de Mon Sep 17 00:00:00 2001 From: MrMelon54 Date: Wed, 8 Jan 2025 01:03:05 +0000 Subject: [PATCH] Change primary keys and remove id column --- api/api.go | 95 ++++++++++++++++++- ...0250107235645_change-primary-keys.down.sql | 30 ++++++ .../20250107235645_change-primary-keys.up.sql | 29 ++++++ database/models.go | 2 - database/queries/sites.sql | 19 +++- database/sites.sql.go | 76 +++++++++++++-- upload/upload_test.go | 1 - 7 files changed, 234 insertions(+), 18 deletions(-) create mode 100644 database/migrations/20250107235645_change-primary-keys.down.sql create mode 100644 database/migrations/20250107235645_change-primary-keys.up.sql diff --git a/api/api.go b/api/api.go index 1cc07d6..e0e7f38 100644 --- a/api/api.go +++ b/api/api.go @@ -2,6 +2,8 @@ package api import ( "context" + "crypto/rand" + "encoding/hex" "encoding/json" "github.com/1f349/bluebell/database" "github.com/1f349/bluebell/upload" @@ -14,18 +16,95 @@ import ( ) type apiDB interface { - SetDomainBranchEnabled(ctx context.Context, arg database.SetDomainBranchEnabledParams) error + AddSite(ctx context.Context, arg database.AddSiteParams) error + UpdateSiteToken(ctx context.Context, arg database.UpdateSiteTokenParams) error + SetBranchEnabled(ctx context.Context, arg database.SetBranchEnabledParams) error } func New(upload *upload.Handler, keyStore *mjwt.KeyStore, db apiDB) *httprouter.Router { router := httprouter.New() + + // Site upload endpoint router.POST("/u/:site/:branch", upload.Handle) + + // Site creation endpoint + router.PUT("/sites/:host", checkAuth(keyStore, func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, b AuthClaims) { + host := params.ByName("host") + + if !validation.IsValidSite(host) { + http.Error(rw, "Invalid site", http.StatusBadRequest) + return + } + + if !validateDomainOwnershipClaims(host, b.Claims.Perms) { + http.Error(rw, "Forbidden", http.StatusForbidden) + return + } + + token, err := generateToken() + if err != nil { + http.Error(rw, "Failed to generate token", http.StatusInternalServerError) + return + } + + err = db.AddSite(req.Context(), database.AddSiteParams{ + Domain: host, + Token: token, + }) + if err != nil { + http.Error(rw, "Failed to register site", http.StatusInternalServerError) + return + } + + rw.WriteHeader(http.StatusOK) + _ = json.NewEncoder(rw).Encode(struct { + Token string `json:"token"` + }{Token: token}) + })) + + // Reset site token endpoint + router.POST("/sites/:host/reset-token", checkAuth(keyStore, func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, b AuthClaims) { + host := params.ByName("host") + + if !validation.IsValidSite(host) { + http.Error(rw, "Invalid site", http.StatusBadRequest) + return + } + + if !validateDomainOwnershipClaims(host, b.Claims.Perms) { + http.Error(rw, "Forbidden", http.StatusForbidden) + return + } + + token, err := generateToken() + if err != nil { + http.Error(rw, "Failed to generate token", http.StatusInternalServerError) + return + } + + err = db.UpdateSiteToken(req.Context(), database.UpdateSiteTokenParams{ + Domain: host, + Token: token, + }) + if err != nil { + http.Error(rw, "Failed to register site", http.StatusInternalServerError) + return + } + + rw.WriteHeader(http.StatusOK) + _ = json.NewEncoder(rw).Encode(struct { + Token string `json:"token"` + }{Token: token}) + })) + + // Enable/disable site branch router.PUT("/sites/:host/:branch/enable", checkAuth(keyStore, func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, b AuthClaims) { setEnabled(rw, req, params, b, db, true) })) router.DELETE("/sites/:host/:branch/enable", checkAuth(keyStore, func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, b AuthClaims) { setEnabled(rw, req, params, b, db, false) })) + return router } @@ -48,7 +127,7 @@ func setEnabled(rw http.ResponseWriter, req *http.Request, params httprouter.Par return } - err := db.SetDomainBranchEnabled(req.Context(), database.SetDomainBranchEnabledParams{ + err := db.SetBranchEnabled(req.Context(), database.SetBranchEnabledParams{ Domain: host, Branch: branch, Enable: enable, @@ -57,7 +136,17 @@ func setEnabled(rw http.ResponseWriter, req *http.Request, params httprouter.Par http.Error(rw, "Failed to update branch state", http.StatusInternalServerError) return } - rw.WriteHeader(http.StatusAccepted) + + rw.WriteHeader(http.StatusOK) +} + +func generateToken() (string, error) { + b := make([]byte, 32) + _, err := rand.Read(b) + if err != nil { + return "", err + } + return hex.EncodeToString(b), nil } // apiError outputs a generic JSON error message diff --git a/database/migrations/20250107235645_change-primary-keys.down.sql b/database/migrations/20250107235645_change-primary-keys.down.sql new file mode 100644 index 0000000..0c2512c --- /dev/null +++ b/database/migrations/20250107235645_change-primary-keys.down.sql @@ -0,0 +1,30 @@ +ALTER TABLE sites RENAME TO sites_new; +ALTER TABLE branches RENAME TO branches_new; + +CREATE TABLE sites +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + domain TEXT NOT NULL, + token TEXT NOT NULL +); + +CREATE TABLE branches +( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + domain TEXT NOT NULL, + branch TEXT NOT NULL, + last_update DATETIME NOT NULL, + enable BOOLEAN NOT NULL +); + + +INSERT INTO sites (domain, token) +SELECT domain, token +FROM sites_new; + +INSERT INTO branches (domain, branch, last_update, enable) +SELECT domain, branch, last_update, enable +FROM branches_new; + +DROP TABLE sites_new; +DROP TABLE branches_new; diff --git a/database/migrations/20250107235645_change-primary-keys.up.sql b/database/migrations/20250107235645_change-primary-keys.up.sql new file mode 100644 index 0000000..5517d66 --- /dev/null +++ b/database/migrations/20250107235645_change-primary-keys.up.sql @@ -0,0 +1,29 @@ +ALTER TABLE sites RENAME TO sites_old; +ALTER TABLE branches RENAME TO branches_old; + +CREATE TABLE sites +( + domain TEXT NOT NULL PRIMARY KEY, + token TEXT NOT NULL +); + +CREATE TABLE branches +( + domain TEXT NOT NULL, + branch TEXT NOT NULL, + last_update DATETIME NOT NULL, + enable BOOLEAN NOT NULL, + + PRIMARY KEY (domain, branch) +); + +INSERT INTO sites +SELECT domain, token +FROM sites_old; + +INSERT INTO branches +SELECT domain, branch, last_update, enable +FROM branches_old; + +DROP TABLE sites_old; +DROP TABLE branches_old; diff --git a/database/models.go b/database/models.go index d042aa2..070c7c7 100644 --- a/database/models.go +++ b/database/models.go @@ -9,7 +9,6 @@ import ( ) type Branch struct { - ID int64 `json:"id"` Domain string `json:"domain"` Branch string `json:"branch"` LastUpdate time.Time `json:"last_update"` @@ -17,7 +16,6 @@ type Branch struct { } type Site struct { - ID int64 `json:"id"` Domain string `json:"domain"` Token string `json:"token"` } diff --git a/database/queries/sites.sql b/database/queries/sites.sql index edd85f5..5e463fe 100644 --- a/database/queries/sites.sql +++ b/database/queries/sites.sql @@ -12,11 +12,26 @@ WHERE domain = ? AND enable = true LIMIT 1; --- name: AddSiteDomain :exec +-- name: AddSite :exec INSERT INTO sites (domain, token) VALUES (?, ?); --- name: SetDomainBranchEnabled :exec +-- name: UpdateSiteToken :exec +UPDATE sites +SET token = ? +WHERE domain = ?; + +-- name: AddBranch :exec +INSERT INTO branches (domain, branch, last_update, enable) +VALUES (?, ?, ?, ?); + +-- name: UpdateBranch :exec +UPDATE branches +SET last_update = ? +WHERE domain = ? + AND branch = ?; + +-- name: SetBranchEnabled :exec UPDATE branches SET enable = ? WHERE domain = ? diff --git a/database/sites.sql.go b/database/sites.sql.go index 961027e..392e518 100644 --- a/database/sites.sql.go +++ b/database/sites.sql.go @@ -10,18 +10,40 @@ import ( "time" ) -const addSiteDomain = `-- name: AddSiteDomain :exec +const addBranch = `-- name: AddBranch :exec +INSERT INTO branches (domain, branch, last_update, enable) +VALUES (?, ?, ?, ?) +` + +type AddBranchParams struct { + Domain string `json:"domain"` + Branch string `json:"branch"` + LastUpdate time.Time `json:"last_update"` + Enable bool `json:"enable"` +} + +func (q *Queries) AddBranch(ctx context.Context, arg AddBranchParams) error { + _, err := q.db.ExecContext(ctx, addBranch, + arg.Domain, + arg.Branch, + arg.LastUpdate, + arg.Enable, + ) + return err +} + +const addSite = `-- name: AddSite :exec INSERT INTO sites (domain, token) VALUES (?, ?) ` -type AddSiteDomainParams struct { +type AddSiteParams struct { Domain string `json:"domain"` Token string `json:"token"` } -func (q *Queries) AddSiteDomain(ctx context.Context, arg AddSiteDomainParams) error { - _, err := q.db.ExecContext(ctx, addSiteDomain, arg.Domain, arg.Token) +func (q *Queries) AddSite(ctx context.Context, arg AddSiteParams) error { + _, err := q.db.ExecContext(ctx, addSite, arg.Domain, arg.Token) return err } @@ -47,7 +69,7 @@ func (q *Queries) GetLastUpdatedByDomainBranch(ctx context.Context, arg GetLastU } const getSiteByDomain = `-- name: GetSiteByDomain :one -SELECT id, domain, token +SELECT domain, token FROM sites WHERE domain = ? LIMIT 1 @@ -56,24 +78,58 @@ LIMIT 1 func (q *Queries) GetSiteByDomain(ctx context.Context, domain string) (Site, error) { row := q.db.QueryRowContext(ctx, getSiteByDomain, domain) var i Site - err := row.Scan(&i.ID, &i.Domain, &i.Token) + err := row.Scan(&i.Domain, &i.Token) return i, err } -const setDomainBranchEnabled = `-- name: SetDomainBranchEnabled :exec +const setBranchEnabled = `-- name: SetBranchEnabled :exec UPDATE branches SET enable = ? WHERE domain = ? AND branch = ? ` -type SetDomainBranchEnabledParams struct { +type SetBranchEnabledParams struct { Enable bool `json:"enable"` Domain string `json:"domain"` Branch string `json:"branch"` } -func (q *Queries) SetDomainBranchEnabled(ctx context.Context, arg SetDomainBranchEnabledParams) error { - _, err := q.db.ExecContext(ctx, setDomainBranchEnabled, arg.Enable, arg.Domain, arg.Branch) +func (q *Queries) SetBranchEnabled(ctx context.Context, arg SetBranchEnabledParams) error { + _, err := q.db.ExecContext(ctx, setBranchEnabled, arg.Enable, arg.Domain, arg.Branch) + return err +} + +const updateBranch = `-- name: UpdateBranch :exec +UPDATE branches +SET last_update = ? +WHERE domain = ? + AND branch = ? +` + +type UpdateBranchParams struct { + LastUpdate time.Time `json:"last_update"` + Domain string `json:"domain"` + Branch string `json:"branch"` +} + +func (q *Queries) UpdateBranch(ctx context.Context, arg UpdateBranchParams) error { + _, err := q.db.ExecContext(ctx, updateBranch, arg.LastUpdate, arg.Domain, arg.Branch) + return err +} + +const updateSiteToken = `-- name: UpdateSiteToken :exec +UPDATE sites +SET token = ? +WHERE domain = ? +` + +type UpdateSiteTokenParams struct { + Token string `json:"token"` + Domain string `json:"domain"` +} + +func (q *Queries) UpdateSiteToken(ctx context.Context, arg UpdateSiteTokenParams) error { + _, err := q.db.ExecContext(ctx, updateSiteToken, arg.Token, arg.Domain) return err } diff --git a/upload/upload_test.go b/upload/upload_test.go index 1cf5030..1bdbdfe 100644 --- a/upload/upload_test.go +++ b/upload/upload_test.go @@ -48,7 +48,6 @@ type fakeUploadDB struct { func (f *fakeUploadDB) GetSiteByDomain(_ context.Context, domain string) (database.Site, error) { if domain == "example.com" { return database.Site{ - ID: 1, Domain: "example.com", Token: "abcd1234", }, nil