diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 98df1251..20efe34a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -164,12 +164,22 @@ repos: rev: v6.0.0 hooks: - id: check-yaml + exclude: (\.pnpm-store|node_modules)/ - id: check-json + exclude: (\.pnpm-store|node_modules)/ - id: check-toml + exclude: (\.pnpm-store|node_modules)/ - id: check-ast + exclude: (\.pnpm-store|node_modules|\.venv)/ - id: check-symlinks + exclude: (\.pnpm-store|node_modules)/ - id: end-of-file-fixer + exclude: (\.pnpm-store|node_modules)/ - id: trailing-whitespace + exclude: (\.pnpm-store|node_modules)/ - id: check-illegal-windows-names + exclude: (\.pnpm-store|node_modules)/ - id: check-executables-have-shebangs + exclude: (\.pnpm-store|node_modules)/ - id: check-shebang-scripts-are-executable + exclude: (\.pnpm-store|node_modules)/ diff --git a/PROJECTS/advanced/monitor-the-situation-dashboard/.env.example b/PROJECTS/advanced/monitor-the-situation-dashboard/.env.example index 39b2128b..003d47e0 100644 --- a/PROJECTS/advanced/monitor-the-situation-dashboard/.env.example +++ b/PROJECTS/advanced/monitor-the-situation-dashboard/.env.example @@ -2,13 +2,13 @@ # .env.example APP_NAME=monitor -PUBLIC_URL=http://localhost:8430 +PUBLIC_URL=http://localhost:8432 -NGINX_HOST_PORT=8430 -BACKEND_HOST_PORT=5430 -FRONTEND_HOST_PORT=3430 -POSTGRES_HOST_PORT=5431 -REDIS_HOST_PORT=6430 +NGINX_HOST_PORT=8432 +BACKEND_HOST_PORT=5432 +FRONTEND_HOST_PORT=3432 +POSTGRES_HOST_PORT=4432 +REDIS_HOST_PORT=6432 POSTGRES_USER=monitor POSTGRES_PASSWORD=changeme diff --git a/PROJECTS/advanced/monitor-the-situation-dashboard/.gitignore b/PROJECTS/advanced/monitor-the-situation-dashboard/.gitignore index 48cf6544..df37de2a 100644 --- a/PROJECTS/advanced/monitor-the-situation-dashboard/.gitignore +++ b/PROJECTS/advanced/monitor-the-situation-dashboard/.gitignore @@ -14,5 +14,6 @@ backend/keys/ frontend/dist/ frontend/node_modules/ frontend/.vite/ +frontend/.pnpm-store/ docs/ diff --git a/PROJECTS/advanced/monitor-the-situation-dashboard/backend/cmd/api/main.go b/PROJECTS/advanced/monitor-the-situation-dashboard/backend/cmd/api/main.go index a4eac3f3..9fbf02db 100644 --- a/PROJECTS/advanced/monitor-the-situation-dashboard/backend/cmd/api/main.go +++ b/PROJECTS/advanced/monitor-the-situation-dashboard/backend/cmd/api/main.go @@ -10,6 +10,7 @@ import ( "log/slog" "os" "os/signal" + "path/filepath" "syscall" "time" @@ -97,6 +98,10 @@ func run(configPath string) error { "pool_size", cfg.Redis.PoolSize, ) + if err := ensureJWTKeys(cfg.JWT, logger); err != nil { + return err + } + jwtManager, err := auth.NewJWTManager(cfg.JWT) if err != nil { return err @@ -237,6 +242,31 @@ func run(configPath string) error { return nil } +func ensureJWTKeys(cfg config.JWTConfig, logger *slog.Logger) error { + if _, err := os.Stat(cfg.PrivateKeyPath); err == nil { + return nil + } else if !errors.Is(err, os.ErrNotExist) { + return err + } + + if dir := filepath.Dir(cfg.PrivateKeyPath); dir != "" && dir != "." { + if err := os.MkdirAll(dir, 0o700); err != nil { + return err + } + } + if dir := filepath.Dir(cfg.PublicKeyPath); dir != "" && dir != "." { + if err := os.MkdirAll(dir, 0o700); err != nil { + return err + } + } + + logger.Warn("JWT keys missing, generating", + "private", cfg.PrivateKeyPath, + "public", cfg.PublicKeyPath, + ) + return auth.GenerateKeyPair(cfg.PrivateKeyPath, cfg.PublicKeyPath) +} + func setupLogger(cfg config.LogConfig) *slog.Logger { var handler slog.Handler diff --git a/PROJECTS/advanced/monitor-the-situation-dashboard/backend/migrations/0001_template_baseline.sql b/PROJECTS/advanced/monitor-the-situation-dashboard/backend/migrations/0001_template_baseline.sql new file mode 100644 index 00000000..1f06134e --- /dev/null +++ b/PROJECTS/advanced/monitor-the-situation-dashboard/backend/migrations/0001_template_baseline.sql @@ -0,0 +1,52 @@ +-- ©AngelaMos | 2026 +-- 00000_template_baseline.sql + +-- +goose Up +-- +goose StatementBegin +CREATE EXTENSION IF NOT EXISTS "pgcrypto"; +-- +goose StatementEnd + +-- +goose StatementBegin +CREATE TABLE users ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + email text NOT NULL UNIQUE, + password_hash text NOT NULL, + name text NOT NULL DEFAULT '', + role text NOT NULL DEFAULT 'user', + tier text NOT NULL DEFAULT 'free', + token_version integer NOT NULL DEFAULT 0, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now(), + deleted_at timestamptz +); +CREATE INDEX idx_users_email ON users (email) WHERE deleted_at IS NULL; +CREATE INDEX idx_users_role ON users (role) WHERE deleted_at IS NULL; +CREATE INDEX idx_users_created ON users (created_at DESC); +-- +goose StatementEnd + +-- +goose StatementBegin +CREATE TABLE refresh_tokens ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid(), + user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE, + token_hash text NOT NULL UNIQUE, + family_id uuid NOT NULL, + expires_at timestamptz NOT NULL, + created_at timestamptz NOT NULL DEFAULT now(), + is_used boolean NOT NULL DEFAULT false, + used_at timestamptz, + revoked_at timestamptz, + replaced_by_id uuid, + user_agent text NOT NULL DEFAULT '', + ip_address text NOT NULL DEFAULT '' +); +CREATE INDEX idx_refresh_tokens_user ON refresh_tokens (user_id); +CREATE INDEX idx_refresh_tokens_family ON refresh_tokens (family_id); +CREATE INDEX idx_refresh_tokens_expires ON refresh_tokens (expires_at); +CREATE INDEX idx_refresh_tokens_hash ON refresh_tokens (token_hash); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP TABLE IF EXISTS refresh_tokens; +DROP TABLE IF EXISTS users; +-- +goose StatementEnd diff --git a/PROJECTS/advanced/monitor-the-situation-dashboard/backend/migrations/0001_alerts.sql b/PROJECTS/advanced/monitor-the-situation-dashboard/backend/migrations/0002_alerts.sql similarity index 100% rename from PROJECTS/advanced/monitor-the-situation-dashboard/backend/migrations/0001_alerts.sql rename to PROJECTS/advanced/monitor-the-situation-dashboard/backend/migrations/0002_alerts.sql diff --git a/PROJECTS/advanced/monitor-the-situation-dashboard/backend/migrations/0002_panel_data.sql b/PROJECTS/advanced/monitor-the-situation-dashboard/backend/migrations/0003_panel_data.sql similarity index 100% rename from PROJECTS/advanced/monitor-the-situation-dashboard/backend/migrations/0002_panel_data.sql rename to PROJECTS/advanced/monitor-the-situation-dashboard/backend/migrations/0003_panel_data.sql diff --git a/PROJECTS/advanced/monitor-the-situation-dashboard/backend/migrations/0003_collector_state.sql b/PROJECTS/advanced/monitor-the-situation-dashboard/backend/migrations/0004_collector_state.sql similarity index 100% rename from PROJECTS/advanced/monitor-the-situation-dashboard/backend/migrations/0003_collector_state.sql rename to PROJECTS/advanced/monitor-the-situation-dashboard/backend/migrations/0004_collector_state.sql diff --git a/PROJECTS/advanced/monitor-the-situation-dashboard/compose.yml b/PROJECTS/advanced/monitor-the-situation-dashboard/compose.yml index b3d9d08c..cde7224f 100644 --- a/PROJECTS/advanced/monitor-the-situation-dashboard/compose.yml +++ b/PROJECTS/advanced/monitor-the-situation-dashboard/compose.yml @@ -10,7 +10,7 @@ services: dockerfile: conf/docker/prod/vite.docker container_name: ${APP_NAME:-monitor}-nginx ports: - - "${NGINX_HOST_PORT:-8430}:80" + - "${NGINX_HOST_PORT:-8432}:80" depends_on: backend: condition: service_healthy diff --git a/PROJECTS/advanced/monitor-the-situation-dashboard/conf/docker/dev/vite.docker b/PROJECTS/advanced/monitor-the-situation-dashboard/conf/docker/dev/vite.docker index e3400dcb..4c09838b 100644 --- a/PROJECTS/advanced/monitor-the-situation-dashboard/conf/docker/dev/vite.docker +++ b/PROJECTS/advanced/monitor-the-situation-dashboard/conf/docker/dev/vite.docker @@ -11,4 +11,4 @@ ENV NODE_ENV=development EXPOSE 5173 -CMD ["pnpm", "dev", "--host", "0.0.0.0"] +CMD ["sh", "-c", "pnpm install && pnpm dev --host 0.0.0.0"] diff --git a/PROJECTS/advanced/monitor-the-situation-dashboard/dev.compose.yml b/PROJECTS/advanced/monitor-the-situation-dashboard/dev.compose.yml index 465186c6..e4bce9a0 100644 --- a/PROJECTS/advanced/monitor-the-situation-dashboard/dev.compose.yml +++ b/PROJECTS/advanced/monitor-the-situation-dashboard/dev.compose.yml @@ -8,7 +8,7 @@ services: image: nginx:1.27-alpine container_name: ${APP_NAME:-monitor}-nginx-dev ports: - - "${NGINX_HOST_PORT:-8430}:80" + - "${NGINX_HOST_PORT:-8432}:80" volumes: - ./conf/nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./conf/nginx/dev.nginx:/etc/nginx/conf.d/default.conf:ro @@ -28,7 +28,7 @@ services: dockerfile: conf/docker/dev/go.docker container_name: ${APP_NAME:-monitor}-backend-dev ports: - - "${BACKEND_HOST_PORT:-5430}:8080" + - "${BACKEND_HOST_PORT:-5432}:8080" volumes: - ./backend:/app - go-mod-cache:/go/pkg/mod @@ -46,7 +46,7 @@ services: networks: - backend healthcheck: - test: ["CMD-SHELL", "wget -q -O- http://localhost:8080/api/v1/healthz || exit 1"] + test: ["CMD-SHELL", "wget -q -O- http://localhost:8080/healthz || exit 1"] interval: 10s timeout: 5s retries: 5 @@ -59,7 +59,7 @@ services: dockerfile: conf/docker/dev/vite.docker container_name: ${APP_NAME:-monitor}-frontend-dev ports: - - "${FRONTEND_HOST_PORT:-3430}:5173" + - "${FRONTEND_HOST_PORT:-3432}:5173" volumes: - ./frontend:/app - frontend-node-modules:/app/node_modules @@ -73,7 +73,7 @@ services: image: postgres:17-alpine container_name: ${APP_NAME:-monitor}-postgres-dev ports: - - "${POSTGRES_HOST_PORT:-5431}:5432" + - "${POSTGRES_HOST_PORT:-4432}:5432" environment: POSTGRES_USER: ${POSTGRES_USER:-monitor} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-monitor} @@ -94,7 +94,7 @@ services: image: redis:7-alpine container_name: ${APP_NAME:-monitor}-redis-dev ports: - - "${REDIS_HOST_PORT:-6430}:6379" + - "${REDIS_HOST_PORT:-6432}:6379" command: redis-server --appendonly yes volumes: - redisdata-dev:/data