178 lines
4.1 KiB
Go
178 lines
4.1 KiB
Go
//go:generate sh -c "NODE_ENV=production bun --cwd=./ui install"
|
|
//go:generate sh -c "NODE_ENV=production bun --bun --cwd=./ui run generate"
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"filething/middleware"
|
|
"filething/models"
|
|
"filething/routes"
|
|
"filething/ui"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/labstack/echo/v4"
|
|
echoMiddleware "github.com/labstack/echo/v4/middleware"
|
|
"github.com/uptrace/bun"
|
|
"github.com/uptrace/bun/dialect/pgdialect"
|
|
"github.com/uptrace/bun/driver/pgdriver"
|
|
)
|
|
|
|
var initUi func(e *echo.Echo)
|
|
|
|
func main() {
|
|
|
|
dbHost := os.Getenv("DB_HOST")
|
|
dbName := os.Getenv("DB_NAME")
|
|
dbUser := os.Getenv("DB_USER")
|
|
dbPasswd := os.Getenv("DB_PASSWD")
|
|
|
|
if dbHost == "" || dbName == "" || dbUser == "" || os.Getenv("STORAGE_PATH") == "" {
|
|
panic("Missing database environment variabled!")
|
|
}
|
|
|
|
dbUrl := fmt.Sprintf("postgres://%s:%s@%s/%s?dial_timeout=10s&sslmode=disable", dbUser, dbPasswd, dbHost, dbName)
|
|
|
|
sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(dbUrl)))
|
|
db := bun.NewDB(sqldb, pgdialect.New())
|
|
|
|
err := createSchema(db)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
err = seedPlans(db)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
e := echo.New()
|
|
|
|
e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
|
|
return func(c echo.Context) error {
|
|
c.Set("db", db)
|
|
return next(c)
|
|
}
|
|
})
|
|
|
|
e.Use(echoMiddleware.Gzip())
|
|
e.Use(echoMiddleware.CORS())
|
|
e.Use(echoMiddleware.CSRFWithConfig(echoMiddleware.CSRFConfig{
|
|
TokenLookup: "cookie:_csrf",
|
|
CookiePath: "/",
|
|
CookieSecure: true,
|
|
CookieHTTPOnly: true,
|
|
CookieSameSite: http.SameSiteStrictMode,
|
|
}))
|
|
|
|
api := e.Group("/api")
|
|
{
|
|
api.POST("/login", routes.LoginHandler)
|
|
api.POST("/signup", routes.SignupHandler)
|
|
|
|
// everything past this needs auth
|
|
api.Use(middleware.SessionMiddleware(db))
|
|
api.GET("/user", routes.GetUser)
|
|
|
|
api.POST("/files/upload*", routes.UploadFile)
|
|
api.GET("/files/get/*", routes.GetFiles)
|
|
api.GET("/files/download*", routes.GetFile)
|
|
api.POST("/files/delete*", routes.DeleteFiles)
|
|
}
|
|
|
|
// redirects to the proper pages if you are trying to access one that expects you have/dont have an api key
|
|
// this isnt explicitly required, but it provides a better experience than doing this same thing clientside
|
|
e.Use(middleware.AuthCheckMiddleware)
|
|
|
|
initUi(e)
|
|
|
|
e.HTTPErrorHandler = customHTTPErrorHandler
|
|
|
|
if err := e.Start(":1323"); err != nil && err != http.ErrServerClosed {
|
|
fmt.Println("Error starting HTTP server:", err)
|
|
}
|
|
}
|
|
|
|
func customHTTPErrorHandler(err error, c echo.Context) {
|
|
if he, ok := err.(*echo.HTTPError); ok && he.Code == http.StatusNotFound {
|
|
path := c.Request().URL.Path
|
|
|
|
if !strings.HasPrefix(path, "/api") {
|
|
file, err := ui.DistDirFS.Open("404.html")
|
|
if err != nil {
|
|
c.Logger().Error(err)
|
|
}
|
|
|
|
fileInfo, err := file.Stat()
|
|
if err != nil {
|
|
c.Logger().Error(err)
|
|
}
|
|
|
|
fileBuf := make([]byte, fileInfo.Size())
|
|
_, err = file.Read(fileBuf)
|
|
defer func() {
|
|
if err := file.Close(); err != nil {
|
|
panic(err)
|
|
}
|
|
}()
|
|
if err != nil {
|
|
c.Logger().Error(err)
|
|
panic(err)
|
|
}
|
|
|
|
c.HTML(http.StatusNotFound, string(fileBuf))
|
|
return
|
|
}
|
|
}
|
|
|
|
c.Echo().DefaultHTTPErrorHandler(err, c)
|
|
}
|
|
|
|
func createSchema(db *bun.DB) error {
|
|
models := []interface{}{
|
|
(*models.User)(nil),
|
|
(*models.Session)(nil),
|
|
(*models.Plan)(nil),
|
|
}
|
|
|
|
ctx := context.Background()
|
|
for _, model := range models {
|
|
_, err := db.NewCreateTable().Model(model).IfNotExists().Exec(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func seedPlans(db *bun.DB) error {
|
|
ctx := context.Background()
|
|
count, err := db.NewSelect().Model((*models.Plan)(nil)).Count(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to count plans: %w", err)
|
|
}
|
|
|
|
// If the table is not empty, no need to seed
|
|
if count > 0 {
|
|
return nil
|
|
}
|
|
|
|
plans := []models.Plan{
|
|
{MaxStorage: 10 * 1024 * 1024 * 1024}, // 10GB
|
|
{MaxStorage: 50 * 1024 * 1024 * 1024}, // 50GB
|
|
{MaxStorage: 100 * 1024 * 1024 * 1024}, // 100GB
|
|
}
|
|
|
|
_, err = db.NewInsert().Model(&plans).Exec(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to seed plans: %w", err)
|
|
}
|
|
|
|
log.Println("Successfully seeded the plans table")
|
|
return nil
|
|
}
|