feat: add router package structure for route organization

Create internal/router/ package to organize route registration:
- router.go: Main router setup and configuration
- auth.go: Authentication routes (login, register, profile, etc.)
- docs.go: Documentation routes
- frontend.go: Frontend SSR routes (/, /login, /admin, etc.)
- helpers.go: Helper functions for template rendering

This is the first step in refactoring 858-line main.go into
a more maintainable structure following Go best practices.

Routes themselves have NOT changed - only organization.
This commit is contained in:
2026-02-06 11:09:26 -05:00
parent 2a7338200c
commit 9bc8cd7bf3
5 changed files with 504 additions and 0 deletions
+97
View File
@@ -0,0 +1,97 @@
package router
import (
"context"
"time"
"bookhoard/internal/handlers"
"bookhoard/templates"
"github.com/google/uuid"
"github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgxpool"
"github.com/labstack/echo/v4"
)
func getTemplateUserWithTheme(c echo.Context, cfg *Config) (templates.User, error) {
userID := c.Get("user_id").(string)
userEmail := c.Get("user_email").(string)
userUsername := c.Get("user_username").(string)
userRole := c.Get("user_role").(string)
userUUID, err := uuid.Parse(userID)
if err != nil {
return templates.User{}, err
}
userDB, err := cfg.Queries.GetUser(c.Request().Context(), uuidToPGType(userUUID))
if err != nil {
return templates.User{}, err
}
userTheme := "tokyo-night"
if userDB.Theme.Valid {
userTheme = userDB.Theme.String
}
return templates.User{
ID: userID,
Email: userEmail,
Username: userUsername,
Role: userRole,
Theme: userTheme,
}, nil
}
func convertDevices(deviceInfos []handlers.DeviceInfo) []templates.DeviceData {
result := make([]templates.DeviceData, len(deviceInfos))
for i, d := range deviceInfos {
lastSync := ""
if d.LastSync != nil {
lastSync = d.LastSync.Format(time.RFC3339)
}
lastSeen := ""
if d.LastSeen != nil {
lastSeen = d.LastSeen.Format(time.RFC3339)
}
result[i] = templates.DeviceData{
ID: d.ID.String(),
DeviceName: d.DeviceName,
DeviceType: d.DeviceType,
SyncEnabled: d.SyncEnabled,
LastSync: lastSync,
LastSeen: lastSeen,
}
}
return result
}
func convertPending(pending []map[string]interface{}) []templates.PendingRegistrationData {
result := make([]templates.PendingRegistrationData, len(pending))
for i, p := range pending {
result[i] = templates.PendingRegistrationData{
RegistrationID: p["registration_id"].(string),
DeviceName: p["device_name"].(string),
DeviceType: p["device_type"].(string),
ExpiresAt: p["expires_at"].(string),
}
}
return result
}
func pingDB(cfg *Config, ctx context.Context) error {
if cfg.DBPool != nil {
if pool, ok := cfg.DBPool.(*pgxpool.Pool); ok {
return pool.Ping(ctx)
}
}
return nil
}
func parseUUID(s string) (uuid.UUID, error) {
return uuid.Parse(s)
}
func uuidToPGType(u uuid.UUID) pgtype.UUID {
return pgtype.UUID{Bytes: [16]byte(u), Valid: true}
}