diff --git a/.env.example b/.env.example index 2c3ef81..6fba3c7 100644 --- a/.env.example +++ b/.env.example @@ -2,7 +2,7 @@ # FRONTEND CONFIGURATION # ============================================================================= BASE_URL=http://localhost:3000 -API_URL=http://localhost:8080/api +API_URL=http://localhost:8080 # ============================================================================= # BACKEND CONFIGURATION diff --git a/README.md b/README.md index b0d464b..4ecff88 100644 --- a/README.md +++ b/README.md @@ -48,8 +48,8 @@ services: ports: - "3000:3000" environment: - - NUXT_PUBLIC_BASE_URL=http://localhost:3000 - - NUXT_PUBLIC_API_URL=http://localhost:8080/api + - BASE_URL=http://localhost:3000 + - API_URL=http://localhost:8080 depends_on: - backend @@ -78,8 +78,8 @@ docker run -d \ ```bash docker run -d \ -p 3000:3000 \ - -e NUXT_PUBLIC_BASE_URL=http://localhost:3000 \ - -e NUXT_PUBLIC_API_URL=http://localhost:8080/api \ + -e BASE_URL=http://localhost:3000 \ + -e API_URL=http://localhost:8080 \ ghcr.io/formeraapp/formera-frontend:latest ``` @@ -99,8 +99,8 @@ cd frontend && yarn install && yarn dev | Variable | Description | Default | |----------|-------------|---------| -| `NUXT_PUBLIC_BASE_URL` | Public URL of the frontend | `http://localhost:3000` | -| `NUXT_PUBLIC_API_URL` | Backend API URL | `http://localhost:8080/api` | +| `BASE_URL` | Public URL of the frontend | `http://localhost:3000` | +| `API_URL` | Backend base URL | `http://localhost:8080` | ### Backend @@ -176,8 +176,8 @@ services: image: ghcr.io/formeraapp/formera-frontend:latest restart: unless-stopped environment: - - NUXT_PUBLIC_BASE_URL=https://forms.example.com - - NUXT_PUBLIC_API_URL=https://forms.example.com/api + - BASE_URL=https://forms.example.com + - API_URL=https://forms.example.com labels: - "traefik.enable=true" - "traefik.http.routers.formera.rule=Host(`forms.example.com`)" diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index 3be996e..261bb15 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -179,9 +179,9 @@ func initStorage(cfg *config.Config) (storage.Storage, error) { return s3Store, nil default: - // Build full URL for local storage (ApiURL + LocalURL path) - apiURL := cfg.ApiURL + cfg.Storage.LocalURL - return storage.NewLocalStorage(cfg.Storage.LocalPath, apiURL) + // ApiURL is the backend base URL (e.g., http://localhost:8080) + uploadsURL := cfg.ApiURL + cfg.Storage.LocalURL + return storage.NewLocalStorage(cfg.Storage.LocalPath, uploadsURL) } } diff --git a/backend/internal/config/config.go b/backend/internal/config/config.go index ce18cbe..ecebd58 100644 --- a/backend/internal/config/config.go +++ b/backend/internal/config/config.go @@ -32,7 +32,7 @@ func init() { type Config struct { Port string BaseURL string // Frontend URL (e.g., http://localhost:3000) - ApiURL string // Backend API URL (e.g., http://localhost:8080/api) + ApiURL string // Backend base URL (e.g., http://localhost:8080) DBPath string JWTSecret string CorsOrigin string @@ -100,7 +100,7 @@ func Load() *Config { port := getEnv("PORT", "8080") baseURL := getEnv("BASE_URL", "http://localhost:3000") - apiURL := getEnv("API_URL", "http://localhost:"+port+"/api") + apiURL := getEnv("API_URL", "http://localhost:"+port) // CORS_ORIGIN defaults to BASE_URL if not set (same-origin deployment) corsOrigin := getEnv("CORS_ORIGIN", "") diff --git a/backend/internal/handlers/form.go b/backend/internal/handlers/form.go index 8a80d77..d96a542 100644 --- a/backend/internal/handlers/form.go +++ b/backend/internal/handlers/form.go @@ -243,7 +243,8 @@ func (h *FormHandler) Update(c *gin.Context) { if req.Slug != nil { slug := *req.Slug if slug == "" { - form.Slug = "" + // When slug is cleared, use first 8 chars of ID (form accessible via ID) + form.Slug = form.ID[:8] } else { slug = normalizeSlug(slug) if !isValidSlug(slug) { diff --git a/backend/internal/handlers/setup.go b/backend/internal/handlers/setup.go index 8823400..b8d6956 100644 --- a/backend/internal/handlers/setup.go +++ b/backend/internal/handlers/setup.go @@ -27,6 +27,8 @@ type SetupStatusResponse struct { LogoShowText bool `json:"logo_show_text"` FaviconURL string `json:"favicon_url"` LoginBackgroundURL string `json:"login_background_url"` + Language string `json:"language"` + Theme string `json:"theme"` } type SetupRequest struct { @@ -56,6 +58,8 @@ func (h *SetupHandler) GetStatus(c *gin.Context) { LogoShowText: settings.LogoShowText, FaviconURL: settings.FaviconURL, LoginBackgroundURL: settings.LoginBackgroundURL, + Language: settings.Language, + Theme: settings.Theme, }) } @@ -130,6 +134,8 @@ type UpdateSettingsRequest struct { LogoShowText *bool `json:"logo_show_text"` FaviconURL *string `json:"favicon_url"` LoginBackgroundURL *string `json:"login_background_url"` + Language string `json:"language"` + Theme string `json:"theme"` } func (h *SetupHandler) UpdateSettings(c *gin.Context) { @@ -166,6 +172,12 @@ func (h *SetupHandler) UpdateSettings(c *gin.Context) { if req.LoginBackgroundURL != nil { settings.LoginBackgroundURL = *req.LoginBackgroundURL } + if req.Language != "" { + settings.Language = req.Language + } + if req.Theme != "" { + settings.Theme = req.Theme + } database.DB.Save(&settings) diff --git a/backend/internal/models/form.go b/backend/internal/models/form.go index fb86192..85047d1 100644 --- a/backend/internal/models/form.go +++ b/backend/internal/models/form.go @@ -157,6 +157,11 @@ type Form struct { func (f *Form) BeforeCreate(tx *gorm.DB) error { f.ID = uuid.New().String() + // Auto-generate unique slug from ID if not set + if f.Slug == "" { + // Use first 8 characters of UUID as slug (unique enough) + f.Slug = f.ID[:8] + } if f.Status == "" { f.Status = FormStatusDraft } diff --git a/backend/internal/models/settings.go b/backend/internal/models/settings.go index f9cf981..e27d606 100644 --- a/backend/internal/models/settings.go +++ b/backend/internal/models/settings.go @@ -49,8 +49,11 @@ type Settings struct { LogoShowText bool `json:"logo_show_text" gorm:"default:true"` FaviconURL string `json:"favicon_url"` LoginBackgroundURL string `json:"login_background_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` + // Language and Theme + Language string `json:"language" gorm:"default:en"` + Theme string `json:"theme" gorm:"default:system"` // "light", "dark", or "system" + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` } func GetDefaultSettings() *Settings { @@ -65,5 +68,7 @@ func GetDefaultSettings() *Settings { LogoShowText: true, FaviconURL: "", LoginBackgroundURL: "", + Language: "en", + Theme: "system", } } diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 11c6a1a..8963788 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -20,10 +20,13 @@ RUN apk add --no-cache ca-certificates tzdata WORKDIR /app COPY --from=builder /app/.output ./.output +COPY docker-entrypoint.sh /docker-entrypoint.sh -# Environment variables (override with NUXT_PUBLIC_BASE_URL and NUXT_PUBLIC_API_URL) -ENV NUXT_PUBLIC_BASE_URL=http://localhost:3000 -ENV NUXT_PUBLIC_API_URL=http://localhost:8080/api +RUN chmod +x /docker-entrypoint.sh + +# Environment variables - use simple BASE_URL and API_URL +ENV BASE_URL=http://localhost:3000 +ENV API_URL=http://localhost:8080 ENV NITRO_PORT=3000 EXPOSE 3000 @@ -31,4 +34,5 @@ EXPOSE 3000 RUN addgroup -S app && adduser -S app -G app \ && chown -R app:app /app USER app +ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["node", ".output/server/index.mjs"] diff --git a/frontend/app/app.vue b/frontend/app/app.vue index 355a7e3..2ab3e21 100644 --- a/frontend/app/app.vue +++ b/frontend/app/app.vue @@ -1,14 +1,3 @@ - -