This commit is contained in:
Melon 2024-08-16 16:48:50 +01:00
parent c953eb7f67
commit 7a78057213
Signed by: melon
GPG Key ID: 6C9D970C50D26A25
12 changed files with 206 additions and 88 deletions

1
.gitignore vendored
View File

@ -22,3 +22,4 @@
go.work go.work
.idea/ .idea/
.data/

View File

@ -4,12 +4,14 @@ import (
"context" "context"
"errors" "errors"
"flag" "flag"
"github.com/1f349/bluebell"
"github.com/1f349/bluebell/conf" "github.com/1f349/bluebell/conf"
"github.com/1f349/bluebell/logger" "github.com/1f349/bluebell/logger"
"github.com/1f349/bluebell/serve" "github.com/1f349/bluebell/serve"
"github.com/1f349/bluebell/upload" "github.com/1f349/bluebell/upload"
"github.com/charmbracelet/log" "github.com/charmbracelet/log"
"github.com/cloudflare/tableflip" "github.com/cloudflare/tableflip"
"github.com/dustin/go-humanize"
"github.com/julienschmidt/httprouter" "github.com/julienschmidt/httprouter"
"github.com/spf13/afero" "github.com/spf13/afero"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
@ -86,18 +88,30 @@ func main() {
} }
}() }()
db, err := bluebell.InitDB(config.DB)
if err != nil {
logger.Logger.Fatal("Failed to open database", "err", err)
return
}
// Listen must be called before Ready // Listen must be called before Ready
ln, err := upg.Listen("tcp", config.Listen) ln, err := upg.Listen("tcp", config.Listen)
if err != nil { if err != nil {
logger.Logger.Fatal("Listen failed", "err", err) logger.Logger.Fatal("Listen failed", "err", err)
} }
uploadHandler := upload.New(sitesFs) uploadHandler := upload.New(sitesFs, db)
serveHandler := serve.New(sitesFs) serveHandler := serve.New(sitesFs, db, config.Domain)
router := httprouter.New() router := httprouter.New()
router.POST("/u/:site", uploadHandler.Handle) router.POST("/u/:site", uploadHandler.Handle)
router.GET("/*filepath", serveHandler.Handle) router.GET("/*filepath", serveHandler.Handle)
router.POST("/sites/:host", func(rw http.ResponseWriter, req *http.Request, params httprouter.Params) {
})
router.DELETE("/sites/:host", func(rw http.ResponseWriter, req *http.Request, params httprouter.Params) {
})
server := &http.Server{ server := &http.Server{
Handler: router, Handler: router,
@ -105,7 +119,7 @@ func main() {
ReadHeaderTimeout: 1 * time.Minute, ReadHeaderTimeout: 1 * time.Minute,
WriteTimeout: 1 * time.Minute, WriteTimeout: 1 * time.Minute,
IdleTimeout: 1 * time.Minute, IdleTimeout: 1 * time.Minute,
MaxHeaderBytes: 4_096_000, MaxHeaderBytes: 4 * humanize.MiByte,
} }
logger.Logger.Info("HTTP server listening on", "addr", config.Listen) logger.Logger.Info("HTTP server listening on", "addr", config.Listen)
go func() { go func() {

View File

@ -1,20 +1,12 @@
package conf package conf
import "github.com/mrmelon54/trie"
type Conf struct { type Conf struct {
Listen string `yaml:"listen"` Listen string `yaml:"listen"`
//fs afero.Fs DB string `yaml:"db"`
//l *sync.RWMutex Domain string `yaml:"domain"`
m *trie.Trie[SiteConf]
} }
type SiteConf struct { func SlugFromDomain(domain string) string {
Domain string `json:"domain"`
Token string `json:"token"`
}
func (c *Conf) slugFromDomain(domain string) string {
a := []byte(domain) a := []byte(domain)
for i := range a { for i := range a {
switch { switch {

View File

@ -1,6 +1,8 @@
CREATE TABLE sites CREATE TABLE sites
( (
id INTEGER NOT NULL PRIMARY KEY, id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
slug TEXT(8) NOT NULL,
domain TEXT NOT NULL, domain TEXT NOT NULL,
token TEXT NOT NULL token TEXT NOT NULL,
enable BOOL NOT NULL
); );

View File

@ -8,6 +8,8 @@ import ()
type Site struct { type Site struct {
ID int32 `json:"id"` ID int32 `json:"id"`
Slug string `json:"slug"`
Domain string `json:"domain"` Domain string `json:"domain"`
Token string `json:"token"` Token string `json:"token"`
Enable bool `json:"enable"`
} }

View File

@ -1,5 +1,20 @@
-- name: GetSiteBySlug :one
SELECT *
FROM sites
WHERE slug = ?
LIMIT 1;
-- name: GetSiteByDomain :one -- name: GetSiteByDomain :one
SELECT * SELECT *
FROM sites FROM sites
WHERE domain = ? WHERE domain = ?
LIMIT 1; LIMIT 1;
-- name: EnableDomain :exec
INSERT INTO sites (slug, domain, token)
VALUES (?, ?, ?);
-- name: DeleteDomain :exec
UPDATE sites
SET enable = false
WHERE domain = ?;

View File

@ -9,8 +9,35 @@ import (
"context" "context"
) )
const deleteDomain = `-- name: DeleteDomain :exec
UPDATE sites
SET enable = false
WHERE domain = ?
`
func (q *Queries) DeleteDomain(ctx context.Context, domain string) error {
_, err := q.db.ExecContext(ctx, deleteDomain, domain)
return err
}
const enableDomain = `-- name: EnableDomain :exec
INSERT INTO sites (slug, domain, token)
VALUES (?, ?, ?)
`
type EnableDomainParams struct {
Slug string `json:"slug"`
Domain string `json:"domain"`
Token string `json:"token"`
}
func (q *Queries) EnableDomain(ctx context.Context, arg EnableDomainParams) error {
_, err := q.db.ExecContext(ctx, enableDomain, arg.Slug, arg.Domain, arg.Token)
return err
}
const getSiteByDomain = `-- name: GetSiteByDomain :one const getSiteByDomain = `-- name: GetSiteByDomain :one
SELECT id, domain, token SELECT id, slug, domain, token, enable
FROM sites FROM sites
WHERE domain = ? WHERE domain = ?
LIMIT 1 LIMIT 1
@ -19,6 +46,32 @@ LIMIT 1
func (q *Queries) GetSiteByDomain(ctx context.Context, domain string) (Site, error) { func (q *Queries) GetSiteByDomain(ctx context.Context, domain string) (Site, error) {
row := q.db.QueryRowContext(ctx, getSiteByDomain, domain) row := q.db.QueryRowContext(ctx, getSiteByDomain, domain)
var i Site var i Site
err := row.Scan(&i.ID, &i.Domain, &i.Token) err := row.Scan(
&i.ID,
&i.Slug,
&i.Domain,
&i.Token,
&i.Enable,
)
return i, err
}
const getSiteBySlug = `-- name: GetSiteBySlug :one
SELECT id, slug, domain, token, enable
FROM sites
WHERE slug = ?
LIMIT 1
`
func (q *Queries) GetSiteBySlug(ctx context.Context, slug string) (Site, error) {
row := q.db.QueryRowContext(ctx, getSiteBySlug, slug)
var i Site
err := row.Scan(
&i.ID,
&i.Slug,
&i.Domain,
&i.Token,
&i.Enable,
)
return i, err return i, err
} }

6
go.mod
View File

@ -5,6 +5,8 @@ go 1.22
require ( require (
github.com/charmbracelet/log v0.4.0 github.com/charmbracelet/log v0.4.0
github.com/cloudflare/tableflip v1.2.3 github.com/cloudflare/tableflip v1.2.3
github.com/dustin/go-humanize v1.0.1
github.com/golang-migrate/migrate/v4 v4.17.1
github.com/julienschmidt/httprouter v1.3.0 github.com/julienschmidt/httprouter v1.3.0
github.com/mrmelon54/trie v0.0.3 github.com/mrmelon54/trie v0.0.3
github.com/spf13/afero v1.11.0 github.com/spf13/afero v1.11.0
@ -18,12 +20,16 @@ require (
github.com/charmbracelet/x/ansi v0.1.4 // indirect github.com/charmbracelet/x/ansi v0.1.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/go-sql-driver/mysql v1.5.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/muesli/termenv v0.15.2 // indirect github.com/muesli/termenv v0.15.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect
go.uber.org/atomic v1.7.0 // indirect
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa // indirect golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa // indirect
golang.org/x/sys v0.24.0 // indirect golang.org/x/sys v0.24.0 // indirect
golang.org/x/text v0.17.0 // indirect golang.org/x/text v0.17.0 // indirect

68
go.sum
View File

@ -1,7 +1,9 @@
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/charmbracelet/lipgloss v0.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMtwg/AFW3s=
github.com/charmbracelet/lipgloss v0.10.0/go.mod h1:Wig9DSfvANsxqkRsqj6x87irdy123SR4dOXlKa91ciE=
github.com/charmbracelet/lipgloss v0.12.1 h1:/gmzszl+pedQpjCOH+wFkZr/N90Snz40J/NR7A0zQcs= github.com/charmbracelet/lipgloss v0.12.1 h1:/gmzszl+pedQpjCOH+wFkZr/N90Snz40J/NR7A0zQcs=
github.com/charmbracelet/lipgloss v0.12.1/go.mod h1:V2CiwIuhx9S1S1ZlADfOj9HmxeMAORuz5izHb0zGbB8= github.com/charmbracelet/lipgloss v0.12.1/go.mod h1:V2CiwIuhx9S1S1ZlADfOj9HmxeMAORuz5izHb0zGbB8=
github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8ZM= github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8ZM=
@ -10,53 +12,87 @@ github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831
github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/cloudflare/tableflip v1.2.3 h1:8I+B99QnnEWPHOY3fWipwVKxS70LGgUsslG7CSfmHMw= github.com/cloudflare/tableflip v1.2.3 h1:8I+B99QnnEWPHOY3fWipwVKxS70LGgUsslG7CSfmHMw=
github.com/cloudflare/tableflip v1.2.3/go.mod h1:P4gRehmV6Z2bY5ao5ml9Pd8u6kuEnlB37pUFMmv7j2E= github.com/cloudflare/tableflip v1.2.3/go.mod h1:P4gRehmV6Z2bY5ao5ml9Pd8u6kuEnlB37pUFMmv7j2E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dhui/dktest v0.4.1 h1:/w+IWuDXVymg3IrRJCHHOkMK10m9aNVMOyD0X12YVTg=
github.com/dhui/dktest v0.4.1/go.mod h1:DdOqcUpL7vgyP4GlF3X3w7HbSlz8cEQzwewPveYEQbA=
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0=
github.com/docker/docker v24.0.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-migrate/migrate/v4 v4.17.1 h1:4zQ6iqL6t6AiItphxJctQb3cFqWiSpMnX7wLTPnnYO4=
github.com/golang-migrate/migrate/v4 v4.17.1/go.mod h1:m8hinFyWBn0SA4QKHuKh175Pm9wjmxj3S2Mia7dbXzM=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/mrmelon54/trie v0.0.3 h1:wZmws84FiGNBZJ00garLyQ2EQhtx0SipVoV7fK8+kZE= github.com/mrmelon54/trie v0.0.3 h1:wZmws84FiGNBZJ00garLyQ2EQhtx0SipVoV7fK8+kZE=
github.com/mrmelon54/trie v0.0.3/go.mod h1:d3hl0YUBSWR3XN4S9BDLkGVzLT4VgwP2mZkBJM6uFpw= github.com/mrmelon54/trie v0.0.3/go.mod h1:d3hl0YUBSWR3XN4S9BDLkGVzLT4VgwP2mZkBJM6uFpw=
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI= golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI=
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@ -5,6 +5,7 @@ import (
"embed" "embed"
"errors" "errors"
"github.com/1f349/bluebell/database" "github.com/1f349/bluebell/database"
"github.com/golang-migrate/migrate/v4"
"github.com/golang-migrate/migrate/v4/database/mysql" "github.com/golang-migrate/migrate/v4/database/mysql"
"github.com/golang-migrate/migrate/v4/source/iofs" "github.com/golang-migrate/migrate/v4/source/iofs"
) )

View File

@ -1,10 +1,13 @@
package serve package serve
import ( import (
"context"
"github.com/1f349/bluebell/conf" "github.com/1f349/bluebell/conf"
"github.com/1f349/bluebell/database"
"github.com/julienschmidt/httprouter" "github.com/julienschmidt/httprouter"
"github.com/spf13/afero" "github.com/spf13/afero"
"io" "io"
"net"
"net/http" "net/http"
"os" "os"
"path" "path"
@ -24,78 +27,59 @@ var (
} }
) )
func New(config conf.Conf, storage afero.Fs) *Handler { type sitesQueries interface {
return &Handler{config, storage} GetSiteByDomain(ctx context.Context, domain string) (database.Site, error)
}
func New(storage afero.Fs, db sitesQueries, domain string) *Handler {
return &Handler{storage, db, domain}
} }
type Handler struct { type Handler struct {
conf conf.Conf
storageFs afero.Fs storageFs afero.Fs
db sitesQueries
domain string
} }
func (h *Handler) Handle(rw http.ResponseWriter, req *http.Request, params httprouter.Params) { func (h *Handler) Handle(rw http.ResponseWriter, req *http.Request, _ httprouter.Params) {
site, branch, subdomain, ok := h.findSiteBranchSubdomain(req.Host) host, _, err := net.SplitHostPort(req.Host)
if err != nil {
http.Error(rw, "Bad Gateway", http.StatusBadGateway)
return
}
site, ok := strings.CutSuffix(host, "."+h.domain)
if !ok { if !ok {
http.Error(rw, "Bad Gateway", http.StatusBadGateway) http.Error(rw, "Bad Gateway", http.StatusBadGateway)
return return
} }
site = conf.SlugFromDomain(site)
branch := req.URL.User.Username()
if branch == "" { if branch == "" {
for _, i := range indexBranches { for _, i := range indexBranches {
if h.tryServePath(rw, site, i, subdomain, req.URL.Path) { if h.tryServePath(rw, site, i, req.URL.Path) {
return return
} }
} }
} else if h.tryServePath(rw, site, branch, subdomain, req.URL.Path) { } else if h.tryServePath(rw, site, branch, req.URL.Path) {
return return
} }
http.Error(rw, "404 Not Found", http.StatusNotFound) http.Error(rw, "404 Not Found", http.StatusNotFound)
} }
func (h *Handler) findSiteBranchSubdomain(host string) (site, branch, subdomain string, ok bool) { func (h *Handler) tryServePath(rw http.ResponseWriter, site, branch, p string) bool {
var siteN int
siteN, site = h.findSite(host)
if site == "" {
return
}
if host[siteN] != '-' {
return
}
host = host[siteN+1:]
strings.LastIndexByte(host, '-')
return
}
func (h *Handler) findSite(host string) (int, string) {
siteVal, siteN, siteOk := h.conf.Get(host)
if !siteOk || siteVal == nil {
return -1, ""
}
// so I used less than or equal here that's to prevent a bug where the prefix
// found is longer than the string obviously that sounds impossible, and it is,
// but I would rather the program not crash if some other bug allows this weird
// event to happen
if siteN <= len(host) {
return -1, ""
}
return siteN, siteVal.Domain
}
func (h *Handler) tryServePath(rw http.ResponseWriter, site, branch, subdomain, p string) bool {
for _, i := range indexFiles { for _, i := range indexFiles {
if h.tryServeFile(rw, site, branch, subdomain, i(p)) { if h.tryServeFile(rw, site, branch, i(p)) {
return true return true
} }
} }
return false return false
} }
func (h *Handler) tryServeFile(rw http.ResponseWriter, site, branch, subdomain, p string) bool { func (h *Handler) tryServeFile(rw http.ResponseWriter, site, branch, p string) bool {
// if there is a subdomain then load files from inside the subdomain folder // prevent path traversal
if subdomain != "" { if strings.Contains(site, "..") || strings.Contains(branch, "..") || strings.Contains(p, "..") {
p = filepath.Join("_subdomain", subdomain, p) http.Error(rw, "400 Bad Request", http.StatusBadRequest)
return true
} }
open, err := h.storageFs.Open(filepath.Join(site, branch, p)) open, err := h.storageFs.Open(filepath.Join(site, branch, p))
switch { switch {

View File

@ -3,8 +3,10 @@ package upload
import ( import (
"archive/tar" "archive/tar"
"compress/gzip" "compress/gzip"
"context"
"fmt" "fmt"
"github.com/1f349/bluebell/conf" "github.com/1f349/bluebell/database"
"github.com/dustin/go-humanize"
"github.com/julienschmidt/httprouter" "github.com/julienschmidt/httprouter"
"github.com/spf13/afero" "github.com/spf13/afero"
"io" "io"
@ -12,25 +14,35 @@ import (
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"strings"
) )
func New(storage afero.Fs) *Handler { type sitesQueries interface {
return &Handler{storage, conf} GetSiteBySlug(ctx context.Context, slug string) (database.Site, error)
GetSiteByDomain(ctx context.Context, domain string) (database.Site, error)
} }
func New(storage afero.Fs, db sitesQueries) *Handler {
return &Handler{storage, db}
}
const maxFileSize = 1 * humanize.GiByte
type Handler struct { type Handler struct {
storageFs afero.Fs storageFs afero.Fs
conf *conf.Conf db sitesQueries
} }
func (h *Handler) Handle(rw http.ResponseWriter, req *http.Request, params httprouter.Params) { func (h *Handler) Handle(rw http.ResponseWriter, req *http.Request, _ httprouter.Params) {
q := req.URL.Query() q := req.URL.Query()
site := q.Get("site") site := q.Get("site")
branch := q.Get("branch") branch := q.Get("branch")
siteConf, siteN, siteOk := h.conf.Get(site) site = strings.ReplaceAll(site, "*", "")
if !siteOk || siteN != len(site) || siteConf == nil {
http.Error(rw, "400 Bad Request", http.StatusBadRequest) siteConf, err := h.db.GetSiteByDomain(req.Context(), "*"+site)
if err != nil {
http.Error(rw, "", http.StatusNotFound)
return return
} }
if "Bearer "+siteConf.Token != req.Header.Get("Authorization") { if "Bearer "+siteConf.Token != req.Header.Get("Authorization") {
@ -45,7 +57,7 @@ func (h *Handler) Handle(rw http.ResponseWriter, req *http.Request, params httpr
} }
// if file is bigger than 1GiB // if file is bigger than 1GiB
if fileHeader.Size > 1074000000 { if fileHeader.Size > maxFileSize {
http.Error(rw, "File too big", http.StatusBadRequest) http.Error(rw, "File too big", http.StatusBadRequest)
return return
} }