Modified models to support a DB
This commit is contained in:
parent
3591e0c723
commit
ebaec3d296
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
||||||
build/
|
build/
|
||||||
|
.idea/
|
||||||
# Local Netlify folder
|
# Local Netlify folder
|
||||||
.netlify
|
.netlify
|
||||||
|
|
|
||||||
11
Makefile
Normal file
11
Makefile
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
BUILD_DIR=build
|
||||||
|
|
||||||
|
all: clean build
|
||||||
|
|
||||||
|
.Phony: clean
|
||||||
|
clean:
|
||||||
|
-rm -r $(BUILD_DIR)
|
||||||
|
|
||||||
|
.Phony: gen
|
||||||
|
gen:
|
||||||
|
go run github.com/sqlc-dev/sqlc/cmd/sqlc@v1.30.0 generate
|
||||||
18
go.mod
18
go.mod
|
|
@ -2,10 +2,28 @@ module lmika.dev/lmika/weiro
|
||||||
|
|
||||||
go 1.24.3
|
go 1.24.3
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/Southclaws/fault v0.8.1
|
||||||
|
github.com/lmika/blogging-tools v0.0.0-20240630114557-8db2b3aa93e6
|
||||||
|
lmika.dev/pkg/litemigrate v0.1.0
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/lmika/gopkgs v0.0.0-20240408110817-a02f6fc67d1f // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/stretchr/testify v1.11.1 // indirect
|
github.com/stretchr/testify v1.11.1 // indirect
|
||||||
github.com/yuin/goldmark v1.7.16 // indirect
|
github.com/yuin/goldmark v1.7.16 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
||||||
|
golang.org/x/sys v0.37.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
modernc.org/libc v1.67.6 // indirect
|
||||||
|
modernc.org/mathutil v1.7.1 // indirect
|
||||||
|
modernc.org/memory v1.11.0 // indirect
|
||||||
|
modernc.org/sqlite v1.46.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
31
go.sum
31
go.sum
|
|
@ -1,11 +1,42 @@
|
||||||
|
github.com/Southclaws/fault v0.8.1 h1:mgqqdC6kUBQ6ExMALZ0nNaDfNJD5h2+wq3se5mAyX+8=
|
||||||
|
github.com/Southclaws/fault v0.8.1/go.mod h1:VUVkAWutC59SL16s6FTqf3I6I2z77RmnaW5XRz4bLOE=
|
||||||
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/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/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/lmika/blogging-tools v0.0.0-20240630114557-8db2b3aa93e6 h1:WHM70gRzPTKHSKm/wf2kp3HErXzMpmcqfkGjHlhtu1Y=
|
||||||
|
github.com/lmika/blogging-tools v0.0.0-20240630114557-8db2b3aa93e6/go.mod h1:w4rGqiE0+/FDUNWiIhfPGSPfT948/9Yw+cja12zOb4o=
|
||||||
|
github.com/lmika/gopkgs v0.0.0-20240408110817-a02f6fc67d1f h1:tz68Lhc1oR15HVz69IGbtdukdH0x70kBDEvvj5pTXyE=
|
||||||
|
github.com/lmika/gopkgs v0.0.0-20240408110817-a02f6fc67d1f/go.mod h1:zHQvhjGXRro/Xp2C9dbC+ZUpE0gL4GYW75x1lk7hwzI=
|
||||||
|
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/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||||
|
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||||
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/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
github.com/yuin/goldmark v1.7.16 h1:n+CJdUxaFMiDUNnWC3dMWCIQJSkxH4uz3ZwQBkAlVNE=
|
github.com/yuin/goldmark v1.7.16 h1:n+CJdUxaFMiDUNnWC3dMWCIQJSkxH4uz3ZwQBkAlVNE=
|
||||||
github.com/yuin/goldmark v1.7.16/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
github.com/yuin/goldmark v1.7.16/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||||
|
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY=
|
||||||
|
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||||
|
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
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=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
lmika.dev/pkg/litemigrate v0.1.0 h1:DBEJahbQO7W3uEmAOQGg1URBWYimg0ClWHi83M2MZwk=
|
||||||
|
lmika.dev/pkg/litemigrate v0.1.0/go.mod h1:GQWWDiMZGQaVspcwKNq8vIBPN5H+KsUo/VBIeh9OfLg=
|
||||||
|
modernc.org/libc v1.67.6 h1:eVOQvpModVLKOdT+LvBPjdQqfrZq+pC39BygcT+E7OI=
|
||||||
|
modernc.org/libc v1.67.6/go.mod h1:JAhxUVlolfYDErnwiqaLvUqc8nfb2r6S6slAgZOnaiE=
|
||||||
|
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||||
|
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||||
|
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||||
|
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||||
|
modernc.org/sqlite v1.46.1 h1:eFJ2ShBLIEnUWlLy12raN0Z1plqmFX9Qe3rjQTKt6sU=
|
||||||
|
modernc.org/sqlite v1.46.1/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA=
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package site_templates
|
package simplecss
|
||||||
|
|
||||||
import "embed"
|
import "embed"
|
||||||
|
|
||||||
13
main.go
13
main.go
|
|
@ -4,22 +4,29 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"lmika.dev/lmika/weiro/layouts/simplecss"
|
||||||
|
"lmika.dev/lmika/weiro/models/pubmodel"
|
||||||
"lmika.dev/lmika/weiro/providers/sitebuilder"
|
"lmika.dev/lmika/weiro/providers/sitebuilder"
|
||||||
"lmika.dev/lmika/weiro/providers/sitereader"
|
"lmika.dev/lmika/weiro/providers/sitereader"
|
||||||
"lmika.dev/lmika/weiro/site_templates"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
sr := sitereader.New(os.DirFS("_test-site"))
|
sr := sitereader.New(os.DirFS("_test-site"))
|
||||||
|
|
||||||
site, err := sr.ReadSite()
|
readSite, err := sr.ReadSite()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
site := pubmodel.Site{
|
||||||
|
Site: readSite.Site,
|
||||||
|
BaseURL: readSite.Target.BaseURL,
|
||||||
|
Posts: readSite.Posts,
|
||||||
|
}
|
||||||
|
|
||||||
sb, err := sitebuilder.New(site, sitebuilder.Options{
|
sb, err := sitebuilder.New(site, sitebuilder.Options{
|
||||||
BasePosts: "/posts",
|
BasePosts: "/posts",
|
||||||
TemplatesFS: site_templates.FS,
|
TemplatesFS: simplecss.FS,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
|
|
||||||
14
models/posts.go
Normal file
14
models/posts.go
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Post struct {
|
||||||
|
ID int64
|
||||||
|
SiteID int64
|
||||||
|
GUID string
|
||||||
|
Title string
|
||||||
|
Body string
|
||||||
|
Slug string
|
||||||
|
CreatedAt time.Time
|
||||||
|
PublishedAt time.Time
|
||||||
|
}
|
||||||
9
models/pubmodel/sites.go
Normal file
9
models/pubmodel/sites.go
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
package pubmodel
|
||||||
|
|
||||||
|
import "lmika.dev/lmika/weiro/models"
|
||||||
|
|
||||||
|
type Site struct {
|
||||||
|
models.Site
|
||||||
|
BaseURL string
|
||||||
|
Posts []*models.Post
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,29 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import "time"
|
const (
|
||||||
|
PublishTargetTypeNone int = iota
|
||||||
|
PublishTargetTypeNetlify
|
||||||
|
)
|
||||||
|
|
||||||
type Site struct {
|
type Site struct {
|
||||||
Meta SiteMeta
|
ID int64
|
||||||
Posts []*Post
|
OwnerID int64
|
||||||
|
Title string
|
||||||
|
Tagline string
|
||||||
|
//Meta SiteMeta
|
||||||
|
//Posts []*Post
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SitePublishTarget struct {
|
||||||
|
ID int64
|
||||||
|
SiteID int64
|
||||||
|
PublishTargetType int
|
||||||
|
BaseURL string
|
||||||
|
TargetSiteID string
|
||||||
|
TargetPublishKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
type SiteMeta struct {
|
type SiteMeta struct {
|
||||||
Title string `yaml:"title"`
|
Title string `yaml:"title"`
|
||||||
Tagline string `yaml:"tagline"`
|
Tagline string `yaml:"tagline"`
|
||||||
|
|
@ -25,3 +42,4 @@ type Post struct {
|
||||||
Meta PostMeta
|
Meta PostMeta
|
||||||
Content string
|
Content string
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
|
||||||
7
models/users.go
Normal file
7
models/users.go
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
ID int64
|
||||||
|
Username string
|
||||||
|
PasswordHashed []byte
|
||||||
|
}
|
||||||
31
providers/db/gen/sql/db.go
Normal file
31
providers/db/gen/sql/db.go
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.30.0
|
||||||
|
|
||||||
|
package sql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DBTX interface {
|
||||||
|
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
|
||||||
|
PrepareContext(context.Context, string) (*sql.Stmt, error)
|
||||||
|
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
|
||||||
|
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(db DBTX) *Queries {
|
||||||
|
return &Queries{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Queries struct {
|
||||||
|
db DBTX
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
|
||||||
|
return &Queries{
|
||||||
|
db: tx,
|
||||||
|
}
|
||||||
|
}
|
||||||
38
providers/db/gen/sql/models.go
Normal file
38
providers/db/gen/sql/models.go
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.30.0
|
||||||
|
|
||||||
|
package sql
|
||||||
|
|
||||||
|
type Post struct {
|
||||||
|
ID int64
|
||||||
|
SiteID int64
|
||||||
|
Guid string
|
||||||
|
Title string
|
||||||
|
Body string
|
||||||
|
Slug string
|
||||||
|
CreatedAt int64
|
||||||
|
PublishedAt int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type PublishTarget struct {
|
||||||
|
ID int64
|
||||||
|
SiteID int64
|
||||||
|
PublishTargetType int64
|
||||||
|
BaseUrl string
|
||||||
|
TargetSiteID string
|
||||||
|
TargetPublishKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Site struct {
|
||||||
|
ID int64
|
||||||
|
OwnerID int64
|
||||||
|
Title string
|
||||||
|
Tagline string
|
||||||
|
}
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
ID int64
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
}
|
||||||
84
providers/db/gen/sql/posts.sql.go
Normal file
84
providers/db/gen/sql/posts.sql.go
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.30.0
|
||||||
|
// source: posts.sql
|
||||||
|
|
||||||
|
package sql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
const insertPost = `-- name: InsertPost :one
|
||||||
|
INSERT INTO posts (
|
||||||
|
site_id,
|
||||||
|
guid,
|
||||||
|
title,
|
||||||
|
body,
|
||||||
|
slug,
|
||||||
|
created_at,
|
||||||
|
published_at
|
||||||
|
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||||
|
RETURNING id
|
||||||
|
`
|
||||||
|
|
||||||
|
type InsertPostParams struct {
|
||||||
|
SiteID int64
|
||||||
|
Guid string
|
||||||
|
Title string
|
||||||
|
Body string
|
||||||
|
Slug string
|
||||||
|
CreatedAt int64
|
||||||
|
PublishedAt int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) InsertPost(ctx context.Context, arg InsertPostParams) (int64, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, insertPost,
|
||||||
|
arg.SiteID,
|
||||||
|
arg.Guid,
|
||||||
|
arg.Title,
|
||||||
|
arg.Body,
|
||||||
|
arg.Slug,
|
||||||
|
arg.CreatedAt,
|
||||||
|
arg.PublishedAt,
|
||||||
|
)
|
||||||
|
var id int64
|
||||||
|
err := row.Scan(&id)
|
||||||
|
return id, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectPostsOfSite = `-- name: SelectPostsOfSite :many
|
||||||
|
SELECT id, site_id, guid, title, body, slug, created_at, published_at FROM posts WHERE site_id = ? ORDER BY created_at DESC LIMIT 10
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) SelectPostsOfSite(ctx context.Context, siteID int64) ([]Post, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, selectPostsOfSite, siteID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []Post
|
||||||
|
for rows.Next() {
|
||||||
|
var i Post
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.SiteID,
|
||||||
|
&i.Guid,
|
||||||
|
&i.Title,
|
||||||
|
&i.Body,
|
||||||
|
&i.Slug,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.PublishedAt,
|
||||||
|
); 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
|
||||||
|
}
|
||||||
76
providers/db/gen/sql/pubtargets.sql.go
Normal file
76
providers/db/gen/sql/pubtargets.sql.go
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.30.0
|
||||||
|
// source: pubtargets.sql
|
||||||
|
|
||||||
|
package sql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
const insertPublishTarget = `-- name: InsertPublishTarget :one
|
||||||
|
INSERT INTO publish_targets (
|
||||||
|
site_id,
|
||||||
|
publish_target_type,
|
||||||
|
base_url,
|
||||||
|
target_site_id,
|
||||||
|
target_publish_key
|
||||||
|
) VALUES (?, ?, ?, ?, ?)
|
||||||
|
RETURNING id
|
||||||
|
`
|
||||||
|
|
||||||
|
type InsertPublishTargetParams struct {
|
||||||
|
SiteID int64
|
||||||
|
PublishTargetType int64
|
||||||
|
BaseUrl string
|
||||||
|
TargetSiteID string
|
||||||
|
TargetPublishKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) InsertPublishTarget(ctx context.Context, arg InsertPublishTargetParams) (int64, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, insertPublishTarget,
|
||||||
|
arg.SiteID,
|
||||||
|
arg.PublishTargetType,
|
||||||
|
arg.BaseUrl,
|
||||||
|
arg.TargetSiteID,
|
||||||
|
arg.TargetPublishKey,
|
||||||
|
)
|
||||||
|
var id int64
|
||||||
|
err := row.Scan(&id)
|
||||||
|
return id, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectPublishTargetsOfSite = `-- name: SelectPublishTargetsOfSite :many
|
||||||
|
SELECT id, site_id, publish_target_type, base_url, target_site_id, target_publish_key FROM publish_targets WHERE site_id = ?
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) SelectPublishTargetsOfSite(ctx context.Context, siteID int64) ([]PublishTarget, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, selectPublishTargetsOfSite, siteID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []PublishTarget
|
||||||
|
for rows.Next() {
|
||||||
|
var i PublishTarget
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.SiteID,
|
||||||
|
&i.PublishTargetType,
|
||||||
|
&i.BaseUrl,
|
||||||
|
&i.TargetSiteID,
|
||||||
|
&i.TargetPublishKey,
|
||||||
|
); 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
|
||||||
|
}
|
||||||
64
providers/db/gen/sql/sites.sql.go
Normal file
64
providers/db/gen/sql/sites.sql.go
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.30.0
|
||||||
|
// source: sites.sql
|
||||||
|
|
||||||
|
package sql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
const insertSite = `-- name: InsertSite :one
|
||||||
|
INSERT INTO sites (
|
||||||
|
owner_id,
|
||||||
|
title,
|
||||||
|
tagline
|
||||||
|
) VALUES (?, ?, ?)
|
||||||
|
RETURNING id
|
||||||
|
`
|
||||||
|
|
||||||
|
type InsertSiteParams struct {
|
||||||
|
OwnerID int64
|
||||||
|
Title string
|
||||||
|
Tagline string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) InsertSite(ctx context.Context, arg InsertSiteParams) (int64, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, insertSite, arg.OwnerID, arg.Title, arg.Tagline)
|
||||||
|
var id int64
|
||||||
|
err := row.Scan(&id)
|
||||||
|
return id, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectSitesOwnedByUser = `-- name: SelectSitesOwnedByUser :many
|
||||||
|
SELECT id, owner_id, title, tagline FROM sites WHERE owner_id = ?
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) SelectSitesOwnedByUser(ctx context.Context, ownerID int64) ([]Site, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, selectSitesOwnedByUser, ownerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []Site
|
||||||
|
for rows.Next() {
|
||||||
|
var i Site
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.OwnerID,
|
||||||
|
&i.Title,
|
||||||
|
&i.Tagline,
|
||||||
|
); 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
|
||||||
|
}
|
||||||
37
providers/db/gen/sql/users.sql.go
Normal file
37
providers/db/gen/sql/users.sql.go
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.30.0
|
||||||
|
// source: users.sql
|
||||||
|
|
||||||
|
package sql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
const insertUserByUsername = `-- name: InsertUserByUsername :one
|
||||||
|
INSERT INTO users (username, password) VALUES (?, ?) RETURNING id
|
||||||
|
`
|
||||||
|
|
||||||
|
type InsertUserByUsernameParams struct {
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) InsertUserByUsername(ctx context.Context, arg InsertUserByUsernameParams) (int64, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, insertUserByUsername, arg.Username, arg.Password)
|
||||||
|
var id int64
|
||||||
|
err := row.Scan(&id)
|
||||||
|
return id, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectUserByUsername = `-- name: SelectUserByUsername :one
|
||||||
|
SELECT id, username, password FROM users WHERE username = ?
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) SelectUserByUsername(ctx context.Context, username string) (User, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, selectUserByUsername, username)
|
||||||
|
var i User
|
||||||
|
err := row.Scan(&i.ID, &i.Username, &i.Password)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
40
providers/db/provider.go
Normal file
40
providers/db/provider.go
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
"github.com/Southclaws/fault"
|
||||||
|
"github.com/lmika/blogging-tools/providers/db/sqlc/maindbq"
|
||||||
|
"github.com/lmika/blogging-tools/sql/maindb/schema"
|
||||||
|
migration "lmika.dev/pkg/litemigrate"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Provider struct {
|
||||||
|
drvr *sql.DB
|
||||||
|
queries *maindbq.Queries
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(dbFile string) (*Provider, error) {
|
||||||
|
drvr, err := sql.Open("sqlite", dbFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fault.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := migration.New(schema.FS, drvr).MigrateUp(context.Background()); err != nil {
|
||||||
|
return nil, fault.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := drvr.Exec(`PRAGMA foreign_keys = 1;`); err != nil {
|
||||||
|
return nil, fault.Wrap(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Provider{
|
||||||
|
drvr: drvr,
|
||||||
|
queries: maindbq.New(drvr),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Provider) Close() error {
|
||||||
|
return db.drvr.Close()
|
||||||
|
}
|
||||||
|
|
@ -16,16 +16,17 @@ import (
|
||||||
"github.com/yuin/goldmark/parser"
|
"github.com/yuin/goldmark/parser"
|
||||||
"github.com/yuin/goldmark/renderer/html"
|
"github.com/yuin/goldmark/renderer/html"
|
||||||
"lmika.dev/lmika/weiro/models"
|
"lmika.dev/lmika/weiro/models"
|
||||||
|
"lmika.dev/lmika/weiro/models/pubmodel"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Builder struct {
|
type Builder struct {
|
||||||
site models.Site
|
site pubmodel.Site
|
||||||
gmMarkdown goldmark.Markdown
|
gmMarkdown goldmark.Markdown
|
||||||
opts Options
|
opts Options
|
||||||
tmpls *template.Template
|
tmpls *template.Template
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(site models.Site, opts Options) (*Builder, error) {
|
func New(site pubmodel.Site, opts Options) (*Builder, error) {
|
||||||
tmpls, err := template.New("").
|
tmpls, err := template.New("").
|
||||||
Funcs(templateFns(site, opts)).
|
Funcs(templateFns(site, opts)).
|
||||||
ParseFS(opts.TemplatesFS, tmplNamePostSingle, tmplNamePostList, tmplNameLayoutMain)
|
ParseFS(opts.TemplatesFS, tmplNamePostSingle, tmplNamePostList, tmplNameLayoutMain)
|
||||||
|
|
@ -75,11 +76,11 @@ func (b *Builder) renderPostList(ctx buildContext, postList []*models.Post) erro
|
||||||
copy(postCopy, postList)
|
copy(postCopy, postList)
|
||||||
|
|
||||||
sort.Slice(postCopy, func(i, j int) bool {
|
sort.Slice(postCopy, func(i, j int) bool {
|
||||||
return postCopy[i].Meta.Date.After(postCopy[j].Meta.Date)
|
return postCopy[i].PublishedAt.After(postCopy[j].PublishedAt)
|
||||||
})
|
})
|
||||||
|
|
||||||
pl := postListData{
|
pl := postListData{
|
||||||
commonData: commonData{Site: b.site.Meta},
|
commonData: commonData{Site: b.site},
|
||||||
}
|
}
|
||||||
for _, post := range postCopy {
|
for _, post := range postCopy {
|
||||||
rp, err := b.renderPost(post)
|
rp, err := b.renderPost(post)
|
||||||
|
|
@ -95,20 +96,20 @@ func (b *Builder) renderPostList(ctx buildContext, postList []*models.Post) erro
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) renderPost(post *models.Post) (postSingleData, error) {
|
func (b *Builder) renderPost(post *models.Post) (postSingleData, error) {
|
||||||
postPath := post.Meta.Slug
|
postPath := post.Slug
|
||||||
if b.opts.BasePosts != "" {
|
if b.opts.BasePosts != "" {
|
||||||
postPath = filepath.Join(b.opts.BasePosts, strings.TrimPrefix(postPath, "/"))
|
postPath = filepath.Join(b.opts.BasePosts, strings.TrimPrefix(postPath, "/"))
|
||||||
}
|
}
|
||||||
|
|
||||||
var md bytes.Buffer
|
var md bytes.Buffer
|
||||||
if err := b.gmMarkdown.Convert([]byte(post.Content), &md); err != nil {
|
if err := b.gmMarkdown.Convert([]byte(post.Body), &md); err != nil {
|
||||||
return postSingleData{}, fmt.Errorf("failed to write post %s: %w", post.Meta.Slug, err)
|
return postSingleData{}, fmt.Errorf("failed to write post %s: %w", post.Slug, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return postSingleData{
|
return postSingleData{
|
||||||
commonData: commonData{Site: b.site.Meta},
|
commonData: commonData{Site: b.site},
|
||||||
Path: postPath,
|
Path: postPath,
|
||||||
Meta: post.Meta,
|
Meta: post,
|
||||||
HTML: template.HTML(md.String()),
|
HTML: template.HTML(md.String()),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
@ -152,7 +153,7 @@ func (b *Builder) renderTemplate(w io.Writer, name string, data interface{}) err
|
||||||
}
|
}
|
||||||
|
|
||||||
return b.tmpls.ExecuteTemplate(w, tmplNameLayoutMain, layoutData{
|
return b.tmpls.ExecuteTemplate(w, tmplNameLayoutMain, layoutData{
|
||||||
commonData: commonData{Site: b.site.Meta},
|
commonData: commonData{Site: b.site},
|
||||||
Body: template.HTML(buf.String()),
|
Body: template.HTML(buf.String()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"lmika.dev/lmika/weiro/models"
|
"lmika.dev/lmika/weiro/models"
|
||||||
|
"lmika.dev/lmika/weiro/models/pubmodel"
|
||||||
"lmika.dev/lmika/weiro/providers/sitebuilder"
|
"lmika.dev/lmika/weiro/providers/sitebuilder"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -19,24 +20,18 @@ func TestBuilder_BuildSite(t *testing.T) {
|
||||||
"layout_main.html": {Data: []byte(`{{ .Body }}`)},
|
"layout_main.html": {Data: []byte(`{{ .Body }}`)},
|
||||||
}
|
}
|
||||||
|
|
||||||
site := models.Site{
|
site := pubmodel.Site{
|
||||||
Meta: models.SiteMeta{
|
BaseURL: "https://example.com",
|
||||||
BaseURL: "https://example.com",
|
|
||||||
},
|
|
||||||
Posts: []*models.Post{
|
Posts: []*models.Post{
|
||||||
{
|
{
|
||||||
Meta: models.PostMeta{
|
Title: "Test Post",
|
||||||
Title: "Test Post",
|
Slug: "/2026/02/18/test-post",
|
||||||
Slug: "/2026/02/18/test-post",
|
Body: "This is a test post",
|
||||||
},
|
|
||||||
Content: "This is a test post",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Meta: models.PostMeta{
|
Title: "Another Post",
|
||||||
Title: "Another Post",
|
Slug: "/2026/02/20/another-post",
|
||||||
Slug: "/2026/02/20/another-post",
|
Body: "This is **another** test post",
|
||||||
},
|
|
||||||
Content: "This is **another** test post",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -64,4 +59,4 @@ func TestBuilder_BuildSite(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -6,17 +6,17 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"lmika.dev/lmika/weiro/models"
|
"lmika.dev/lmika/weiro/models/pubmodel"
|
||||||
)
|
)
|
||||||
|
|
||||||
func templateFns(site models.Site, opts Options) template.FuncMap {
|
func templateFns(site pubmodel.Site, opts Options) template.FuncMap {
|
||||||
return template.FuncMap{
|
return template.FuncMap{
|
||||||
"url_abs": func(basePath string) (string, error) {
|
"url_abs": func(basePath string) (string, error) {
|
||||||
if site.Meta.BaseURL == "" {
|
if site.BaseURL == "" {
|
||||||
return basePath, nil
|
return basePath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
pu, err := url.Parse(site.Meta.BaseURL)
|
pu, err := url.Parse(site.BaseURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"lmika.dev/lmika/weiro/models"
|
"lmika.dev/lmika/weiro/models"
|
||||||
|
"lmika.dev/lmika/weiro/models/pubmodel"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -22,8 +23,6 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
SiteMeta models.SiteMeta
|
|
||||||
|
|
||||||
// BasePosts is the base path for posts.
|
// BasePosts is the base path for posts.
|
||||||
BasePosts string
|
BasePosts string
|
||||||
|
|
||||||
|
|
@ -34,12 +33,12 @@ type Options struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type commonData struct {
|
type commonData struct {
|
||||||
Site models.SiteMeta
|
Site pubmodel.Site
|
||||||
}
|
}
|
||||||
|
|
||||||
type postSingleData struct {
|
type postSingleData struct {
|
||||||
commonData
|
commonData
|
||||||
Meta models.PostMeta
|
Meta *models.Post
|
||||||
HTML template.HTML
|
HTML template.HTML
|
||||||
Path string
|
Path string
|
||||||
}
|
}
|
||||||
|
|
|
||||||
27
providers/sitereader/models.go
Normal file
27
providers/sitereader/models.go
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
package sitereader
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"lmika.dev/lmika/weiro/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ReadSiteModels struct {
|
||||||
|
Site models.Site
|
||||||
|
Target models.SitePublishTarget
|
||||||
|
Posts []*models.Post
|
||||||
|
}
|
||||||
|
|
||||||
|
type siteMeta struct {
|
||||||
|
Title string `yaml:"title"`
|
||||||
|
Tagline string `yaml:"tagline"`
|
||||||
|
BaseURL string `yaml:"base_url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type postMeta struct {
|
||||||
|
ID string `yaml:"id"`
|
||||||
|
Title string `yaml:"title"`
|
||||||
|
Date time.Time `yaml:"date"`
|
||||||
|
Tags []string `yaml:"tags"`
|
||||||
|
Slug string `yaml:"slug"`
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"time"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"lmika.dev/lmika/weiro/models"
|
"lmika.dev/lmika/weiro/models"
|
||||||
|
|
@ -19,24 +20,33 @@ func New(fs fs.FS) *Provider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) ReadSite() (models.Site, error) {
|
func (p *Provider) ReadSite() (ReadSiteModels, error) {
|
||||||
posts, err := p.ListPosts()
|
posts, err := p.ListPosts()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.Site{}, err
|
return ReadSiteModels{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
meta := models.SiteMeta{}
|
meta := siteMeta{}
|
||||||
metaBytes, err := fs.ReadFile(p.fs, "site.yaml")
|
metaBytes, err := fs.ReadFile(p.fs, "site.yaml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.Site{}, err
|
return ReadSiteModels{}, err
|
||||||
}
|
}
|
||||||
if err := yaml.Unmarshal(metaBytes, &meta); err != nil {
|
if err := yaml.Unmarshal(metaBytes, &meta); err != nil {
|
||||||
return models.Site{}, err
|
return ReadSiteModels{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return models.Site{
|
site := models.Site{
|
||||||
Meta: meta,
|
Title: meta.Title,
|
||||||
Posts: posts,
|
Tagline: meta.Tagline,
|
||||||
|
}
|
||||||
|
publishTarget := models.SitePublishTarget{
|
||||||
|
BaseURL: meta.BaseURL,
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReadSiteModels{
|
||||||
|
Site: site,
|
||||||
|
Target: publishTarget,
|
||||||
|
Posts: posts,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,13 +80,19 @@ func (p *Provider) ReadPost(path string) (*models.Post, error) {
|
||||||
return nil, io.ErrUnexpectedEOF
|
return nil, io.ErrUnexpectedEOF
|
||||||
}
|
}
|
||||||
|
|
||||||
var meta models.PostMeta
|
var meta postMeta
|
||||||
if err := yaml.Unmarshal(parts[1], &meta); err != nil {
|
if err := yaml.Unmarshal(parts[1], &meta); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &models.Post{
|
post := models.Post{
|
||||||
Meta: meta,
|
Slug: meta.Slug,
|
||||||
Content: string(bytes.TrimPrefix(parts[2], []byte("\n"))),
|
Title: meta.Title,
|
||||||
}, nil
|
GUID: meta.ID,
|
||||||
|
PublishedAt: meta.Date,
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
}
|
||||||
|
|
||||||
|
post.Body = string(bytes.TrimPrefix(parts[2], []byte("\n")))
|
||||||
|
return &post, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"lmika.dev/lmika/weiro/models"
|
|
||||||
"lmika.dev/lmika/weiro/providers/sitereader"
|
"lmika.dev/lmika/weiro/providers/sitereader"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -27,10 +26,9 @@ This is just a test post.
|
||||||
|
|
||||||
post, err := pr.ReadPost("posts/test.md")
|
post, err := pr.ReadPost("posts/test.md")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "Test Post Here", post.Meta.Title)
|
assert.Equal(t, "Test Post Here", post.Title)
|
||||||
assert.Equal(t, time.Date(2026, 2, 18, 19, 59, 0, 0, time.UTC), post.Meta.Date)
|
assert.Equal(t, time.Date(2026, 2, 18, 19, 59, 0, 0, time.UTC), post.PublishedAt)
|
||||||
assert.Equal(t, []string{"test", "example"}, post.Meta.Tags)
|
assert.Equal(t, "This is just a test post.\n", post.Body)
|
||||||
assert.Equal(t, "This is just a test post.\n", post.Content)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("without meta", func(t *testing.T) {
|
t.Run("without meta", func(t *testing.T) {
|
||||||
|
|
@ -45,8 +43,8 @@ This is just a test post.
|
||||||
|
|
||||||
post, err := pr.ReadPost("posts/test.md")
|
post, err := pr.ReadPost("posts/test.md")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, models.PostMeta{}, post.Meta)
|
assert.Equal(t, "", post.Title)
|
||||||
assert.Equal(t, "This is just a test post.\n", post.Content)
|
assert.Equal(t, "This is just a test post.\n", post.Body)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,12 +72,13 @@ This is just a test post.
|
||||||
|
|
||||||
assert.Equal(t, 2, len(posts))
|
assert.Equal(t, 2, len(posts))
|
||||||
|
|
||||||
assert.Equal(t, "111", posts[0].Meta.ID)
|
assert.Equal(t, "111", posts[0].GUID)
|
||||||
assert.Equal(t, "222", posts[1].Meta.ID)
|
assert.Equal(t, "222", posts[1].GUID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestProvider_ReadSite(t *testing.T) {
|
func TestProvider_ReadSite(t *testing.T) {
|
||||||
testFS := fstest.MapFS{
|
testFS := fstest.MapFS{
|
||||||
|
"site.yaml": {Data: []byte(`base_url: https://example.com`)},
|
||||||
"posts/01-post1.md": {Data: []byte(`---
|
"posts/01-post1.md": {Data: []byte(`---
|
||||||
id: 111
|
id: 111
|
||||||
date: 2026-02-18T19:59:00Z
|
date: 2026-02-18T19:59:00Z
|
||||||
|
|
@ -102,6 +101,6 @@ This is just a test post.
|
||||||
|
|
||||||
assert.Equal(t, 2, len(sites.Posts))
|
assert.Equal(t, 2, len(sites.Posts))
|
||||||
|
|
||||||
assert.Equal(t, "111", sites.Posts[0].Meta.ID)
|
assert.Equal(t, "111", sites.Posts[0].GUID)
|
||||||
assert.Equal(t, "222", sites.Posts[1].Meta.ID)
|
assert.Equal(t, "222", sites.Posts[1].GUID)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
14
sql/queries/posts.sql
Normal file
14
sql/queries/posts.sql
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
-- name: SelectPostsOfSite :many
|
||||||
|
SELECT * FROM posts WHERE site_id = ? ORDER BY created_at DESC LIMIT 10;
|
||||||
|
|
||||||
|
-- name: InsertPost :one
|
||||||
|
INSERT INTO posts (
|
||||||
|
site_id,
|
||||||
|
guid,
|
||||||
|
title,
|
||||||
|
body,
|
||||||
|
slug,
|
||||||
|
created_at,
|
||||||
|
published_at
|
||||||
|
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||||
|
RETURNING id;
|
||||||
12
sql/queries/pubtargets.sql
Normal file
12
sql/queries/pubtargets.sql
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
-- name: SelectPublishTargetsOfSite :many
|
||||||
|
SELECT * FROM publish_targets WHERE site_id = ?;
|
||||||
|
|
||||||
|
-- name: InsertPublishTarget :one
|
||||||
|
INSERT INTO publish_targets (
|
||||||
|
site_id,
|
||||||
|
publish_target_type,
|
||||||
|
base_url,
|
||||||
|
target_site_id,
|
||||||
|
target_publish_key
|
||||||
|
) VALUES (?, ?, ?, ?, ?)
|
||||||
|
RETURNING id;
|
||||||
10
sql/queries/sites.sql
Normal file
10
sql/queries/sites.sql
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
-- name: SelectSitesOwnedByUser :many
|
||||||
|
SELECT * FROM sites WHERE owner_id = ?;
|
||||||
|
|
||||||
|
-- name: InsertSite :one
|
||||||
|
INSERT INTO sites (
|
||||||
|
owner_id,
|
||||||
|
title,
|
||||||
|
tagline
|
||||||
|
) VALUES (?, ?, ?)
|
||||||
|
RETURNING id;
|
||||||
5
sql/queries/users.sql
Normal file
5
sql/queries/users.sql
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
-- name: SelectUserByUsername :one
|
||||||
|
SELECT * FROM users WHERE username = ?;
|
||||||
|
|
||||||
|
-- name: InsertUserByUsername :one
|
||||||
|
INSERT INTO users (username, password) VALUES (?, ?) RETURNING id;
|
||||||
38
sql/schema/01_init.up.sql
Normal file
38
sql/schema/01_init.up.sql
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
CREATE TABLE users (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
username TEXT NOT NULL,
|
||||||
|
password TEXT NOT NULL
|
||||||
|
);
|
||||||
|
CREATE UNIQUE INDEX idx_users_username ON users (username);
|
||||||
|
|
||||||
|
CREATE TABLE sites (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
owner_id INTEGER NOT NULL,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
tagline TEXT NOT NULL,
|
||||||
|
|
||||||
|
FOREIGN KEY (owner_id) REFERENCES users (id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
CREATE INDEX idx_site_owner ON site (owner_id);
|
||||||
|
|
||||||
|
CREATE TABLE publish_targets (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
site_id INTEGER NOT NULL,
|
||||||
|
publish_target_type INTEGER NOT NULL,
|
||||||
|
base_url TEXT NOT NULL,
|
||||||
|
target_site_id TEXT NOT NULL,
|
||||||
|
target_publish_key TEXT NOT NULL
|
||||||
|
);
|
||||||
|
CREATE INDEX idx_publish_targets_site ON publish_targets (site_id);
|
||||||
|
|
||||||
|
CREATE TABLE posts (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
site_id INTEGER NOT NULL,
|
||||||
|
guid TEXT NOT NULL,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
body TEXT NOT NULL,
|
||||||
|
slug TEXT NOT NULL,
|
||||||
|
created_at INTEGER NOT NULL,
|
||||||
|
published_at INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
CREATE INDEX idx_post_site ON posts (site_id);
|
||||||
Loading…
Reference in a new issue