diff --git a/.air.toml b/.air.toml index d8c5605..b155769 100644 --- a/.air.toml +++ b/.air.toml @@ -3,7 +3,9 @@ testdata_dir = "testdata" tmp_dir = "tmp" [build] -args_bin = [] +args_bin = [ + "-no-auth" +] bin = "./build/hugo-cms" cmd = "make compile" delay = 1000 @@ -12,7 +14,7 @@ exclude_file = [] exclude_regex = ["_test.go", "build/.*"] exclude_unchanged = false follow_symlink = false -full_bin = "export $(cat .env | xargs) ; make prep-dev init-db ; cd build ; ./hugo-cms" +full_bin = "export $(cat .env | xargs) ; cd build ; ./hugo-cms" include_dir = [] include_ext = ["go", "tpl", "tmpl", "html", "gohtml", "css", "js"] include_file = [] diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 5e97acb..0000000 --- a/Dockerfile +++ /dev/null @@ -1,22 +0,0 @@ -FROM golang:1.23.3 AS builder - -WORKDIR /usr/src/app - -COPY go.mod go.sum ./ -RUN go mod download && go mod verify - -COPY . . -RUN make compile - - -FROM scratch - -COPY --from=builder /usr/src/app/build/ /. -COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ - -WORKDIR / - -ENV DATA_DIR=/data -ENV PORT=3000 - -CMD ["/hugo-cms"] \ No newline at end of file diff --git a/Makefile b/Makefile index c7f2386..ced17bd 100644 --- a/Makefile +++ b/Makefile @@ -1,20 +1,13 @@ .Phony: clean clean: -docker-compose down -v - -rm -rf build + -rm -r build .Phony: prep prep: + -docker-compose up -d mkdir -p build .Phony: compile compile: prep - CGO_ENABLED=0 go build -o ./build/hugo-cms - -.Phony: prep-dev -prep-dev: - docker-compose up -d - -.Phony: init-db -init-db: - go run . -user test@example.com -password test123 \ No newline at end of file + go build -o ./build/hugo-cms \ No newline at end of file diff --git a/assets/css/main.css b/assets/css/main.css index 8fd49e2..143bbbe 100644 --- a/assets/css/main.css +++ b/assets/css/main.css @@ -40,11 +40,6 @@ body.role-site main { } -div.post { - border-bottom: solid thin grey; -} - - form.post-form { display: flex; diff --git a/config/config.go b/config/config.go index e7bd671..1eecf8c 100644 --- a/config/config.go +++ b/config/config.go @@ -6,13 +6,11 @@ import ( ) type Config struct { - DatabaseURL string `env:"DATABASE_URL"` - NetlifyAuthToken string `env:"NETLIFY_AUTH_TOKEN"` - DataDir string `env:"DATA_DIR"` - EncryptedCookieKey string `env:"ENCRYPTED_COOKIE_KEY"` - - DataStagingDir string `env:"DATA_STAGING_DIR,default=staging"` - DataScratchDir string `env:"DATA_SCRATCH_DIR,default=scratch"` + DatabaseURL string `env:"DATABASE_URL"` + NetlifyAuthToken string `env:"NETLIFY_AUTH_TOKEN"` + DataDir string `env:"DATA_DIR"` + DataStagingDir string `env:"DATA_STAGING_DIR,default=staging"` + DataScratchDir string `env:"DATA_SCRATCH_DIR,default=scratch"` } func Load() (cfg Config, err error) { diff --git a/gen/sqlc/dbq/models.go b/gen/sqlc/dbq/models.go index d4052a2..b723142 100644 --- a/gen/sqlc/dbq/models.go +++ b/gen/sqlc/dbq/models.go @@ -156,16 +156,10 @@ type PublishTarget struct { } type Site struct { - ID int64 - OwnerUserID int64 - Name string - Title string - Theme string - Props []byte -} - -type User struct { - ID int64 - Email string - Password string + ID int64 + Name string + Title string + Url string + Theme string + Props []byte } diff --git a/gen/sqlc/dbq/posts.sql.go b/gen/sqlc/dbq/posts.sql.go index 6724a64..de5a4b3 100644 --- a/gen/sqlc/dbq/posts.sql.go +++ b/gen/sqlc/dbq/posts.sql.go @@ -11,17 +11,8 @@ import ( "github.com/jackc/pgx/v5/pgtype" ) -const deletePost = `-- name: DeletePost :exec -DELETE FROM posts WHERE id = $1 -` - -func (q *Queries) DeletePost(ctx context.Context, id int64) error { - _, err := q.db.Exec(ctx, deletePost, id) - return err -} - const getPostWithID = `-- name: GetPostWithID :one -SELECT id, site_id, title, body, state, props, post_date, created_at FROM posts WHERE id = $1 LIMIT 1 +SELECT id, site_id, title, body, state, props, post_date, created_at FROM post WHERE id = $1 LIMIT 1 ` func (q *Queries) GetPostWithID(ctx context.Context, id int64) (Post, error) { @@ -41,7 +32,7 @@ func (q *Queries) GetPostWithID(ctx context.Context, id int64) (Post, error) { } const insertPost = `-- name: InsertPost :one -INSERT INTO posts ( +INSERT INTO post ( site_id, title, body, @@ -79,7 +70,7 @@ func (q *Queries) InsertPost(ctx context.Context, arg InsertPostParams) (int64, } const listPosts = `-- name: ListPosts :many -SELECT id, site_id, title, body, state, props, post_date, created_at FROM posts WHERE site_id = $1 ORDER BY post_date DESC LIMIT 25 +SELECT id, site_id, title, body, state, props, post_date, created_at FROM post WHERE site_id = $1 ORDER BY post_date DESC LIMIT 25 ` func (q *Queries) ListPosts(ctx context.Context, siteID int64) ([]Post, error) { @@ -113,7 +104,7 @@ func (q *Queries) ListPosts(ctx context.Context, siteID int64) ([]Post, error) { const listPublishablePosts = `-- name: ListPublishablePosts :many SELECT id, site_id, title, body, state, props, post_date, created_at -FROM posts +FROM post WHERE id > $1 AND site_id = $2 AND state = 'published' AND post_date <= $3 ORDER BY id LIMIT 100 ` @@ -154,7 +145,7 @@ func (q *Queries) ListPublishablePosts(ctx context.Context, arg ListPublishableP } const updatePost = `-- name: UpdatePost :exec -UPDATE posts SET +UPDATE post SET site_id = $2, title = $3, body = $4, diff --git a/gen/sqlc/dbq/sites.sql.go b/gen/sqlc/dbq/sites.sql.go index 3ba7978..3aea488 100644 --- a/gen/sqlc/dbq/sites.sql.go +++ b/gen/sqlc/dbq/sites.sql.go @@ -10,7 +10,7 @@ import ( ) const getSiteWithID = `-- name: GetSiteWithID :one -SELECT id, owner_user_id, name, title, theme, props FROM sites WHERE id = $1 LIMIT 1 +SELECT id, name, title, url, theme, props FROM site WHERE id = $1 LIMIT 1 ` func (q *Queries) GetSiteWithID(ctx context.Context, id int64) (Site, error) { @@ -18,9 +18,9 @@ func (q *Queries) GetSiteWithID(ctx context.Context, id int64) (Site, error) { var i Site err := row.Scan( &i.ID, - &i.OwnerUserID, &i.Name, &i.Title, + &i.Url, &i.Theme, &i.Props, ) @@ -28,7 +28,7 @@ func (q *Queries) GetSiteWithID(ctx context.Context, id int64) (Site, error) { } const listSites = `-- name: ListSites :one -SELECT id, owner_user_id, name, title, theme, props FROM sites +SELECT id, name, title, url, theme, props FROM site ` func (q *Queries) ListSites(ctx context.Context) (Site, error) { @@ -36,9 +36,9 @@ func (q *Queries) ListSites(ctx context.Context) (Site, error) { var i Site err := row.Scan( &i.ID, - &i.OwnerUserID, &i.Name, &i.Title, + &i.Url, &i.Theme, &i.Props, ) @@ -46,10 +46,10 @@ func (q *Queries) ListSites(ctx context.Context) (Site, error) { } const newSite = `-- name: NewSite :one -INSERT INTO sites ( +INSERT INTO site ( name, - owner_user_id, title, + url, theme, props ) VALUES ($1, $2, $3, $4, $5) @@ -57,18 +57,18 @@ RETURNING id ` type NewSiteParams struct { - Name string - OwnerUserID int64 - Title string - Theme string - Props []byte + Name string + Title string + Url string + Theme string + Props []byte } func (q *Queries) NewSite(ctx context.Context, arg NewSiteParams) (int64, error) { row := q.db.QueryRow(ctx, newSite, arg.Name, - arg.OwnerUserID, arg.Title, + arg.Url, arg.Theme, arg.Props, ) diff --git a/gen/sqlc/dbq/targets.sql.go b/gen/sqlc/dbq/targets.sql.go index 6470e95..12712bd 100644 --- a/gen/sqlc/dbq/targets.sql.go +++ b/gen/sqlc/dbq/targets.sql.go @@ -9,31 +9,8 @@ import ( "context" ) -const getTargetOfSiteRole = `-- name: GetTargetOfSiteRole :one -SELECT id, site_id, role, target_type, url, target_ref FROM publish_targets WHERE site_id = $1 AND role = $2 LIMIT 1 -` - -type GetTargetOfSiteRoleParams struct { - SiteID int64 - Role TargetRole -} - -func (q *Queries) GetTargetOfSiteRole(ctx context.Context, arg GetTargetOfSiteRoleParams) (PublishTarget, error) { - row := q.db.QueryRow(ctx, getTargetOfSiteRole, arg.SiteID, arg.Role) - var i PublishTarget - err := row.Scan( - &i.ID, - &i.SiteID, - &i.Role, - &i.TargetType, - &i.Url, - &i.TargetRef, - ) - return i, err -} - const insertPublishTarget = `-- name: InsertPublishTarget :one -INSERT INTO publish_targets ( +INSERT INTO publish_target ( site_id, role, target_type, @@ -64,12 +41,12 @@ func (q *Queries) InsertPublishTarget(ctx context.Context, arg InsertPublishTarg return id, err } -const listTargetsOfSite = `-- name: ListTargetsOfSite :many -SELECT id, site_id, role, target_type, url, target_ref FROM publish_targets WHERE site_id = $1 +const listPublishTargetsOfRole = `-- name: ListPublishTargetsOfRole :many +SELECT id, site_id, role, target_type, url, target_ref FROM publish_target WHERE site_id = $1 AND role = 'production' ` -func (q *Queries) ListTargetsOfSite(ctx context.Context, siteID int64) ([]PublishTarget, error) { - rows, err := q.db.Query(ctx, listTargetsOfSite, siteID) +func (q *Queries) ListPublishTargetsOfRole(ctx context.Context, siteID int64) ([]PublishTarget, error) { + rows, err := q.db.Query(ctx, listPublishTargetsOfRole, siteID) if err != nil { return nil, err } diff --git a/gen/sqlc/dbq/users.sql.go b/gen/sqlc/dbq/users.sql.go deleted file mode 100644 index c48133a..0000000 --- a/gen/sqlc/dbq/users.sql.go +++ /dev/null @@ -1,53 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.28.0 -// source: users.sql - -package dbq - -import ( - "context" -) - -const addUser = `-- name: AddUser :one -INSERT INTO users ( - email, - password -) VALUES ($1, $2) -ON CONFLICT (email) DO UPDATE SET password = $2 -RETURNING id -` - -type AddUserParams struct { - Email string - Password string -} - -func (q *Queries) AddUser(ctx context.Context, arg AddUserParams) (int64, error) { - row := q.db.QueryRow(ctx, addUser, arg.Email, arg.Password) - var id int64 - err := row.Scan(&id) - return id, err -} - -const getUserByEmail = `-- name: GetUserByEmail :one -SELECT id, email, password FROM users WHERE email = $1 LIMIT 1 -` - -func (q *Queries) GetUserByEmail(ctx context.Context, email string) (User, error) { - row := q.db.QueryRow(ctx, getUserByEmail, email) - var i User - err := row.Scan(&i.ID, &i.Email, &i.Password) - return i, err -} - -const getUserByID = `-- name: GetUserByID :one -SELECT id, email, password FROM users WHERE id = $1 LIMIT 1 -` - -func (q *Queries) GetUserByID(ctx context.Context, id int64) (User, error) { - row := q.db.QueryRow(ctx, getUserByID, id) - var i User - err := row.Scan(&i.ID, &i.Email, &i.Password) - return i, err -} diff --git a/go.mod b/go.mod index f64997c..3eaf20e 100644 --- a/go.mod +++ b/go.mod @@ -10,9 +10,7 @@ require ( require ( github.com/Netflix/go-env v0.1.2 // indirect github.com/andybalholm/brotli v1.1.1 // indirect - github.com/coreos/go-oidc/v3 v3.12.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/go-jose/go-jose/v4 v4.0.2 // indirect github.com/gofiber/fiber/v2 v2.52.6 // indirect github.com/gofiber/fiber/v3 v3.0.0-beta.4 // indirect github.com/gofiber/schema v1.2.0 // indirect @@ -20,7 +18,6 @@ require ( github.com/gofiber/template/html/v2 v2.1.3 // indirect github.com/gofiber/utils v1.1.0 // indirect github.com/gofiber/utils/v2 v2.0.0-beta.7 // indirect - github.com/golang-jwt/jwt/v4 v4.5.1 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect @@ -43,7 +40,6 @@ require ( go.uber.org/atomic v1.7.0 // indirect golang.org/x/crypto v0.32.0 // indirect golang.org/x/net v0.34.0 // indirect - golang.org/x/oauth2 v0.21.0 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.29.0 // indirect golang.org/x/text v0.21.0 // indirect diff --git a/go.sum b/go.sum index 9d4e412..b5d4c9b 100644 --- a/go.sum +++ b/go.sum @@ -2,14 +2,10 @@ github.com/Netflix/go-env v0.1.2 h1:0DRoLR9lECQ9Zqvkswuebm3jJ/2enaDX6Ei8/Z+EnK0= github.com/Netflix/go-env v0.1.2/go.mod h1:WlIhYi++8FlKNJtrop1mjXYAJMzv1f43K4MqCoh0yGE= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= -github.com/coreos/go-oidc/v3 v3.12.0 h1:sJk+8G2qq94rDI6ehZ71Bol3oUHy63qNYmkiSjrc/Jo= -github.com/coreos/go-oidc/v3 v3.12.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk= -github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI= github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= github.com/gofiber/fiber/v3 v3.0.0-beta.4 h1:KzDSavvhG7m81NIsmnu5l3ZDbVS4feCidl4xlIfu6V0= @@ -24,8 +20,6 @@ github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM= github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0= github.com/gofiber/utils/v2 v2.0.0-beta.7 h1:NnHFrRHvhrufPABdWajcKZejz9HnCWmT/asoxRsiEbQ= github.com/gofiber/utils/v2 v2.0.0-beta.7/go.mod h1:J/M03s+HMdZdvhAeyh76xT72IfVqBzuz/OJkrMa7cwU= -github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= -github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-migrate/migrate/v4 v4.18.1 h1:JML/k+t4tpHCpQTCAD62Nu43NUFzHY4CV3uAuvHGC+Y= github.com/golang-migrate/migrate/v4 v4.18.1/go.mod h1:HAX6m3sQgcdO81tdjn5exv20+3Kb13cmGli1hrD6hks= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -82,8 +76,6 @@ golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= -golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/handlers/auth.go b/handlers/auth.go deleted file mode 100644 index 64da6c1..0000000 --- a/handlers/auth.go +++ /dev/null @@ -1,69 +0,0 @@ -package handlers - -import ( - "encoding/json" - "errors" - "github.com/gofiber/fiber/v2" - "lmika.dev/lmika/hugo-cms/models" - "lmika.dev/lmika/hugo-cms/services/users" - "net/http" -) - -type AuthHandler struct { - UserService *users.Service -} - -func (h *AuthHandler) ShowLogin(c *fiber.Ctx) error { - return c.Render("auth/login", fiber.Map{}, "layouts/login") -} - -func (h *AuthHandler) Login(c *fiber.Ctx) error { - var req struct { - Email string `form:"email"` - Password string `form:"password"` - } - - if err := c.BodyParser(&req); err != nil { - return errors.New("invalid email or password") - } - - user, err := h.UserService.VerifyLogin(c.UserContext(), req.Email, req.Password) - if err != nil { - return errors.New("invalid email or password") - } - - bts, err := json.Marshal(models.AuthCookie{UserID: user.ID}) - if err != nil { - return err - } - - c.Cookie(&fiber.Cookie{ - Name: models.AuthCookieName, - Value: string(bts), - }) - return c.Redirect("/", http.StatusFound) -} - -func (h *AuthHandler) RequireAuth(c *fiber.Ctx) error { - user, err := h.readAuthCookie(c) - if err != nil { - return c.Redirect("/auth/login", http.StatusFound) - } - - c.Locals("user", user) - return c.Next() -} - -func (h *AuthHandler) readAuthCookie(c *fiber.Ctx) (user models.User, err error) { - authData := c.Cookies(models.AuthCookieName) - if authData == "" { - return models.User{}, errors.New("no auth cookie") - } - - var ac models.AuthCookie - if err := json.Unmarshal([]byte(authData), &ac); err != nil { - return models.User{}, err - } - - return h.UserService.GetUserByID(c.UserContext(), ac.UserID) -} diff --git a/handlers/ctx.go b/handlers/ctx.go index 9a18d63..4846d87 100644 --- a/handlers/ctx.go +++ b/handlers/ctx.go @@ -1,57 +1,14 @@ package handlers import ( - "encoding/json" - "errors" "github.com/gofiber/fiber/v2" "lmika.dev/lmika/hugo-cms/models" - "log" ) -func GetUser(c *fiber.Ctx) models.User { - u, ok := c.Locals("user").(models.User) - if !ok { - panic(errors.New("user not found in context")) - } - return u -} +type siteKeyType struct{} + +var siteKey siteKeyType func GetSite(c *fiber.Ctx) models.Site { - s, ok := c.Locals("site").(models.Site) - if !ok { - panic(errors.New("no site in context")) - } - return s -} - -func UpdatePrefCookie(c *fiber.Ctx, update func(prefs *models.PrefCookie)) { - cookie := GetPrefCookie(c) - update(&cookie) - setPrefCookie(c, cookie) -} - -func GetPrefCookie(c *fiber.Ctx) models.PrefCookie { - prefCookieValue := c.Cookies(models.PrefCookieName) - if prefCookieValue == "" { - return models.PrefCookie{} - } - - var prefCookie models.PrefCookie - err := json.Unmarshal([]byte(prefCookieValue), &prefCookie) - if err != nil { - return models.PrefCookie{} - } - - return prefCookie -} - -func setPrefCookie(c *fiber.Ctx, prefCookie models.PrefCookie) { - if prefJson, err := json.Marshal(prefCookie); err == nil { - c.Cookie(&fiber.Cookie{ - Name: models.PrefCookieName, - Value: string(prefJson), - }) - } else { - log.Printf("unable to save pref cookie: %v", err) - } + return c.UserContext().Value(siteKey).(models.Site) } diff --git a/handlers/index.go b/handlers/index.go deleted file mode 100644 index dfd4aad..0000000 --- a/handlers/index.go +++ /dev/null @@ -1,19 +0,0 @@ -package handlers - -import ( - "fmt" - "github.com/gofiber/fiber/v2" - "net/http" -) - -type IndexHandler struct { -} - -func (h IndexHandler) Index(c *fiber.Ctx) error { - prefs := GetPrefCookie(c) - if prefs.SiteID > 0 { - return c.Redirect(fmt.Sprintf("/sites/%v/posts", prefs.SiteID), http.StatusFound) - } - - return c.Render("index", fiber.Map{}, "layouts/main") -} diff --git a/handlers/mimeselectors.go b/handlers/mimeselectors.go deleted file mode 100644 index 6a47e8b..0000000 --- a/handlers/mimeselectors.go +++ /dev/null @@ -1,37 +0,0 @@ -package handlers - -import "github.com/gofiber/fiber/v2" - -type mimeTypeHandler interface { - CanHandle(c *fiber.Ctx) bool - Handle(c *fiber.Ctx) error -} - -type HTMX func(c *fiber.Ctx) error - -func (h HTMX) CanHandle(c *fiber.Ctx) bool { - return c.Get("Hx-request") == "true" -} - -func (h HTMX) Handle(c *fiber.Ctx) error { - return h(c) -} - -type Otherwise func(c *fiber.Ctx) error - -func (h Otherwise) CanHandle(c *fiber.Ctx) bool { - return true -} - -func (h Otherwise) Handle(c *fiber.Ctx) error { - return h(c) -} - -func Select(c *fiber.Ctx, mimeTypes ...mimeTypeHandler) error { - for _, mt := range mimeTypes { - if mt.CanHandle(c) { - return mt.Handle(c) - } - } - return c.Status(fiber.StatusInternalServerError).SendString("cant handle response") -} diff --git a/handlers/post.go b/handlers/post.go index cf5bff5..62964db 100644 --- a/handlers/post.go +++ b/handlers/post.go @@ -5,125 +5,121 @@ import ( "github.com/gofiber/fiber/v2" "lmika.dev/lmika/hugo-cms/models" "lmika.dev/lmika/hugo-cms/services/posts" - "net/http" ) type Post struct { Post *posts.Service } -func (h *Post) Posts(c *fiber.Ctx) error { - site := GetSite(c) +func (h *Post) Posts() fiber.Handler { + return func(c *fiber.Ctx) error { + site := GetSite(c) - posts, err := h.Post.ListPostOfSite(c.UserContext(), site) - if err != nil { - return err + posts, err := h.Post.ListPostOfSite(c.UserContext(), site) + if err != nil { + return err + } + + return c.Render("posts/index", fiber.Map{ + "site": site, + "posts": posts, + }, "layouts/site") } - - return c.Render("posts/index", fiber.Map{ - "posts": posts, - }, "layouts/site") } -func (h *Post) New(c *fiber.Ctx) error { - return c.Render("posts/new", fiber.Map{ - "post": models.Post{}, - }, "layouts/site") +func (h *Post) New() fiber.Handler { + return func(c *fiber.Ctx) error { + site := GetSite(c) + + return c.Render("posts/new", fiber.Map{ + "site": site, + "post": models.Post{}, + }, "layouts/site") + } } -func (h *Post) Create(c *fiber.Ctx) error { - site := GetSite(c) - - var req struct { +func (h *Post) Create() fiber.Handler { + type Req struct { Title string `json:"title" form:"title"` Body string `json:"body" form:"body"` } - if err := c.BodyParser(&req); err != nil { - return err - } - _, err := h.Post.Create(c.UserContext(), site, posts.NewPost{ - Title: req.Title, - Body: req.Body, - }) - if err != nil { - return err - } + return func(c *fiber.Ctx) error { + site := GetSite(c) - return c.Redirect(fmt.Sprintf("/sites/%v/posts", site.ID)) + var req Req + if err := c.BodyParser(&req); err != nil { + return err + } + + _, err := h.Post.Create(c.UserContext(), site, posts.NewPost{ + Title: req.Title, + Body: req.Body, + }) + if err != nil { + return err + } + + return c.Redirect(fmt.Sprintf("/sites/%v/posts", site.ID)) + } } -func (h *Post) Edit(c *fiber.Ctx) error { - site := GetSite(c) +func (h *Post) Edit() fiber.Handler { + return func(c *fiber.Ctx) error { + site := GetSite(c) - postID, err := c.ParamsInt("postId") - if err != nil { - return err + postID, err := c.ParamsInt("postId") + if err != nil { + return err + } + + post, err := h.Post.GetPost(c.UserContext(), postID) + if err != nil { + return err + } else if post.SiteID != site.ID { + return fmt.Errorf("post id %v not equal to site id %v", postID, site.ID) + } + + return c.Render("posts/new", fiber.Map{ + "site": site, + "post": post, + }, "layouts/site") } - - post, err := h.Post.GetPost(c.UserContext(), postID) - if err != nil { - return err - } else if post.SiteID != site.ID { - return fmt.Errorf("post id %v not equal to site id %v", postID, site.ID) - } - - return c.Render("posts/new", fiber.Map{ - "post": post, - }, "layouts/site") } -func (h *Post) Update(c *fiber.Ctx) error { - site := GetSite(c) - - postID, err := c.ParamsInt("postId") - if err != nil { - return err - } - - var req struct { +func (h *Post) Update() fiber.Handler { + type Req struct { Title string `json:"title" form:"title"` Body string `json:"body" form:"body"` } - if err := c.BodyParser(&req); err != nil { - return err + + return func(c *fiber.Ctx) error { + site := GetSite(c) + + postID, err := c.ParamsInt("postId") + if err != nil { + return err + } + + var req Req + if err := c.BodyParser(&req); err != nil { + return err + } + + post, err := h.Post.GetPost(c.UserContext(), postID) + if err != nil { + return err + } else if post.SiteID != site.ID { + return fmt.Errorf("post id %v not equal to site id %v", postID, site.ID) + } + + post.Title = req.Title + post.Body = req.Body + + if err := h.Post.Save(c.UserContext(), site, &post); err != nil { + return err + } + + return c.Redirect(fmt.Sprintf("/sites/%v/posts", site.ID)) } - - post, err := h.Post.GetPost(c.UserContext(), postID) - if err != nil { - return err - } else if post.SiteID != site.ID { - return fmt.Errorf("post id %v not equal to site id %v", postID, site.ID) - } - - post.Title = req.Title - post.Body = req.Body - - if err := h.Post.Save(c.UserContext(), site, &post); err != nil { - return err - } - - return c.Redirect(fmt.Sprintf("/sites/%v/posts", site.ID)) -} - -func (h *Post) Delete(c *fiber.Ctx) error { - site := GetSite(c) - - postID, err := c.ParamsInt("postId") - if err != nil { - return err - } - - if err := h.Post.DeletePost(c.UserContext(), site, postID); err != nil { - return err - } - - return Select(c, - HTMX(func(c *fiber.Ctx) error { - return c.Status(http.StatusOK).SendString("") - }), - Otherwise(func(c *fiber.Ctx) error { - return c.Redirect(fmt.Sprintf("/sites/%v/posts", site.ID), http.StatusSeeOther) - }), - ) } diff --git a/handlers/site.go b/handlers/site.go index cded58a..8a6c701 100644 --- a/handlers/site.go +++ b/handlers/site.go @@ -1,13 +1,10 @@ package handlers import ( - "errors" + "context" "fmt" "github.com/gofiber/fiber/v2" - "github.com/jackc/pgx/v5" - "lmika.dev/lmika/hugo-cms/models" "lmika.dev/lmika/hugo-cms/services/sites" - "net/http" "time" ) @@ -15,47 +12,19 @@ type Site struct { Site *sites.Service } -func (s *Site) Create(c *fiber.Ctx) error { - user := GetUser(c) +func (s *Site) Create() fiber.Handler { + return func(c *fiber.Ctx) error { + site, err := s.Site.CreateSite(c.UserContext(), "New Site "+time.Now().Format("2006-01-02 15:04:05")) + if err != nil { + return err + } - site, err := s.Site.CreateSite(c.UserContext(), user, "New Site "+time.Now().Format("2006-01-02 15:04:05")) - if err != nil { - return err + return c.Redirect(fmt.Sprintf("/sites/%v/posts", site.ID)) } - - UpdatePrefCookie(c, func(prefs *models.PrefCookie) { - prefs.SiteID = site.ID - }) - - return c.Redirect(fmt.Sprintf("/sites/%v/posts", site.ID)) } -func (s *Site) Show(c *fiber.Ctx) error { - id, err := c.ParamsInt("siteId") - if err != nil { - return err - } - - site, err := s.Site.GetSite(c.UserContext(), id) - if err != nil { - return err - } - - return c.Render("sites/index", fiber.Map{ - "site": site, - }, "layouts/main") -} - -func (s *Site) Rebuild(c *fiber.Ctx) error { - if err := s.Site.Rebuild(c.UserContext(), GetSite(c)); err != nil { - return err - } - - return c.Redirect(fmt.Sprintf("/sites/%v/posts", GetSite(c).ID)) -} - -func (s *Site) WithSite() fiber.Handler { - return func(c *fiber.Ctx) (err error) { +func (s *Site) Show() fiber.Handler { + return func(c *fiber.Ctx) error { id, err := c.ParamsInt("siteId") if err != nil { return err @@ -66,19 +35,35 @@ func (s *Site) WithSite() fiber.Handler { return err } - user := GetUser(c) - if site.OwnerUserID != user.ID { - return c.Status(http.StatusForbidden).SendString("not permitted") - } + return c.Render("sites/index", fiber.Map{ + "site": site, + }, "layouts/main") + } +} - c.Locals("site", site) - - if prodTarget, err := s.Site.GetProdTargetOfSite(c.UserContext(), int(site.ID)); err == nil { - c.Locals("prodTarget", prodTarget) - } else if !errors.Is(err, pgx.ErrNoRows) { +func (s *Site) Rebuild() fiber.Handler { + return func(c *fiber.Ctx) error { + if err := s.Site.Rebuild(c.UserContext(), GetSite(c)); err != nil { return err } + return c.Redirect(fmt.Sprintf("/sites/%v/posts", GetSite(c).ID)) + } +} + +func (s *Site) WithSite() fiber.Handler { + return func(c *fiber.Ctx) error { + id, err := c.ParamsInt("siteId") + if err != nil { + return err + } + + site, err := s.Site.GetSite(c.UserContext(), id) + if err != nil { + return err + } + + c.SetUserContext(context.WithValue(c.UserContext(), siteKey, site)) return c.Next() } } diff --git a/main.go b/main.go index 0badf3f..bde6e26 100644 --- a/main.go +++ b/main.go @@ -3,10 +3,7 @@ package main import ( "bytes" "context" - "flag" - "fmt" "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/encryptcookie" "github.com/gofiber/fiber/v2/middleware/filesystem" "github.com/gofiber/template/html/v2" "github.com/yuin/goldmark" @@ -23,23 +20,12 @@ import ( "lmika.dev/lmika/hugo-cms/services/posts" "lmika.dev/lmika/hugo-cms/services/sitebuilder" "lmika.dev/lmika/hugo-cms/services/sites" - "lmika.dev/lmika/hugo-cms/services/users" "lmika.dev/lmika/hugo-cms/templates" "log" "net/http" ) func main() { - flagGenKey := flag.Bool("gen-key", false, "Generate a new key") - flagUser := flag.String("user", "", "add new user") - flagPassword := flag.String("password", "", "add new password") - flag.Parse() - - if *flagGenKey { - fmt.Println(encryptcookie.GenerateKey()) - return - } - cfg, err := config.Load() if err != nil { log.Fatal(err) @@ -51,25 +37,6 @@ func main() { } defer dbp.Close() - log.Println("Connected to database") - if err := dbp.Migrate(context.Background()); err != nil { - log.Fatal(err) - } - log.Println("Database migrated") - - userService := users.NewService(dbp) - - if *flagUser != "" { - if _, err := userService.AddUser(context.Background(), users.NewUser{ - Email: *flagUser, - Password: *flagPassword, - }); err != nil { - log.Fatal(err) - } - log.Println("User added") - return - } - hugoProvider, err := hugo.New(cfg.StagingDir(), cfg.ScratchDir()) if err != nil { log.Fatal(err) @@ -84,10 +51,15 @@ func main() { siteService := sites.NewService(cfg, dbp, themesProvider, siteBuilderService, jobService) postService := posts.New(dbp, siteBuilderService, jobService) - indexHandlers := handlers.IndexHandler{} siteHandlers := handlers.Site{Site: siteService} postHandlers := handlers.Post{Post: postService} - authHandlers := handlers.AuthHandler{UserService: userService} + + log.Println("Connected to database") + if err := dbp.Migrate(context.Background()); err != nil { + log.Fatal(err) + } + + log.Println("Database migrated") tmplEngine := html.NewFileSystem(http.FS(templates.FS), ".html") tmplEngine.Funcmap["markdown"] = func(s string) (template.HTML, error) { @@ -98,39 +70,33 @@ func main() { return template.HTML(buf.String()), nil } - if cfg.EncryptedCookieKey == "" { - log.Println("No encrypt cookie key defined. Generating random key") - cfg.EncryptedCookieKey = encryptcookie.GenerateKey() - } - app := fiber.New(fiber.Config{ - Views: tmplEngine, - PassLocalsToViews: true, + Views: tmplEngine, }) - app.Use(encryptcookie.New(encryptcookie.Config{Key: cfg.EncryptedCookieKey})) - app.Use("/assets", filesystem.New(filesystem.Config{Root: http.FS(assets.FS)})) + app.Get("/", func(c *fiber.Ctx) error { + return c.Render("index", fiber.Map{}, "layouts/main") + }) - app.Get("/auth/login", authHandlers.ShowLogin) - app.Post("/auth/login", authHandlers.Login) - app.Use(authHandlers.RequireAuth) - - app.Get("/", indexHandlers.Index) - app.Post("/sites", siteHandlers.Create) - app.Get("/sites/:siteId", siteHandlers.Show) + app.Post("/sites", siteHandlers.Create()) + app.Get("/sites/:siteId", siteHandlers.Show()) app.Route("/sites/:siteId", func(r fiber.Router) { r.Use(siteHandlers.WithSite()) - r.Post("/rebuild", siteHandlers.Rebuild) + r.Post("/rebuild", siteHandlers.Rebuild()) - r.Get("/posts", postHandlers.Posts) - r.Get("/posts/new", postHandlers.New) - r.Post("/posts", postHandlers.Create) - r.Get("/posts/:postId", postHandlers.Edit) - r.Post("/posts/:postId", postHandlers.Update) - r.Delete("/posts/:postId", postHandlers.Delete) + r.Get("/posts", postHandlers.Posts()) + r.Get("/posts/:postId", postHandlers.Edit()) + r.Get("/posts/new", postHandlers.New()) + r.Post("/posts", postHandlers.Create()) + r.Post("/posts/:postId", postHandlers.Update()) }) + app.Use("/assets", filesystem.New(filesystem.Config{ + Root: http.FS(assets.FS), + })) + app.Static("/assets", "./assets") + jobService.Start() defer jobService.Stop() diff --git a/models/cookie.go b/models/cookie.go deleted file mode 100644 index 918a7b4..0000000 --- a/models/cookie.go +++ /dev/null @@ -1,14 +0,0 @@ -package models - -const ( - AuthCookieName = "hugocrm_auth" - PrefCookieName = "hugocrm_pref" -) - -type AuthCookie struct { - UserID int64 `json:"uid"` -} - -type PrefCookie struct { - SiteID int64 `json:"siteId"` -} diff --git a/models/posts.go b/models/posts.go index a95fb36..104625f 100644 --- a/models/posts.go +++ b/models/posts.go @@ -12,7 +12,6 @@ const ( type Post struct { ID int64 SiteID int64 - OwnerID int64 Title string Body string State PostState diff --git a/models/sites.go b/models/sites.go index 3da9fc6..9685c60 100644 --- a/models/sites.go +++ b/models/sites.go @@ -1,9 +1,9 @@ package models type Site struct { - ID int64 - OwnerUserID int64 - Name string - Title string - Theme string + ID int64 + Name string + Title string + URL string + Theme string } diff --git a/models/user.go b/models/user.go deleted file mode 100644 index 7d50a4f..0000000 --- a/models/user.go +++ /dev/null @@ -1,7 +0,0 @@ -package models - -type User struct { - ID int64 - Email string - PasswordHash string -} diff --git a/providers/db/posts.go b/providers/db/posts.go index a647453..e838c3a 100644 --- a/providers/db/posts.go +++ b/providers/db/posts.go @@ -27,10 +27,6 @@ func (db *DB) GetPost(ctx context.Context, postID int64) (models.Post, error) { return dbPostToPost(res), nil } -func (db *DB) DeletePost(ctx context.Context, postID int64) error { - return db.q.DeletePost(ctx, postID) -} - func (db *DB) ListPublishablePosts(ctx context.Context, fromID, siteID int64, now time.Time) ([]models.Post, error) { res, err := db.q.ListPublishablePosts(ctx, dbq.ListPublishablePostsParams{ ID: fromID, diff --git a/providers/db/publish.go b/providers/db/publish.go new file mode 100644 index 0000000..51f035f --- /dev/null +++ b/providers/db/publish.go @@ -0,0 +1,42 @@ +package db + +import ( + "context" + "lmika.dev/lmika/hugo-cms/gen/sqlc/dbq" + "lmika.dev/lmika/hugo-cms/models" + "lmika.dev/pkg/modash/moslice" +) + +func (db *DB) InsertPublishTarget(ctx context.Context, target *models.PublishTarget) error { + id, err := db.q.InsertPublishTarget(ctx, dbq.InsertPublishTargetParams{ + SiteID: target.SiteID, + Role: dbq.TargetRole(target.Role), + TargetType: dbq.TargetType(target.Type), + Url: target.URL, + TargetRef: target.TargetRef, + }) + if err != nil { + return err + } + target.ID = id + + return nil +} + +func (db *DB) GetPublishTargets(ctx context.Context, siteID int64) ([]models.PublishTarget, error) { + res, err := db.q.ListPublishTargetsOfRole(ctx, siteID) + if err != nil { + return nil, err + } + + return moslice.Map(res, func(m dbq.PublishTarget) models.PublishTarget { + return models.PublishTarget{ + ID: m.ID, + SiteID: m.SiteID, + Role: models.TargetRole(m.Role), + Type: models.TargetType(m.TargetType), + URL: m.Url, + TargetRef: m.TargetRef, + } + }), nil +} diff --git a/providers/db/sites.go b/providers/db/sites.go index fac59fa..8e8cc55 100644 --- a/providers/db/sites.go +++ b/providers/db/sites.go @@ -8,11 +8,11 @@ import ( func (db *DB) InsertSite(ctx context.Context, site *models.Site) error { id, err := db.q.NewSite(ctx, dbq.NewSiteParams{ - Name: site.Name, - Title: site.Title, - OwnerUserID: site.OwnerUserID, - Theme: site.Theme, - Props: []byte("{}"), + Name: site.Name, + Title: site.Title, + Url: site.URL, + Theme: site.Theme, + Props: []byte("{}"), }) if err != nil { return err @@ -28,10 +28,9 @@ func (db *DB) GetSite(ctx context.Context, id int64) (models.Site, error) { } return models.Site{ - ID: site.ID, - OwnerUserID: site.OwnerUserID, - Name: site.Name, - Title: site.Title, - Theme: site.Theme, + ID: site.ID, + Name: site.Name, + Title: site.Title, + Theme: site.Theme, }, nil } diff --git a/providers/db/targets.go b/providers/db/targets.go deleted file mode 100644 index 6f004cd..0000000 --- a/providers/db/targets.go +++ /dev/null @@ -1,56 +0,0 @@ -package db - -import ( - "context" - "lmika.dev/lmika/hugo-cms/gen/sqlc/dbq" - "lmika.dev/lmika/hugo-cms/models" - "lmika.dev/pkg/modash/moslice" -) - -func (db *DB) InsertPublishTarget(ctx context.Context, target *models.PublishTarget) error { - id, err := db.q.InsertPublishTarget(ctx, dbq.InsertPublishTargetParams{ - SiteID: target.SiteID, - Role: dbq.TargetRole(target.Role), - TargetType: dbq.TargetType(target.Type), - Url: target.URL, - TargetRef: target.TargetRef, - }) - if err != nil { - return err - } - target.ID = id - - return nil -} - -func (db *DB) GetPublishTargets(ctx context.Context, siteID int64) ([]models.PublishTarget, error) { - res, err := db.q.ListTargetsOfSite(ctx, siteID) - if err != nil { - return nil, err - } - - return moslice.Map(res, dbTargetToTarget), nil -} - -func (db *DB) GetPublishTargetBySiteRole(ctx context.Context, siteID int64, role models.TargetRole) (models.PublishTarget, error) { - target, err := db.q.GetTargetOfSiteRole(ctx, dbq.GetTargetOfSiteRoleParams{ - SiteID: siteID, - Role: dbq.TargetRole(role), - }) - if err != nil { - return models.PublishTarget{}, err - } - - return dbTargetToTarget(target), nil -} - -func dbTargetToTarget(m dbq.PublishTarget) models.PublishTarget { - return models.PublishTarget{ - ID: m.ID, - SiteID: m.SiteID, - Role: models.TargetRole(m.Role), - Type: models.TargetType(m.TargetType), - URL: m.Url, - TargetRef: m.TargetRef, - } -} diff --git a/providers/db/user.go b/providers/db/user.go deleted file mode 100644 index 1962ad3..0000000 --- a/providers/db/user.go +++ /dev/null @@ -1,45 +0,0 @@ -package db - -import ( - "context" - "lmika.dev/lmika/hugo-cms/gen/sqlc/dbq" - "lmika.dev/lmika/hugo-cms/models" -) - -func (db *DB) AddUser(ctx context.Context, user *models.User) error { - id, err := db.q.AddUser(ctx, dbq.AddUserParams{ - Email: user.Email, - Password: user.PasswordHash, - }) - if err != nil { - return err - } - user.ID = id - return nil -} - -func (db *DB) GetUserByID(ctx context.Context, id int64) (models.User, error) { - res, err := db.q.GetUserByID(ctx, id) - if err != nil { - return models.User{}, err - } - - return dbUserToUser(res), nil -} - -func (db *DB) GetUserByEmail(ctx context.Context, email string) (models.User, error) { - res, err := db.q.GetUserByEmail(ctx, email) - if err != nil { - return models.User{}, err - } - - return dbUserToUser(res), nil -} - -func dbUserToUser(u dbq.User) models.User { - return models.User{ - ID: u.ID, - Email: u.Email, - PasswordHash: u.Password, - } -} diff --git a/services/posts/services.go b/services/posts/services.go index 902c14c..ce3cd5a 100644 --- a/services/posts/services.go +++ b/services/posts/services.go @@ -40,19 +40,6 @@ func (s *Service) GetPost(ctx context.Context, id int) (models.Post, error) { return post, nil } -func (s *Service) DeletePost(ctx context.Context, site models.Site, id int) error { - post, err := s.db.GetPost(ctx, int64(id)) - if err != nil { - return err - } - - if err := s.db.DeletePost(ctx, int64(id)); err != nil { - return err - } - - return s.jobs.Queue(ctx, s.sb.DeletePost(site, post)) -} - func (s *Service) Create(ctx context.Context, site models.Site, req NewPost) (models.Post, error) { post := models.Post{ SiteID: site.ID, diff --git a/services/sitebuilder/posts.go b/services/sitebuilder/posts.go index 1a7a135..9dd7829 100644 --- a/services/sitebuilder/posts.go +++ b/services/sitebuilder/posts.go @@ -47,41 +47,13 @@ func (s *Service) WriteAllPosts(site models.Site) models.Job { } } -func (s *Service) DeletePost(site models.Site, post models.Post) models.Job { - return models.Jobs( - models.Job{ - Do: func(ctx context.Context) error { - themeMeta, ok := s.themes.Lookup(site.Theme) - if !ok { - return errors.New("theme not found") - } - - postFilename := s.postFilename(site, themeMeta, post) - - if _, err := os.Stat(postFilename); err != nil { - if errors.Is(err, os.ErrNotExist) { - return nil - } - return err - } - - if os.Remove(postFilename) != nil { - return nil - } - return nil - }, - }, - s.Publish(site), - ) -} - func (s *Service) writePost(site models.Site, post models.Post) error { themeMeta, ok := s.themes.Lookup(site.Theme) if !ok { return errors.New("theme not found") } - postFilename := s.postFilename(site, themeMeta, post) + postFilename := filepath.Join(s.hugo.SiteStagingDir(site, hugo.ContentSiteDir), themeMeta.PostDir, post.CreatedAt.Format("2006-01-02-150405.md")) log.Printf(" .. post %v", postFilename) @@ -124,7 +96,3 @@ func (s *Service) writePost(site models.Site, post models.Post) error { return nil } - -func (s *Service) postFilename(site models.Site, themeMeta models.ThemeMeta, post models.Post) string { - return filepath.Join(s.hugo.SiteStagingDir(site, hugo.ContentSiteDir), themeMeta.PostDir, post.CreatedAt.Format("2006-01-02-150405.md")) -} diff --git a/services/sites/service.go b/services/sites/service.go index 53a0492..346f749 100644 --- a/services/sites/service.go +++ b/services/sites/service.go @@ -41,16 +41,11 @@ func (s *Service) GetSite(ctx context.Context, id int) (models.Site, error) { return s.db.GetSite(ctx, int64(id)) } -func (s *Service) GetProdTargetOfSite(ctx context.Context, siteID int) (models.PublishTarget, error) { - return s.db.GetPublishTargetBySiteRole(ctx, int64(siteID), models.TargetRoleProduction) -} - -func (s *Service) CreateSite(ctx context.Context, user models.User, name string) (models.Site, error) { +func (s *Service) CreateSite(ctx context.Context, name string) (models.Site, error) { newSite := models.Site{ - Name: normaliseName(name), - OwnerUserID: user.ID, - Title: name, - Theme: "bear", + Name: normaliseName(name), + Title: name, + Theme: "bear", } _, ok := s.themes.Lookup(newSite.Theme) @@ -67,7 +62,7 @@ func (s *Service) CreateSite(ctx context.Context, user models.User, name string) SiteID: newSite.ID, Role: models.TargetRoleProduction, Type: models.TargetTypeNetlify, - URL: "https://meek-meringue-060cfc.netlify.app/", + URL: "https://meek-meringue-060cfc.netlify.app", TargetRef: "e628dc6e-e6e1-45a9-847a-982adef940a8", }); err != nil { return models.Site{}, err diff --git a/services/users/service.go b/services/users/service.go deleted file mode 100644 index 1fcd925..0000000 --- a/services/users/service.go +++ /dev/null @@ -1,76 +0,0 @@ -package users - -import ( - "context" - "encoding/base64" - "golang.org/x/crypto/bcrypt" - "lmika.dev/lmika/hugo-cms/models" - "lmika.dev/lmika/hugo-cms/providers/db" - "log" -) - -type Service struct { - dbp *db.DB -} - -func NewService(dbp *db.DB) *Service { - return &Service{ - dbp: dbp, - } -} - -func (s *Service) AddUser(ctx context.Context, newUser NewUser) (models.User, error) { - passwd, err := bcrypt.GenerateFromPassword([]byte(newUser.Password), bcrypt.DefaultCost) - if err != nil { - return models.User{}, err - } - - user := models.User{ - Email: newUser.Email, - PasswordHash: base64.StdEncoding.EncodeToString(passwd), - } - if err := s.dbp.AddUser(ctx, &user); err != nil { - return models.User{}, err - } - - return user, nil -} - -func (s *Service) GetUserByID(ctx context.Context, id int64) (models.User, error) { - return s.dbp.GetUserByID(ctx, id) -} - -func (s *Service) VerifyLogin(ctx context.Context, email string, password string) (models.User, error) { - user, err := s.dbp.GetUserByEmail(ctx, email) - if err != nil { - log.Println("User not found") - return models.User{}, err - } - - pwdHash, err := base64.StdEncoding.DecodeString(user.PasswordHash) - if err != nil { - return models.User{}, err - } - - err = bcrypt.CompareHashAndPassword(pwdHash, []byte(password)) - if err != nil { - log.Println("Password incorrect") - return models.User{}, err - } - - return user, nil -} - -func (s *Service) GetUserByEmail(ctx context.Context, email string) (models.User, error) { - user, err := s.dbp.GetUserByEmail(ctx, email) - if err != nil { - return models.User{}, err - } - - return user, nil -} - -type NewUser struct { - Email string `json:"email"` - Password string `json:"password"` -} diff --git a/sql/queries/posts.sql b/sql/queries/posts.sql index 1ddf3cf..34130ad 100644 --- a/sql/queries/posts.sql +++ b/sql/queries/posts.sql @@ -1,17 +1,17 @@ -- name: ListPosts :many -SELECT * FROM posts WHERE site_id = $1 ORDER BY post_date DESC LIMIT 25; +SELECT * FROM post WHERE site_id = $1 ORDER BY post_date DESC LIMIT 25; -- name: GetPostWithID :one -SELECT * FROM posts WHERE id = $1 LIMIT 1; +SELECT * FROM post WHERE id = $1 LIMIT 1; -- name: ListPublishablePosts :many SELECT * -FROM posts +FROM post WHERE id > $1 AND site_id = $2 AND state = 'published' AND post_date <= $3 ORDER BY id LIMIT 100; -- name: InsertPost :one -INSERT INTO posts ( +INSERT INTO post ( site_id, title, body, @@ -23,7 +23,7 @@ INSERT INTO posts ( RETURNING id; -- name: UpdatePost :exec -UPDATE posts SET +UPDATE post SET site_id = $2, title = $3, body = $4, @@ -31,7 +31,4 @@ UPDATE posts SET props = $6, post_date = $7 -- updated_at = $7 -WHERE id = $1; - --- name: DeletePost :exec -DELETE FROM posts WHERE id = $1; \ No newline at end of file +WHERE id = $1; \ No newline at end of file diff --git a/sql/queries/sites.sql b/sql/queries/sites.sql index c71346c..d03f924 100644 --- a/sql/queries/sites.sql +++ b/sql/queries/sites.sql @@ -1,14 +1,14 @@ -- name: ListSites :one -SELECT * FROM sites; +SELECT * FROM site; -- name: GetSiteWithID :one -SELECT * FROM sites WHERE id = $1 LIMIT 1; +SELECT * FROM site WHERE id = $1 LIMIT 1; -- name: NewSite :one -INSERT INTO sites ( +INSERT INTO site ( name, - owner_user_id, title, + url, theme, props ) VALUES ($1, $2, $3, $4, $5) diff --git a/sql/queries/targets.sql b/sql/queries/targets.sql index 011cc4d..55be1d6 100644 --- a/sql/queries/targets.sql +++ b/sql/queries/targets.sql @@ -1,11 +1,8 @@ --- name: ListTargetsOfSite :many -SELECT * FROM publish_targets WHERE site_id = $1; - --- name: GetTargetOfSiteRole :one -SELECT * FROM publish_targets WHERE site_id = $1 AND role = $2 LIMIT 1; +-- name: ListPublishTargetsOfRole :many +SELECT * FROM publish_target WHERE site_id = $1 AND role = 'production'; -- name: InsertPublishTarget :one -INSERT INTO publish_targets ( +INSERT INTO publish_target ( site_id, role, target_type, diff --git a/sql/queries/users.sql b/sql/queries/users.sql deleted file mode 100644 index cb01e46..0000000 --- a/sql/queries/users.sql +++ /dev/null @@ -1,13 +0,0 @@ --- name: AddUser :one -INSERT INTO users ( - email, - password -) VALUES ($1, $2) -ON CONFLICT (email) DO UPDATE SET password = $2 -RETURNING id; - --- name: GetUserByID :one -SELECT * FROM users WHERE id = $1 LIMIT 1; - --- name: GetUserByEmail :one -SELECT * FROM users WHERE email = $1 LIMIT 1; \ No newline at end of file diff --git a/sql/schema/1_init.up.sql b/sql/schema/1_init.up.sql index 38492d1..15b6253 100644 --- a/sql/schema/1_init.up.sql +++ b/sql/schema/1_init.up.sql @@ -11,24 +11,16 @@ CREATE TYPE target_type AS ENUM ( 'netlify' ); -CREATE TABLE users ( - id BIGSERIAL NOT NULL PRIMARY KEY, - email TEXT NOT NULL UNIQUE, - password TEXT NOT NULL +CREATE TABLE site ( + id BIGSERIAL NOT NULL PRIMARY KEY, + name TEXT NOT NULL UNIQUE, + title TEXT NOT NULL, + url TEXT NOT NULL, + theme TEXT NOT NULL, + props JSON NOT NULL ); -CREATE TABLE sites ( - id BIGSERIAL NOT NULL PRIMARY KEY, - owner_user_id BIGINT NOT NULL, - name TEXT NOT NULL UNIQUE, - title TEXT NOT NULL, - theme TEXT NOT NULL, - props JSON NOT NULL, - - FOREIGN KEY (owner_user_id) REFERENCES users (id) -); - -CREATE TABLE posts ( +CREATE TABLE post ( id BIGSERIAL NOT NULL PRIMARY KEY, site_id BIGINT NOT NULL, title TEXT, @@ -38,10 +30,10 @@ CREATE TABLE posts ( post_date TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP NOT NULL, - FOREIGN KEY (site_id) REFERENCES sites (id) ON DELETE CASCADE + FOREIGN KEY (site_id) REFERENCES site (id) ); -CREATE TABLE publish_targets ( +CREATE TABLE publish_target ( id BIGSERIAL NOT NULL PRIMARY KEY, site_id BIGINT NOT NULL, role target_role NOT NULL, @@ -49,6 +41,5 @@ CREATE TABLE publish_targets ( url TEXT NOT NULL, target_ref TEXT NOT NULL, - FOREIGN KEY (site_id) REFERENCES sites (id) ON DELETE CASCADE, - UNIQUE (site_id, role) + FOREIGN KEY (site_id) REFERENCES site (id) ); \ No newline at end of file diff --git a/templates/auth/login.html b/templates/auth/login.html deleted file mode 100644 index 1cd4f0a..0000000 --- a/templates/auth/login.html +++ /dev/null @@ -1,11 +0,0 @@ -
-

- -

-

- -

-

- -

-
\ No newline at end of file diff --git a/templates/fs.go b/templates/fs.go index aedde93..64b74d4 100644 --- a/templates/fs.go +++ b/templates/fs.go @@ -3,7 +3,6 @@ package templates import "embed" //go:embed *.html -//go:embed auth/*.html //go:embed layouts/*.html //go:embed posts/*.html var FS embed.FS diff --git a/templates/index.html b/templates/index.html index 614290c..0c4d297 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,7 +1,5 @@

Thing

-User = {{.user.Email}} -
diff --git a/templates/layouts/login.html b/templates/layouts/login.html deleted file mode 100644 index 822b957..0000000 --- a/templates/layouts/login.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - Hugo CRM - - - {{embed}} - - \ No newline at end of file diff --git a/templates/layouts/main.html b/templates/layouts/main.html index 66bfca4..822b957 100644 --- a/templates/layouts/main.html +++ b/templates/layouts/main.html @@ -6,7 +6,6 @@ Hugo CRM - {{embed}} diff --git a/templates/layouts/site.html b/templates/layouts/site.html index 789354e..5539345 100644 --- a/templates/layouts/site.html +++ b/templates/layouts/site.html @@ -6,16 +6,12 @@ Hugo CMS -

Hugo CMS

diff --git a/templates/posts/index.html b/templates/posts/index.html index 87b2647..c297bba 100644 --- a/templates/posts/index.html +++ b/templates/posts/index.html @@ -3,18 +3,18 @@ {{range .posts}} -
- {{if .Title}} -

{{.Title}}

- {{end}} + {{if .Title}} +

{{.Title}}

+ {{end}} +
{{.Body | markdown}}
- Edit | - Delete + Edit
+
{{else}}

No posts yet

{{end}} \ No newline at end of file diff --git a/templates/posts/new.html b/templates/posts/new.html index 350d71e..72769fa 100644 --- a/templates/posts/new.html +++ b/templates/posts/new.html @@ -1,5 +1,5 @@ {{- $postTarget := printf "/sites/%v/posts" .site.ID -}} -{{- if (ne .post.ID 0) -}} +{{- if .post.ID -}} {{- $postTarget = printf "/sites/%v/posts/%v" .site.ID .post.ID -}} {{- end -}}