mirror of
https://github.com/1f349/site-hosting.git
synced 2025-04-12 22:56:02 +01:00
Add GET /api/v1/sites/:host
This commit is contained in:
parent
79912dc5c4
commit
1f2e9880bc
28
api/api.go
28
api/api.go
@ -16,6 +16,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type apiDB interface {
|
type apiDB interface {
|
||||||
|
GetBranchesByHost(ctx context.Context, arg database.GetBranchesByHostParams) ([]database.Branch, error)
|
||||||
AddSite(ctx context.Context, arg database.AddSiteParams) error
|
AddSite(ctx context.Context, arg database.AddSiteParams) error
|
||||||
UpdateSiteToken(ctx context.Context, arg database.UpdateSiteTokenParams) error
|
UpdateSiteToken(ctx context.Context, arg database.UpdateSiteTokenParams) error
|
||||||
SetBranchEnabled(ctx context.Context, arg database.SetBranchEnabledParams) error
|
SetBranchEnabled(ctx context.Context, arg database.SetBranchEnabledParams) error
|
||||||
@ -38,6 +39,33 @@ func New(upload *upload.Handler, keyStore *mjwt.KeyStore, db apiDB) *httprouter.
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Site lookup endpoint
|
||||||
|
router.GET("/api/v1/sites/:host", checkAuth(keyStore, func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, b AuthClaims) {
|
||||||
|
host := params.ByName("host")
|
||||||
|
|
||||||
|
if !validation.IsValidHost(host) {
|
||||||
|
http.Error(rw, "Invalid host", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !validateDomainOwnershipClaims(host, b.Claims.Perms) {
|
||||||
|
http.Error(rw, "Forbidden", http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
branches, err := db.GetBranchesByHost(req.Context(), database.GetBranchesByHostParams{
|
||||||
|
Domain: host,
|
||||||
|
DomainWildcard: "%." + host,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
http.Error(rw, "Failed to fetch sites", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rw.WriteHeader(http.StatusOK)
|
||||||
|
_ = json.NewEncoder(rw).Encode(branches)
|
||||||
|
}))
|
||||||
|
|
||||||
// Site creation endpoint
|
// Site creation endpoint
|
||||||
router.PUT("/api/v1/sites/:host", checkAuth(keyStore, func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, b AuthClaims) {
|
router.PUT("/api/v1/sites/:host", checkAuth(keyStore, func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, b AuthClaims) {
|
||||||
host := params.ByName("host")
|
host := params.ByName("host")
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.25.0
|
// sqlc v1.28.0
|
||||||
|
|
||||||
package database
|
package database
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.25.0
|
// sqlc v1.28.0
|
||||||
|
|
||||||
package database
|
package database
|
||||||
|
|
||||||
|
@ -4,6 +4,12 @@ FROM sites
|
|||||||
WHERE domain = ?
|
WHERE domain = ?
|
||||||
LIMIT 1;
|
LIMIT 1;
|
||||||
|
|
||||||
|
-- name: GetBranchesByHost :many
|
||||||
|
SELECT *
|
||||||
|
FROM branches
|
||||||
|
WHERE domain = ?
|
||||||
|
OR domain LIKE @domain_wildcard;
|
||||||
|
|
||||||
-- name: GetLastUpdatedByDomainBranch :one
|
-- name: GetLastUpdatedByDomainBranch :one
|
||||||
SELECT last_update
|
SELECT last_update
|
||||||
FROM branches
|
FROM branches
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.25.0
|
// sqlc v1.28.0
|
||||||
// source: sites.sql
|
// source: sites.sql
|
||||||
|
|
||||||
package database
|
package database
|
||||||
@ -47,6 +47,46 @@ func (q *Queries) AddSite(ctx context.Context, arg AddSiteParams) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getBranchesByHost = `-- name: GetBranchesByHost :many
|
||||||
|
SELECT domain, branch, last_update, enable
|
||||||
|
FROM branches
|
||||||
|
WHERE domain = ?
|
||||||
|
OR domain LIKE ?
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetBranchesByHostParams struct {
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
DomainWildcard string `json:"domain_wildcard"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetBranchesByHost(ctx context.Context, arg GetBranchesByHostParams) ([]Branch, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, getBranchesByHost, arg.Domain, arg.DomainWildcard)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []Branch
|
||||||
|
for rows.Next() {
|
||||||
|
var i Branch
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.Domain,
|
||||||
|
&i.Branch,
|
||||||
|
&i.LastUpdate,
|
||||||
|
&i.Enable,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
const getLastUpdatedByDomainBranch = `-- name: GetLastUpdatedByDomainBranch :one
|
const getLastUpdatedByDomainBranch = `-- name: GetLastUpdatedByDomainBranch :one
|
||||||
SELECT last_update
|
SELECT last_update
|
||||||
FROM branches
|
FROM branches
|
||||||
|
@ -1,5 +1,27 @@
|
|||||||
package validation
|
package validation
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// IsValidHost ensures a host string is valid.
|
||||||
|
//
|
||||||
|
// - Each rune must match 0-9, a-z, "-" or ".".
|
||||||
|
// - Hosts are separated by "." into segments and each segment must not be empty.
|
||||||
|
// - Naturally this also ensures the host does not start or end with ".".
|
||||||
|
// - Host segments must not start or end with "-".
|
||||||
|
func IsValidHost(domain string) bool {
|
||||||
|
if !containsOnly(domain, isDomainRune) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
segments := strings.Split(domain, ".")
|
||||||
|
for _, segment := range segments {
|
||||||
|
if segment == "" || strings.HasPrefix(segment, "-") || strings.HasSuffix(segment, "-") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func IsValidSite(site string) bool {
|
func IsValidSite(site string) bool {
|
||||||
if len(site) < 1 || site[0] == '-' {
|
if len(site) < 1 || site[0] == '-' {
|
||||||
return false
|
return false
|
||||||
|
@ -5,6 +5,35 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestIsValidHost(t *testing.T) {
|
||||||
|
for _, i := range []struct {
|
||||||
|
s string
|
||||||
|
valid bool
|
||||||
|
}{
|
||||||
|
{"example.com", true},
|
||||||
|
{"example.org", true},
|
||||||
|
{"foobar.example.com", true},
|
||||||
|
{"foobar.example.com_", false},
|
||||||
|
{"foobar.example.com[", false},
|
||||||
|
{"foobar.example.com]", false},
|
||||||
|
{"foobar.example.com<", false},
|
||||||
|
{"foobar.example.com/", false},
|
||||||
|
{"foobar.example.com?", false},
|
||||||
|
{"foobar.example.com@", false},
|
||||||
|
{"foobar.example.com!", false},
|
||||||
|
{"foobar.example..com", false},
|
||||||
|
{"foobar.example-.com", false},
|
||||||
|
{"foobar.-example.com", false},
|
||||||
|
{"-foobar.example.com", false},
|
||||||
|
{"foobar.example.com-", false},
|
||||||
|
{"foobar..example..com", false},
|
||||||
|
{".example.com", false},
|
||||||
|
{"example.com.", false},
|
||||||
|
} {
|
||||||
|
assert.Equal(t, i.valid, IsValidHost(i.s), "Test failed \"%s\" - %v", i.s, i.valid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestIsValidSite(t *testing.T) {
|
func TestIsValidSite(t *testing.T) {
|
||||||
for _, i := range []struct {
|
for _, i := range []struct {
|
||||||
s string
|
s string
|
||||||
|
Loading…
x
Reference in New Issue
Block a user