Add OpenAPI docs, frontend, migrations, and API updates

- OpenAPI: add missing endpoints (add-from-url, subscriptions, public availability)
- OpenAPI: CalendarSubscription schema, Subscriptions tag
- Frontend app
- Migrations: count_for_availability, subscriptions_sync, user_preferences, calendar_settings
- Config, rate limit, auth, calendar, booking, ICS, availability, user service updates

Made-with: Cursor
This commit is contained in:
Michilis
2026-03-02 14:07:55 +00:00
parent 2cb9d72a7f
commit 75105b8b46
8120 changed files with 1486881 additions and 314 deletions

View File

@@ -2,31 +2,84 @@ package config
import (
"bufio"
"log"
"os"
"strconv"
"strings"
)
type Config struct {
DatabaseURL string
JWTSecret string
RedisAddr string
ServerPort string
Env string
BaseURL string
DatabaseURL string
JWTSecret string
RedisAddr string
ServerPort string
Env string
BaseURL string
CORSOrigins []string
RateLimitRPS float64
RateLimitBurst int
}
func Load() *Config {
loadEnvFile(".env")
port := getEnv("SERVER_PORT", "8080")
return &Config{
DatabaseURL: getEnv("DATABASE_URL", "postgres://postgres:postgres@localhost:5432/calendarapi?sslmode=disable"),
JWTSecret: getEnv("JWT_SECRET", "dev-secret-change-me"),
RedisAddr: os.Getenv("REDIS_ADDR"),
ServerPort: port,
Env: getEnv("ENV", "development"),
BaseURL: getEnv("BASE_URL", "http://localhost:"+port),
jwtSecret := getEnv("JWT_SECRET", "dev-secret-change-me")
env := getEnv("ENV", "development")
if env == "production" && (jwtSecret == "" || jwtSecret == "dev-secret-change-me") {
log.Fatal("JWT_SECRET must be set to a secure value in production")
}
corsOrigins := parseCORSOrigins(getEnv("CORS_ORIGINS", "http://localhost:5173,http://127.0.0.1:5173"))
rateRPS := parseFloat(getEnv("RATE_LIMIT_RPS", "100"), 100)
rateBurst := parseInt(getEnv("RATE_LIMIT_BURST", "200"), 200)
return &Config{
DatabaseURL: getEnv("DATABASE_URL", "postgres://postgres:postgres@localhost:5432/calendarapi?sslmode=disable"),
JWTSecret: jwtSecret,
RedisAddr: os.Getenv("REDIS_ADDR"),
ServerPort: port,
Env: env,
BaseURL: getEnv("BASE_URL", "http://localhost:"+port),
CORSOrigins: corsOrigins,
RateLimitRPS: rateRPS,
RateLimitBurst: rateBurst,
}
}
func parseCORSOrigins(s string) []string {
if s == "" {
return []string{}
}
parts := strings.Split(s, ",")
out := make([]string, 0, len(parts))
for _, p := range parts {
p = strings.TrimSpace(p)
if p != "" {
out = append(out, p)
}
}
if len(out) == 0 {
return []string{"http://localhost:5173", "http://127.0.0.1:5173"}
}
return out
}
func parseFloat(s string, def float64) float64 {
v, err := strconv.ParseFloat(s, 64)
if err != nil {
return def
}
return v
}
func parseInt(s string, def int) int {
v, err := strconv.Atoi(s)
if err != nil {
return def
}
return v
}
func loadEnvFile(path string) {