fix(monitor): pick conflict-free host ports (8432/5432/4432/6432/3432); JWT keygen-on-boot, healthcheck path, baseline migration; ignore frontend/.pnpm-store; pre-commit excludes
This commit is contained in:
parent
ecaacc48fa
commit
92202b1c16
|
|
@ -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)/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -14,5 +14,6 @@ backend/keys/
|
|||
frontend/dist/
|
||||
frontend/node_modules/
|
||||
frontend/.vite/
|
||||
frontend/.pnpm-store/
|
||||
|
||||
docs/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue