Added file management.

This commit is contained in:
mikestefanello 2025-02-16 14:23:52 -05:00
parent 09b8393c8a
commit 3eab2f5562
12 changed files with 201 additions and 21 deletions

100
pkg/handlers/files.go Normal file
View file

@ -0,0 +1,100 @@
package handlers
import (
"fmt"
"io"
"time"
"github.com/labstack/echo/v4"
"github.com/mikestefanello/pagoda/pkg/msg"
"github.com/mikestefanello/pagoda/pkg/page"
"github.com/mikestefanello/pagoda/pkg/services"
"github.com/mikestefanello/pagoda/templates"
"github.com/spf13/afero"
)
const (
routeNameFiles = "files"
routeNameFilesSubmit = "files.submit"
)
type (
Files struct {
files afero.Fs
*services.TemplateRenderer
}
File struct {
Name string
Size int64
Modified string
}
)
func init() {
Register(new(Files))
}
func (h *Files) Init(c *services.Container) error {
h.TemplateRenderer = c.TemplateRenderer
h.files = c.Files
return nil
}
func (h *Files) Routes(g *echo.Group) {
g.GET("/files", h.Page).Name = routeNameFiles
g.POST("/files", h.Submit).Name = routeNameFilesSubmit
}
func (h *Files) Page(ctx echo.Context) error {
p := page.New(ctx)
p.Layout = templates.LayoutMain
p.Name = templates.PageFiles
p.Title = "Upload a file"
// Send a list of all uploaded files to the template to be rendered.
info, err := afero.ReadDir(h.files, "")
if err != nil {
return err
}
files := make([]File, 0)
for _, file := range info {
files = append(files, File{
Name: file.Name(),
Size: file.Size(),
Modified: file.ModTime().Format(time.DateTime),
})
}
p.Data = files
return h.RenderPage(ctx, p)
}
func (h *Files) Submit(ctx echo.Context) error {
file, err := ctx.FormFile("file")
if err != nil {
return err
}
src, err := file.Open()
if err != nil {
return err
}
defer src.Close()
dst, err := h.files.Create(file.Filename)
if err != nil {
return err
}
defer dst.Close()
if _, err = io.Copy(dst, src); err != nil {
return err
}
msg.Success(ctx, fmt.Sprintf("%s was uploaded successfully.", file.Filename))
return h.Page(ctx)
}

View file

@ -4,11 +4,13 @@ import (
"context"
"database/sql"
"fmt"
"github.com/mikestefanello/backlite"
"log/slog"
"os"
"strings"
"github.com/mikestefanello/backlite"
"github.com/spf13/afero"
entsql "entgo.io/ent/dialect/sql"
"github.com/labstack/echo/v4"
_ "github.com/mattn/go-sqlite3"
@ -39,6 +41,9 @@ type Container struct {
// Database stores the connection to the database
Database *sql.DB
// Files stores the file system.
Files afero.Fs
// ORM stores a client to the ORM
ORM *ent.Client
@ -63,6 +68,7 @@ func NewContainer() *Container {
c.initWeb()
c.initCache()
c.initDatabase()
c.initFiles()
c.initORM()
c.initAuth()
c.initTemplateRenderer()
@ -159,6 +165,21 @@ func (c *Container) initDatabase() {
}
}
// initFiles initializes the file system.
func (c *Container) initFiles() {
// Use in-memory storage for tests.
if c.Config.App.Environment == config.EnvTest {
c.Files = afero.NewMemMapFs()
return
}
fs := afero.NewOsFs()
if err := fs.MkdirAll(c.Config.Files.Directory, 0755); err != nil {
panic(err)
}
c.Files = afero.NewBasePathFs(fs, c.Config.Files.Directory)
}
// initORM initializes the ORM
func (c *Container) initORM() {
drv := entsql.OpenDB(c.Config.Database.Driver, c.Database)

View file

@ -12,6 +12,7 @@ func TestNewContainer(t *testing.T) {
assert.NotNil(t, c.Validator)
assert.NotNil(t, c.Cache)
assert.NotNil(t, c.Database)
assert.NotNil(t, c.Files)
assert.NotNil(t, c.ORM)
assert.NotNil(t, c.Mail)
assert.NotNil(t, c.Auth)