feat: initialize warpbox.dev project structure and backend
Initialize the repository with the core Go backend architecture and a frontend mockup for warpbox.dev, a self-hosted file-sharing application. - Set up Go backend modules for configuration, HTTP server, middleware, handlers, and templates. - Add local development scripts, environment templates, and basic project configuration. - Include a React-based frontend mockup under the docs directory.
This commit is contained in:
34
backend/libs/handlers/app.go
Normal file
34
backend/libs/handlers/app.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"warpbox.dev/backend/libs/config"
|
||||
"warpbox.dev/backend/libs/services"
|
||||
"warpbox.dev/backend/libs/web"
|
||||
)
|
||||
|
||||
type App struct {
|
||||
cfg config.Config
|
||||
logger *slog.Logger
|
||||
renderer *web.Renderer
|
||||
uploadService *services.UploadService
|
||||
}
|
||||
|
||||
func NewApp(cfg config.Config, logger *slog.Logger, renderer *web.Renderer, uploadService *services.UploadService) *App {
|
||||
return &App{
|
||||
cfg: cfg,
|
||||
logger: logger,
|
||||
renderer: renderer,
|
||||
uploadService: uploadService,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) RegisterRoutes(mux *http.ServeMux) {
|
||||
mux.HandleFunc("GET /", a.Home)
|
||||
mux.HandleFunc("GET /healthz", a.Health)
|
||||
mux.HandleFunc("GET /api/v1/health", a.Health)
|
||||
mux.HandleFunc("POST /api/v1/upload", a.UploadPlaceholder)
|
||||
mux.Handle("GET /static/", a.Static())
|
||||
}
|
||||
20
backend/libs/handlers/health.go
Normal file
20
backend/libs/handlers/health.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"warpbox.dev/backend/libs/helpers"
|
||||
)
|
||||
|
||||
type healthResponse struct {
|
||||
Status string `json:"status"`
|
||||
Time string `json:"time"`
|
||||
}
|
||||
|
||||
func (a *App) Health(w http.ResponseWriter, r *http.Request) {
|
||||
helpers.WriteJSON(w, http.StatusOK, healthResponse{
|
||||
Status: "ok",
|
||||
Time: time.Now().UTC().Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
21
backend/libs/handlers/pages.go
Normal file
21
backend/libs/handlers/pages.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"warpbox.dev/backend/libs/web"
|
||||
)
|
||||
|
||||
type homeData struct {
|
||||
MaxUploadSize string
|
||||
}
|
||||
|
||||
func (a *App) Home(w http.ResponseWriter, r *http.Request) {
|
||||
a.renderer.Render(w, http.StatusOK, "home.gohtml", web.PageData{
|
||||
Title: "Upload your files",
|
||||
Description: "Upload and share files through a self-hosted Warpbox instance.",
|
||||
Data: homeData{
|
||||
MaxUploadSize: a.uploadService.MaxUploadSizeLabel(),
|
||||
},
|
||||
})
|
||||
}
|
||||
31
backend/libs/handlers/static.go
Normal file
31
backend/libs/handlers/static.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (a *App) Static() http.Handler {
|
||||
fileServer := http.StripPrefix("/static/", http.FileServer(http.Dir(a.cfg.StaticDir)))
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
setStaticCacheHeaders(w, r.URL.Path)
|
||||
fileServer.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func setStaticCacheHeaders(w http.ResponseWriter, path string) {
|
||||
ext := strings.ToLower(filepath.Ext(path))
|
||||
|
||||
switch ext {
|
||||
case ".avif", ".gif", ".ico", ".jpg", ".jpeg", ".png", ".svg", ".webp",
|
||||
".mp4", ".m4v", ".mov", ".webm", ".mp3", ".ogg",
|
||||
".eot", ".otf", ".ttf", ".woff", ".woff2":
|
||||
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
|
||||
case ".css", ".js":
|
||||
w.Header().Set("Cache-Control", "public, max-age=86400")
|
||||
default:
|
||||
w.Header().Set("Cache-Control", "public, max-age=3600")
|
||||
}
|
||||
}
|
||||
26
backend/libs/handlers/static_test.go
Normal file
26
backend/libs/handlers/static_test.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSetStaticCacheHeaders(t *testing.T) {
|
||||
tests := map[string]string{
|
||||
"/static/css/app.css": "public, max-age=86400",
|
||||
"/static/js/app.js": "public, max-age=86400",
|
||||
"/static/img/preview.webp": "public, max-age=31536000, immutable",
|
||||
"/static/fonts/ui.woff2": "public, max-age=31536000, immutable",
|
||||
"/static/videos/intro.mp4": "public, max-age=31536000, immutable",
|
||||
"/static/uploads/file.data": "public, max-age=3600",
|
||||
}
|
||||
|
||||
for path, want := range tests {
|
||||
response := httptest.NewRecorder()
|
||||
setStaticCacheHeaders(response, path)
|
||||
|
||||
if got := response.Header().Get("Cache-Control"); got != want {
|
||||
t.Fatalf("Cache-Control for %s = %q, want %q", path, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
19
backend/libs/handlers/upload.go
Normal file
19
backend/libs/handlers/upload.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"warpbox.dev/backend/libs/helpers"
|
||||
)
|
||||
|
||||
type uploadPlaceholderResponse struct {
|
||||
Message string `json:"message"`
|
||||
MaxUploadSize string `json:"maxUploadSize"`
|
||||
}
|
||||
|
||||
func (a *App) UploadPlaceholder(w http.ResponseWriter, r *http.Request) {
|
||||
helpers.WriteJSON(w, http.StatusNotImplemented, uploadPlaceholderResponse{
|
||||
Message: "upload storage is not implemented yet; backend base is ready for the upload pipeline",
|
||||
MaxUploadSize: a.uploadService.MaxUploadSizeLabel(),
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user