Added publishing of uploads to built site
This commit is contained in:
parent
48f39133d7
commit
d0cebe6564
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -5,3 +5,4 @@ static/assets/
|
||||||
# Local Netlify folder
|
# Local Netlify folder
|
||||||
.netlify
|
.netlify
|
||||||
.env
|
.env
|
||||||
|
.DS_Store
|
||||||
|
|
|
||||||
8
Makefile
8
Makefile
|
|
@ -25,4 +25,10 @@ build: frontend
|
||||||
|
|
||||||
.Phony: run
|
.Phony: run
|
||||||
run: build
|
run: build
|
||||||
./build/weiro
|
./build/weiro
|
||||||
|
|
||||||
|
.Phony: setup_targets
|
||||||
|
setup_targets: build
|
||||||
|
SITE_ID=$$(DATA_DIR=$(BUILD_DIR)/data ./$(BUILD_DIR)/weiro sites | tail -n1 | awk '{ print $$1 }'); \
|
||||||
|
DATA_DIR=$(BUILD_DIR)/data ./build/weiro pubtargets "$$SITE_ID"; \
|
||||||
|
DATA_DIR=$(BUILD_DIR)/data ./build/weiro pubtargets add --site "$$SITE_ID" --type localfs --ref ./$(BUILD_DIR)/out --url http://localhost:8000
|
||||||
|
|
|
||||||
19
assets/js/controllers/show_upload.js
Normal file
19
assets/js/controllers/show_upload.js
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { Controller } from "@hotwired/stimulus"
|
||||||
|
import {showToast} from "../services/toast";
|
||||||
|
|
||||||
|
export default class ShowUploadController extends Controller {
|
||||||
|
static values = {
|
||||||
|
copySnippet: String,
|
||||||
|
};
|
||||||
|
|
||||||
|
async copy(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
|
||||||
|
await navigator.clipboard.writeText(this.copySnippetValue);
|
||||||
|
|
||||||
|
showToast({
|
||||||
|
title: "️📋 HTML Snippet",
|
||||||
|
body: "Copied to clipboard.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ import PosteditController from "./controllers/postedit";
|
||||||
import LogoutController from "./controllers/logout";
|
import LogoutController from "./controllers/logout";
|
||||||
import FirstRunController from "./controllers/firstrun";
|
import FirstRunController from "./controllers/firstrun";
|
||||||
import UploadController from "./controllers/upload";
|
import UploadController from "./controllers/upload";
|
||||||
|
import ShowUploadController from "./controllers/show_upload";
|
||||||
|
|
||||||
window.Stimulus = Application.start()
|
window.Stimulus = Application.start()
|
||||||
Stimulus.register("toast", ToastController);
|
Stimulus.register("toast", ToastController);
|
||||||
|
|
@ -13,4 +14,5 @@ Stimulus.register("postlist", PostlistController);
|
||||||
Stimulus.register("postedit", PosteditController);
|
Stimulus.register("postedit", PosteditController);
|
||||||
Stimulus.register("logout", LogoutController);
|
Stimulus.register("logout", LogoutController);
|
||||||
Stimulus.register("first-run", FirstRunController);
|
Stimulus.register("first-run", FirstRunController);
|
||||||
Stimulus.register("upload", UploadController);
|
Stimulus.register("upload", UploadController);
|
||||||
|
Stimulus.register("show-upload", ShowUploadController);
|
||||||
1
go.mod
1
go.mod
|
|
@ -78,6 +78,7 @@ require (
|
||||||
golang.org/x/crypto v0.48.0 // indirect
|
golang.org/x/crypto v0.48.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
||||||
golang.org/x/net v0.50.0 // indirect
|
golang.org/x/net v0.50.0 // indirect
|
||||||
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.41.0 // indirect
|
golang.org/x/sys v0.41.0 // indirect
|
||||||
golang.org/x/text v0.34.0 // indirect
|
golang.org/x/text v0.34.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
|
|
||||||
2
go.sum
2
go.sum
|
|
@ -581,6 +581,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,16 @@
|
||||||
package pubmodel
|
package pubmodel
|
||||||
|
|
||||||
import "lmika.dev/lmika/weiro/models"
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"lmika.dev/lmika/weiro/models"
|
||||||
|
)
|
||||||
|
|
||||||
type Site struct {
|
type Site struct {
|
||||||
models.Site
|
models.Site
|
||||||
BaseURL string
|
BaseURL string
|
||||||
Posts []*models.Post
|
Posts []*models.Post
|
||||||
|
Uploads []models.Upload
|
||||||
|
|
||||||
|
OpenUpload func(u models.Upload) (io.ReadCloser, error)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
@ -15,6 +14,7 @@ import (
|
||||||
"github.com/yuin/goldmark/extension"
|
"github.com/yuin/goldmark/extension"
|
||||||
"github.com/yuin/goldmark/parser"
|
"github.com/yuin/goldmark/parser"
|
||||||
"github.com/yuin/goldmark/renderer/html"
|
"github.com/yuin/goldmark/renderer/html"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
"lmika.dev/lmika/weiro/models"
|
"lmika.dev/lmika/weiro/models"
|
||||||
"lmika.dev/lmika/weiro/models/pubmodel"
|
"lmika.dev/lmika/weiro/models/pubmodel"
|
||||||
)
|
)
|
||||||
|
|
@ -57,13 +57,32 @@ func (b *Builder) BuildSite(outDir string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, post := range b.site.Posts {
|
eg := errgroup.Group{}
|
||||||
if err := b.writePost(buildCtx, post); err != nil {
|
|
||||||
|
eg.Go(func() error {
|
||||||
|
for _, post := range b.site.Posts {
|
||||||
|
if err := b.writePost(buildCtx, post); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
eg.Go(func() error {
|
||||||
|
if err := b.renderPostList(buildCtx, b.site.Posts); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
if err := b.renderPostList(buildCtx, b.site.Posts); err != nil {
|
// Copy uploads
|
||||||
|
eg.Go(func() error {
|
||||||
|
if err := b.writeUploads(buildCtx, b.site.Uploads); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err := eg.Wait(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,7 +149,6 @@ func (b *Builder) createAtPath(ctx buildContext, path string, fn func(f io.Write
|
||||||
if filepath.Ext(outFile) == "" {
|
if filepath.Ext(outFile) == "" {
|
||||||
outFile = filepath.Join(outFile, "index.html")
|
outFile = filepath.Join(outFile, "index.html")
|
||||||
}
|
}
|
||||||
log.Printf("Writing %s\n", outFile)
|
|
||||||
|
|
||||||
// Render it within the template
|
// Render it within the template
|
||||||
if err := os.MkdirAll(filepath.Dir(outFile), 0755); err != nil {
|
if err := os.MkdirAll(filepath.Dir(outFile), 0755); err != nil {
|
||||||
|
|
@ -158,6 +176,37 @@ func (b *Builder) renderTemplate(w io.Writer, name string, data interface{}) err
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Builder) writeUploads(ctx buildContext, uploads []models.Upload) error {
|
||||||
|
for _, u := range uploads {
|
||||||
|
fullPath := filepath.Join(ctx.outDir, "uploads", u.Slug)
|
||||||
|
if err := os.MkdirAll(filepath.Dir(fullPath), 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := func() error {
|
||||||
|
r, err := b.site.OpenUpload(u)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
w, err := os.Create(fullPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer w.Close()
|
||||||
|
|
||||||
|
if _, err := io.Copy(w, r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type buildContext struct {
|
type buildContext struct {
|
||||||
outDir string
|
outDir string
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,10 @@ func (p *Provider) OpenUpload(site models.Site, up models.Upload) (io.ReadCloser
|
||||||
return os.Open(fullPath)
|
return os.Open(fullPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Provider) UploadDir(site models.Site) string {
|
||||||
|
return filepath.Join(p.baseDir, site.GUID)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Provider) uploadFileName(site models.Site, up models.Upload) string {
|
func (p *Provider) uploadFileName(site models.Site, up models.Upload) string {
|
||||||
return filepath.Join(p.baseDir, site.GUID, up.Slug)
|
return filepath.Join(p.baseDir, site.GUID, up.Slug)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package publisher
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
|
@ -16,15 +17,18 @@ import (
|
||||||
"lmika.dev/lmika/weiro/providers/db"
|
"lmika.dev/lmika/weiro/providers/db"
|
||||||
"lmika.dev/lmika/weiro/providers/sitebuilder"
|
"lmika.dev/lmika/weiro/providers/sitebuilder"
|
||||||
"lmika.dev/lmika/weiro/providers/siteexporter"
|
"lmika.dev/lmika/weiro/providers/siteexporter"
|
||||||
|
"lmika.dev/lmika/weiro/providers/uploadfiles"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Publisher struct {
|
type Publisher struct {
|
||||||
db *db.Provider
|
db *db.Provider
|
||||||
|
up *uploadfiles.Provider
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(db *db.Provider) *Publisher {
|
func New(db *db.Provider, up *uploadfiles.Provider) *Publisher {
|
||||||
return &Publisher{
|
return &Publisher{
|
||||||
db: db,
|
db: db,
|
||||||
|
up: up,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,6 +44,12 @@ func (p *Publisher) Publish(ctx context.Context, site models.Site) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetch all uploads of site
|
||||||
|
uploads, err := p.db.SelectUploadsOfSite(ctx, site.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
for _, target := range targets {
|
for _, target := range targets {
|
||||||
if !target.Enabled {
|
if !target.Enabled {
|
||||||
continue
|
continue
|
||||||
|
|
@ -49,6 +59,10 @@ func (p *Publisher) Publish(ctx context.Context, site models.Site) error {
|
||||||
Site: site,
|
Site: site,
|
||||||
Posts: posts,
|
Posts: posts,
|
||||||
BaseURL: target.BaseURL,
|
BaseURL: target.BaseURL,
|
||||||
|
Uploads: uploads,
|
||||||
|
OpenUpload: func(u models.Upload) (io.ReadCloser, error) {
|
||||||
|
return p.up.OpenUpload(site, u)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := p.publishSite(ctx, pubSite, target); err != nil {
|
if err := p.publishSite(ctx, pubSite, target); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ func New(cfg config.Config) (*Services, error) {
|
||||||
ufp := uploadfiles.New(filepath.Join(cfg.DataDir, "uploads"))
|
ufp := uploadfiles.New(filepath.Join(cfg.DataDir, "uploads"))
|
||||||
|
|
||||||
authSvc := auth.New(dbp)
|
authSvc := auth.New(dbp)
|
||||||
publisherSvc := publisher.New(dbp)
|
publisherSvc := publisher.New(dbp, ufp)
|
||||||
publisherQueue := publisher.NewQueue(publisherSvc)
|
publisherQueue := publisher.NewQueue(publisherSvc)
|
||||||
postService := posts.New(dbp, publisherQueue)
|
postService := posts.New(dbp, publisherQueue)
|
||||||
siteService := sites.New(dbp)
|
siteService := sites.New(dbp)
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,22 @@ package uploads
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"html/template"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"lmika.dev/lmika/weiro/models"
|
"lmika.dev/lmika/weiro/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
uploadCopyTemplate = template.Must(template.New("upload-copy").Parse(`<img src="/uploads/{{.Slug}}" alt="{{.Alt}}">`))
|
||||||
|
)
|
||||||
|
|
||||||
type UploadWithURL struct {
|
type UploadWithURL struct {
|
||||||
Upload models.Upload
|
Upload models.Upload
|
||||||
URL string
|
CopyTemplate string
|
||||||
|
URL string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) FetchUpload(ctx context.Context, uploadID int64) (res UploadWithURL, _ error) {
|
func (s *Service) FetchUpload(ctx context.Context, uploadID int64) (res UploadWithURL, _ error) {
|
||||||
|
|
@ -25,11 +33,22 @@ func (s *Service) FetchUpload(ctx context.Context, uploadID int64) (res UploadWi
|
||||||
}
|
}
|
||||||
|
|
||||||
return UploadWithURL{
|
return UploadWithURL{
|
||||||
Upload: upload,
|
Upload: upload,
|
||||||
URL: fmt.Sprintf("/sites/%v/uploads/%v/raw", site.ID, upload.ID),
|
CopyTemplate: s.renderCopyTemplate(upload),
|
||||||
|
URL: fmt.Sprintf("/sites/%v/uploads/%v/raw", site.ID, upload.ID),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) renderCopyTemplate(upload models.Upload) string {
|
||||||
|
var sb strings.Builder
|
||||||
|
|
||||||
|
if err := uploadCopyTemplate.Execute(&sb, upload); err != nil {
|
||||||
|
log.Printf("error rendering upload copy template: %v", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) ListUploads(ctx context.Context) (res []UploadWithURL, _ error) {
|
func (s *Service) ListUploads(ctx context.Context) (res []UploadWithURL, _ error) {
|
||||||
site, _, err := s.fetchSiteAndUser(ctx)
|
site, _, err := s.fetchSiteAndUser(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -44,8 +63,9 @@ func (s *Service) ListUploads(ctx context.Context) (res []UploadWithURL, _ error
|
||||||
res = make([]UploadWithURL, len(uploads))
|
res = make([]UploadWithURL, len(uploads))
|
||||||
for i, upload := range uploads {
|
for i, upload := range uploads {
|
||||||
res[i] = UploadWithURL{
|
res[i] = UploadWithURL{
|
||||||
Upload: upload,
|
Upload: upload,
|
||||||
URL: fmt.Sprintf("/sites/%v/uploads/%v/raw", site.ID, upload.ID),
|
CopyTemplate: s.renderCopyTemplate(upload),
|
||||||
|
URL: fmt.Sprintf("/sites/%v/uploads/%v/raw", site.ID, upload.ID),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
<main class="container">
|
<main class="container">
|
||||||
<div class="my-4 d-flex justify-content-between align-items-baseline"
|
<div class="my-4 d-flex justify-content-between align-items-baseline"
|
||||||
data-controller="upload" data-upload-site-id-value="{{ .site.ID }}">
|
data-controller="show-upload" data-show-upload-copy-snippet-value="{{ .upload.CopyTemplate }}">
|
||||||
<button class="btn" data-action="upload#upload">Copy HTML</button>
|
<button class="btn" data-action="show-upload#copy">Copy HTML</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<img src="{{ .URL }}" alt="{{ .Upload.Alt }}" class="img-fluid">
|
<img src="{{ .upload.URL }}" alt="{{ .upload.Upload.Alt }}" class="img-fluid">
|
||||||
</main>
|
</main>
|
||||||
Loading…
Reference in a new issue