261 lines
9.2 KiB
Go
261 lines
9.2 KiB
Go
package main
|
|
|
|
import (
|
|
"embed"
|
|
"fmt"
|
|
"io/fs"
|
|
"net/http"
|
|
"time"
|
|
|
|
"git.readonly.ch/bouzoure/pop-camarades/controllers"
|
|
"git.readonly.ch/bouzoure/pop-camarades/helpers"
|
|
"git.readonly.ch/bouzoure/pop-camarades/jobs"
|
|
"git.readonly.ch/bouzoure/pop-camarades/middlewares"
|
|
"github.com/flosch/pongo2/v6"
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/gofiber/fiber/v2/middleware/filesystem"
|
|
loggerMiddleware "github.com/gofiber/fiber/v2/middleware/logger"
|
|
"github.com/gofiber/helmet/v2"
|
|
"github.com/gofiber/template/django/v3"
|
|
)
|
|
|
|
//go:embed static/*
|
|
var embedStatic embed.FS
|
|
|
|
//go:embed views
|
|
var embedViews embed.FS
|
|
|
|
func main() {
|
|
log := helpers.GetLogger()
|
|
|
|
// Add embedded filesystems to shared state
|
|
helpers.AddEmbeddedFS("static", &embedStatic)
|
|
helpers.AddEmbeddedFS("views", &embedViews)
|
|
|
|
// Fetch app config
|
|
config, err := helpers.GetConfig()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Check if at least one account exists
|
|
// If not, create the default one: admin@invalid.tld / password
|
|
// Note: since this is the fist call to the DB, this also applies the migrations
|
|
accountCheck, err := helpers.FirstAccountCheck()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
if !accountCheck {
|
|
err = helpers.FirstAccountCreate()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// Register background jobs
|
|
// Note: jobs will be executed at launch and after every interval
|
|
go helpers.RegisterJob(60*time.Minute, "clean saved sessions", jobs.CleanSavedSessions)
|
|
|
|
// Initialize the Pongo2 templating engine
|
|
// If we are in dev mode, load templates from directory
|
|
// Otherwise, use the templates embedded in the binary
|
|
var engine *django.Engine
|
|
if config.DevMode {
|
|
engine = django.New("./views", ".html")
|
|
engine.ShouldReload = true
|
|
} else {
|
|
embedViews2, err := fs.Sub(embedViews, "views")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
engine = django.NewFileSystem(http.FS(embedViews2), ".html")
|
|
}
|
|
|
|
// Register any custom functions or filters in the template engine
|
|
pongo2.RegisterFilter("time_diff", helpers.TemplTimeDiff)
|
|
|
|
// Base config for the Fiber web app
|
|
// We pass our newly created template engine
|
|
// as well as our custom error handler
|
|
fiberConfig := fiber.Config{
|
|
Views: engine,
|
|
ErrorHandler: helpers.FiberErrorHandler,
|
|
}
|
|
|
|
// If web app is behind a reverse proxy,
|
|
// use X-Forwarded-For header as the IP header
|
|
if config.App.BehindProxy {
|
|
fiberConfig.ProxyHeader = fiber.HeaderXForwardedFor
|
|
}
|
|
|
|
// Create the Fiber web app
|
|
app := fiber.New(fiberConfig)
|
|
|
|
// If we are in dev mode, serve assets from directory
|
|
// Otherwise, serve assets embedded in binary
|
|
if config.DevMode {
|
|
app.Static("/static", "./static")
|
|
} else {
|
|
app.Use("/static", filesystem.New(filesystem.Config{
|
|
Root: http.FS(embedStatic),
|
|
PathPrefix: "static",
|
|
Browse: false,
|
|
}))
|
|
}
|
|
|
|
// Base middlewares
|
|
app.Use(loggerMiddleware.New())
|
|
app.Use(helmet.New())
|
|
|
|
// Misc endpoints without auth
|
|
app.Post("/set-color-mode", controllers.SetColorMode)
|
|
|
|
// Licences page
|
|
app.Use("/licenses", middlewares.TemplatesMiddleware)
|
|
app.Get("/licenses", controllers.Licenses)
|
|
|
|
// Security middlewares
|
|
app.Use(middlewares.SavedSessionMiddleware)
|
|
app.Use(middlewares.AuthMiddleware)
|
|
app.Use(middlewares.WelcomeMiddleware)
|
|
app.Use(middlewares.MfaEnrollMiddleware)
|
|
app.Use(middlewares.MfaVerifyMiddleware)
|
|
app.Use(middlewares.TemplatesMiddleware)
|
|
app.Use("/admin", middlewares.AuthzAdmin)
|
|
app.Use("/debug", middlewares.AuthzAdmin)
|
|
|
|
// Main pages
|
|
app.Get("/", controllers.Homepage)
|
|
app.Get("/admin", controllers.Admin)
|
|
|
|
// Login & logout
|
|
app.Get("/login", controllers.LoginForm)
|
|
app.Post("/login", controllers.LoginForm)
|
|
app.Get("/logout", controllers.LogoutProcess)
|
|
|
|
// Welcome page on first login
|
|
app.Get("/welcome", controllers.WelcomePage)
|
|
app.Post("/welcome", controllers.WelcomePage)
|
|
|
|
// TOTP enroll & verify
|
|
app.Get("/totp/enroll", controllers.TotpEnrollPage)
|
|
app.Post("/totp/enroll", controllers.TotpEnrollPage)
|
|
app.Get("/totp/verify", controllers.TotpVerifyPage)
|
|
app.Post("/totp/verify", controllers.TotpVerifyPage)
|
|
|
|
// Members
|
|
app.Get("/members", controllers.Members)
|
|
app.Get("/members/export", controllers.MembersExport)
|
|
app.Get("/members/:id<int;min(0)>", controllers.MemberShow)
|
|
app.Get("/members/add", controllers.MemberAdd)
|
|
app.Post("/members/add", controllers.MemberAdd)
|
|
app.Get("/members/:id<int;min(0)>/edit", controllers.MemberEdit)
|
|
app.Post("/members/:id<int;min(0)>/edit", controllers.MemberEdit)
|
|
app.Post("/members/:id<int;min(0)>/convert", controllers.MemberConvert)
|
|
app.Post("/members/:id<int;min(0)>/archive", controllers.MemberArchive)
|
|
app.Post("/members/:id<int;min(0)>/restore", controllers.MemberRestore)
|
|
app.Post("/members/:id<int;min(0)>/purge", controllers.MemberPurge)
|
|
|
|
// Contacts
|
|
app.Get("/contacts", controllers.Contacts)
|
|
app.Get("/contacts/export", controllers.ContactsExport)
|
|
app.Get("/contacts/:id<int;min(0)>", controllers.ContactShow)
|
|
app.Get("/contacts/add", controllers.ContactAdd)
|
|
app.Post("/contacts/add", controllers.ContactAdd)
|
|
app.Get("/contacts/:id<int;min(0)>/edit", controllers.ContactEdit)
|
|
app.Post("/contacts/:id<int;min(0)>/edit", controllers.ContactEdit)
|
|
app.Post("/contacts/:id<int;min(0)>/convert", controllers.ContactConvert)
|
|
app.Post("/contacts/:id<int;min(0)>/archive", controllers.ContactArchive)
|
|
app.Post("/contacts/:id<int;min(0)>/restore", controllers.ContactRestore)
|
|
app.Post("/contacts/:id<int;min(0)>/purge", controllers.ContactPurge)
|
|
|
|
// Fields
|
|
app.Get("/fields/:id<int;min(0)>", controllers.FieldJSON)
|
|
|
|
// Account manage
|
|
app.Get("/account/manage", controllers.AccountManage)
|
|
app.Post("/account/manage", controllers.AccountManage)
|
|
app.Get("/account/totp", controllers.AccountTotp)
|
|
app.Post("/account/totp", controllers.AccountTotp)
|
|
|
|
// Admin: Sections
|
|
app.Get("/admin/sections", controllers.Sections)
|
|
app.Get("/admin/sections/:id<int;min(0)>", controllers.SectionShow)
|
|
app.Get("/admin/sections/add", controllers.SectionAdd)
|
|
app.Post("/admin/sections/add", controllers.SectionAdd)
|
|
app.Get("/admin/sections/:id<int;min(0)>/edit", controllers.SectionEdit)
|
|
app.Post("/admin/sections/:id<int;min(0)>/edit", controllers.SectionEdit)
|
|
app.Post("/admin/sections/:id<int;min(0)>/delete", controllers.SectionDelete)
|
|
|
|
// Admin: Lists
|
|
app.Get("/admin/lists", controllers.Lists)
|
|
app.Get("/admin/lists/:id<int;min(0)>", controllers.ListShow)
|
|
app.Get("/admin/lists/add", controllers.ListAdd)
|
|
app.Post("/admin/lists/add", controllers.ListAdd)
|
|
app.Get("/admin/lists/:id<int;min(0)>/edit", controllers.ListEdit)
|
|
app.Post("/admin/lists/:id<int;min(0)>/edit", controllers.ListEdit)
|
|
app.Post("/admin/lists/:id<int;min(0)>/delete", controllers.ListDelete)
|
|
app.Get("/admin/lists/:id<int;min(0)>/items/add", controllers.ListItemAdd)
|
|
app.Post("/admin/lists/:id<int;min(0)>/items/add", controllers.ListItemAdd)
|
|
app.Get("/admin/lists/:id<int;min(0)>/items/:itemid<int;min(0)>", controllers.ListItemEdit)
|
|
app.Post("/admin/lists/:id<int;min(0)>/items/:itemid<int;min(0)>", controllers.ListItemEdit)
|
|
app.Post("/admin/lists/:id<int;min(0)>/items/:itemid<int;min(0)>/delete", controllers.ListItemDelete)
|
|
|
|
// Admin: Fields
|
|
app.Get("/admin/fields", controllers.Fields)
|
|
app.Get("/admin/fields/:id<int;min(0)>", controllers.FieldShow)
|
|
app.Get("/admin/fields/add", controllers.FieldAdd)
|
|
app.Post("/admin/fields/add", controllers.FieldAdd)
|
|
app.Get("/admin/fields/:id<int;min(0)>/edit", controllers.FieldEdit)
|
|
app.Post("/admin/fields/:id<int;min(0)>/edit", controllers.FieldEdit)
|
|
app.Post("/admin/fields/:id<int;min(0)>/delete", controllers.FieldDelete)
|
|
app.Get("/admin/fields/:id<int;min(0)>/move-up", controllers.FieldMoveUp)
|
|
app.Get("/admin/fields/:id<int;min(0)>/move-down", controllers.FieldMoveDown)
|
|
|
|
// Admin: Users
|
|
app.Get("/admin/users", controllers.Users)
|
|
app.Get("/admin/users/:id<int;min(0)>", controllers.UserShow)
|
|
app.Get("/admin/users/add", controllers.UserAdd)
|
|
app.Post("/admin/users/add", controllers.UserAdd)
|
|
app.Get("/admin/users/:id<int;min(0)>/edit", controllers.UserEdit)
|
|
app.Post("/admin/users/:id<int;min(0)>/edit", controllers.UserEdit)
|
|
app.Get("/admin/users/:id<int;min(0)>/permissions", controllers.UserPermissions)
|
|
app.Post("/admin/users/:id<int;min(0)>/permissions", controllers.UserPermissions)
|
|
app.Post("/admin/users/:id<int;min(0)>/delete", controllers.UserDelete)
|
|
|
|
// Admin: Roles
|
|
app.Get("/admin/roles", controllers.Roles)
|
|
app.Get("/admin/roles/:id<int;min(0)>", controllers.RoleShow)
|
|
app.Get("/admin/roles/add", controllers.RoleAdd)
|
|
app.Post("/admin/roles/add", controllers.RoleAdd)
|
|
app.Get("/admin/roles/:id<int;min(0)>/edit", controllers.RoleEdit)
|
|
app.Post("/admin/roles/:id<int;min(0)>/edit", controllers.RoleEdit)
|
|
app.Post("/admin/roles/:id<int;min(0)>/delete", controllers.RoleDelete)
|
|
|
|
// Endpoints only enabled when debug mode is enabled
|
|
if config.Debug {
|
|
app.Get("/debug/fake-members/:number<int;min(1)>", controllers.DebugFakeMembers)
|
|
app.Get("/debug/fake-contacts/:number<int;min(1)>", controllers.DebugFakeContacts)
|
|
app.Get("/debug/fix-fields-order", controllers.DebugFixFieldsOrder)
|
|
}
|
|
|
|
log.Info(
|
|
"starting web server",
|
|
"address", config.App.ListenAddress,
|
|
"port", config.App.ListenPort,
|
|
)
|
|
|
|
listenAddr := fmt.Sprintf(
|
|
"%s:%d",
|
|
config.App.ListenAddress,
|
|
config.App.ListenPort,
|
|
)
|
|
|
|
// Start the fiber web app
|
|
err = app.Listen(listenAddr)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|