diff --git a/fonts/InstrumentSans-Italic.ttf b/assets/fonts/InstrumentSans-Italic.ttf similarity index 100% rename from fonts/InstrumentSans-Italic.ttf rename to assets/fonts/InstrumentSans-Italic.ttf diff --git a/fonts/InstrumentSans-Regular.ttf b/assets/fonts/InstrumentSans-Regular.ttf similarity index 100% rename from fonts/InstrumentSans-Regular.ttf rename to assets/fonts/InstrumentSans-Regular.ttf diff --git a/fonts/InstrumentSans-SemiBold.ttf b/assets/fonts/InstrumentSans-SemiBold.ttf similarity index 100% rename from fonts/InstrumentSans-SemiBold.ttf rename to assets/fonts/InstrumentSans-SemiBold.ttf diff --git a/assets/leaves.webp b/assets/leaves.webp new file mode 100644 index 0000000..3c3dd9e Binary files /dev/null and b/assets/leaves.webp differ diff --git a/go.mod b/go.mod index d604292..24d011a 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( require ( github.com/andybalholm/brotli v1.1.0 // indirect + github.com/chai2010/webp v1.1.1 github.com/gofiber/fiber/v2 v2.52.5 // indirect github.com/gofiber/fiber/v3 v3.0.0-beta.3 github.com/gofiber/template v1.8.3 // indirect @@ -22,6 +23,7 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mattn/go-sqlite3 v1.14.24 + github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/rivo/uniseg v0.2.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.55.0 // indirect diff --git a/go.sum b/go.sum index 20728c7..a8b89ff 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/chai2010/webp v1.1.1 h1:jTRmEccAJ4MGrhFOrPMpNGIJ/eybIgwKpcACsrTEapk= +github.com/chai2010/webp v1.1.1/go.mod h1:0XVwvZWdjjdxpUEIf7b9g9VkHFnInUSYujwqTLEuldU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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= @@ -32,6 +34,8 @@ github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZ github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= 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/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= diff --git a/main.go b/main.go index d31219f..e4571ad 100644 --- a/main.go +++ b/main.go @@ -1,10 +1,14 @@ package main import ( + "bytes" "database/sql" "embed" "encoding/json" "fmt" + "image" + "image/jpeg" + "image/png" "io" "io/fs" "log" @@ -16,6 +20,7 @@ import ( "sync" "time" + "github.com/chai2010/webp" "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/static" "github.com/gofiber/template/handlebars/v2" @@ -23,13 +28,14 @@ import ( "github.com/joho/godotenv" "github.com/juls0730/passport/middleware" _ "github.com/mattn/go-sqlite3" + "github.com/nfnt/resize" ) //go:embed views/** var viewsFS embed.FS -//go:embed fonts/** -var fontsFS embed.FS +//go:embed assets/** +var assetsFS embed.FS type Category struct { ID int64 `json:"id"` @@ -252,13 +258,55 @@ func CreateLink(db *sql.DB) fiber.Handler { }) } + srcFile, err := file.Open() + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "message": "Failed to open file", + }) + } + defer srcFile.Close() + + // Decode the image + var img image.Image + switch contentType { + case "image/jpeg": + img, err = jpeg.Decode(srcFile) + case "image/png": + img, err = png.Decode(srcFile) + default: + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "message": "Unsupported image format", + }) + } + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "message": "Failed to decode image", + }) + } + + resizedImg := resize.Resize(64, 0, img, resize.Lanczos3) + + var buf bytes.Buffer + options := &webp.Options{Lossless: false, Quality: 80} + if err := webp.Encode(&buf, resizedImg, options); err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "message": "Failed to encode image as WebP", + }) + } + assetsDir := "public/uploads" - ext := filepath.Ext(file.Filename) - filename := fmt.Sprintf("%d_%s%s", time.Now().Unix(), strings.ReplaceAll(req.Name, " ", "_"), ext) + filename := fmt.Sprintf("%d_%s.webp", time.Now().Unix(), strings.ReplaceAll(req.Name, " ", "_")) iconPath := filepath.Join(assetsDir, filename) - if err := c.SaveFile(file, iconPath); err != nil { + outFile, err := os.Create(iconPath) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "message": "Failed to save file", + }) + } + defer outFile.Close() + if _, err := io.Copy(outFile, &buf); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "message": "Failed to save file", }) @@ -417,10 +465,19 @@ func main() { Views: engine, }) - router.Use("/", static.New("./public")) + router.Use("/", static.New("./public", static.Config{ + Browse: false, + MaxAge: 31536000, + })) - router.Use("/fonts", static.New("", static.Config{ - FS: fontsFS, + assetsDir, err := fs.Sub(assetsFS, "assets") + if err != nil { + log.Fatal(err) + } + + router.Use("/assets", static.New("", static.Config{ + FS: assetsDir, + MaxAge: 31536000, })) router.Get("/", func(c fiber.Ctx) error { @@ -442,11 +499,21 @@ func main() { }, "layouts/main") }) + router.Use(middleware.AdminMiddleware(app.db)) + router.Get("/admin/login", func(c fiber.Ctx) error { + if c.Locals("IsAdmin") != nil { + return c.Redirect().To("/admin") + } + return c.Render("admin/login", fiber.Map{}, "layouts/main") }) router.Post("/admin/login", func(c fiber.Ctx) error { + if c.Locals("IsAdmin") != nil { + return c.Redirect().To("/admin") + } + var loginData struct { Username string `json:"username"` Password string `json:"password"` @@ -480,9 +547,11 @@ func main() { return c.Status(http.StatusOK).JSON(fiber.Map{"message": "Logged in successfully"}) }) - router.Use(middleware.AdminMiddleware(app.db)) - router.Get("/admin", func(c fiber.Ctx) error { + if c.Locals("IsAdmin") == nil { + return c.Redirect().To("/admin/login") + } + categories, err := app.GetCategories() if err != nil { return err @@ -495,6 +564,13 @@ func main() { api := router.Group("/api") { + api.Use(func(c fiber.Ctx) error { + if c.Locals("IsAdmin") == nil { + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"message": "Unauthorized"}) + } + return c.Next() + }) + api.Post("/categories", CreateCategory(app.db)) api.Post("/links", CreateLink(app.db)) diff --git a/middleware/admin.go b/middleware/admin.go index 2e287e5..e0b3e03 100644 --- a/middleware/admin.go +++ b/middleware/admin.go @@ -16,7 +16,7 @@ func AdminMiddleware(db *sql.DB) func(c fiber.Ctx) error { return func(c fiber.Ctx) error { sessionToken := c.Cookies("SessionToken") if sessionToken == "" { - return c.Redirect().To("/admin/login") + return c.Next() } // Check if session exists @@ -27,18 +27,19 @@ func AdminMiddleware(db *sql.DB) func(c fiber.Ctx) error { WHERE session_id = ? `, sessionToken).Scan(&session.SessionID, &session.ExpiresAt) if err != nil { - return c.Redirect().To("/admin/login") + return c.Next() } sessionExpiry, err := time.Parse("2006-01-02 15:04:05-07:00", session.ExpiresAt) if err != nil { - return c.Redirect().To("/admin/login") + return c.Next() } if sessionExpiry.Before(time.Now()) { - return c.Redirect().To("/admin/login") + return c.Next() } + c.Locals("IsAdmin", true) return c.Next() } } diff --git a/public/leaves.jpg b/public/leaves.jpg deleted file mode 100644 index d9489e9..0000000 Binary files a/public/leaves.jpg and /dev/null differ diff --git a/views/admin/login.hbs b/views/admin/login.hbs index c0e84d5..9f1281a 100644 --- a/views/admin/login.hbs +++ b/views/admin/login.hbs @@ -1,6 +1,6 @@
- +

Login diff --git a/views/layouts/main.hbs b/views/layouts/main.hbs index e863f3d..34dadb2 100644 --- a/views/layouts/main.hbs +++ b/views/layouts/main.hbs @@ -8,21 +8,21 @@