Add timezone backend support (handlers, utilities, user context)
- Add FormatInTimezone and FormatTimestamptzInTimezone helpers in templates/utils.go for timezone-aware time display - Add Timezone field to templates.User struct - Pass user timezone from DB to template context in helpers.go - Add timezone update handling in auth.go UpdateProfile with validation via time.LoadLocation - Add UpdateTimezoneSettings handler in system_settings.go for admin system-wide default timezone using UpdateSystemSetting
This commit is contained in:
@@ -87,6 +87,7 @@ type UpdateProfileRequest struct {
|
|||||||
FirstName string `json:"first_name,omitempty" validate:"omitempty,max=100"`
|
FirstName string `json:"first_name,omitempty" validate:"omitempty,max=100"`
|
||||||
LastName string `json:"last_name,omitempty" validate:"omitempty,max=100"`
|
LastName string `json:"last_name,omitempty" validate:"omitempty,max=100"`
|
||||||
Theme string `json:"theme,omitempty" validate:"omitempty"`
|
Theme string `json:"theme,omitempty" validate:"omitempty"`
|
||||||
|
Timezone string `json:"timezone,omitempty" validate:"omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AdminUpdateUserRequest struct {
|
type AdminUpdateUserRequest struct {
|
||||||
@@ -564,6 +565,22 @@ func (h *AuthHandler) UpdateProfile(c *echo.Context) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update timezone
|
||||||
|
if req.Timezone != "" {
|
||||||
|
if _, err := time.LoadLocation(req.Timezone); err != nil {
|
||||||
|
return c.JSON(http.StatusBadRequest, map[string]string{
|
||||||
|
"error": "Invalid timezone",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
err := h.db.UpdateUserTimezone(ctx, database.UpdateUserTimezoneParams{
|
||||||
|
ID: pgtype.UUID{Bytes: userUUID, Valid: true},
|
||||||
|
Timezone: pgtype.Text{String: req.Timezone, Valid: true},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update email (if provided)
|
// Update email (if provided)
|
||||||
if req.Email != "" {
|
if req.Email != "" {
|
||||||
existingUser, err := h.db.GetUserByEmail(c.Request().Context(), req.Email)
|
existingUser, err := h.db.GetUserByEmail(c.Request().Context(), req.Email)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5"
|
"github.com/jackc/pgx/v5"
|
||||||
"github.com/labstack/echo/v5"
|
"github.com/labstack/echo/v5"
|
||||||
@@ -31,6 +32,28 @@ type ScanSettingsResponse struct {
|
|||||||
Message string `json:"message,omitempty"`
|
Message string `json:"message,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UpdateTimezoneSettingsRequest struct {
|
||||||
|
DefaultTimezone string `json:"default_timezone" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *SystemSettingsHandler) UpdateTimezoneSettings(c *echo.Context) error {
|
||||||
|
var req UpdateTimezoneSettingsRequest
|
||||||
|
if err := c.Bind(&req); err != nil {
|
||||||
|
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid request"})
|
||||||
|
}
|
||||||
|
if _, err := time.LoadLocation(req.DefaultTimezone); err != nil {
|
||||||
|
return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid timezone"})
|
||||||
|
}
|
||||||
|
err := h.db.UpdateSystemSetting(c.Request().Context(), database.UpdateSystemSettingParams{
|
||||||
|
SettingKey: "default_timezone",
|
||||||
|
SettingValue: req.DefaultTimezone,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.JSON(http.StatusOK, map[string]string{"message": "Timezone updated"})
|
||||||
|
}
|
||||||
|
|
||||||
func (h *SystemSettingsHandler) UpdateScanSettings(c *echo.Context) error {
|
func (h *SystemSettingsHandler) UpdateScanSettings(c *echo.Context) error {
|
||||||
var req UpdateScanSettingsRequest
|
var req UpdateScanSettingsRequest
|
||||||
if err := c.Bind(&req); err != nil {
|
if err := c.Bind(&req); err != nil {
|
||||||
|
|||||||
@@ -35,6 +35,11 @@ func getTemplateUserWithTheme(c *echo.Context, cfg *Config) (templates.User, err
|
|||||||
userTheme = userDB.Theme.String
|
userTheme = userDB.Theme.String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
userTimezone := "UTC"
|
||||||
|
if userDB.Timezone.Valid {
|
||||||
|
userTimezone = userDB.Timezone.String
|
||||||
|
}
|
||||||
|
|
||||||
// Extract JWT token for WebSocket authentication
|
// Extract JWT token for WebSocket authentication
|
||||||
token := ""
|
token := ""
|
||||||
if cookie, err := c.Cookie("token"); err == nil {
|
if cookie, err := c.Cookie("token"); err == nil {
|
||||||
@@ -48,6 +53,7 @@ func getTemplateUserWithTheme(c *echo.Context, cfg *Config) (templates.User, err
|
|||||||
Role: userRole,
|
Role: userRole,
|
||||||
Theme: userTheme,
|
Theme: userTheme,
|
||||||
Token: token,
|
Token: token,
|
||||||
|
Timezone: userTimezone,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ type User struct {
|
|||||||
LastName string
|
LastName string
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
Token string
|
Token string
|
||||||
|
Timezone string
|
||||||
}
|
}
|
||||||
|
|
||||||
type PageData struct {
|
type PageData struct {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/jackc/pgx/v5/pgtype"
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
@@ -197,3 +198,25 @@ func formatAlternateInfo(data []byte) string {
|
|||||||
|
|
||||||
return strings.Join(parts, " ")
|
return strings.Join(parts, " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FormatInTimezone formats a time.Time in the specified timezone as MM-DD-YYYY HH:MM
|
||||||
|
func FormatInTimezone(t time.Time, timezone string) string {
|
||||||
|
if t.IsZero() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
loc, err := time.LoadLocation(timezone)
|
||||||
|
if err != nil {
|
||||||
|
loc = time.UTC
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.In(loc).Format("01-02-2006 03:04 PM")
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatTimestamptzInTimezone formats a pgtype.Timestamptz in the specified timezone
|
||||||
|
func FormatTimestamptzInTimezone(t pgtype.Timestamptz, timezone string) string {
|
||||||
|
if !t.Valid {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return FormatInTimezone(t.Time, timezone)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user