Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
6f87bfc
chore(deps)(deps): bump socket.io-parser from 4.2.4 to 4.2.6
dependabot[bot] Mar 19, 2026
dce1866
docs: establish documentation structure and conventions
sabrydawood May 20, 2026
24a7097
Merge pull request #72 from FutureSolutionDev/dependabot/npm_and_yarn…
sabrydawood May 20, 2026
fbe0c59
chore: ignore docs/spec-kit in .gitignore
sabrydawood May 23, 2026
2f2645c
feat(queue): implement persistent BullMQ-backed deployment queue
sabrydawood May 23, 2026
169ad7e
feat(db): add environment variables support
sabrydawood May 23, 2026
455bc0a
feat(api): implement environment variables and git bare-clone cache
sabrydawood May 23, 2026
ba413a2
feat(deployment): add log download endpoint and expand test suite
sabrydawood May 23, 2026
ce95a2b
feat(notifications): implement subscription-based notification system
sabrydawood May 23, 2026
2d27641
feat(db): add migration status reporting and update env template
sabrydawood May 23, 2026
aa5b502
chore(cli): add migration management scripts and npm commands
sabrydawood May 23, 2026
f2ad05c
refactor(api): improve validation error messages and query reliability
sabrydawood May 23, 2026
eee640e
feat(workspace): implement workspace management and project association
sabrydawood May 23, 2026
6b81624
refactor(db): enforce migration discipline and improve security middl…
sabrydawood May 24, 2026
12d17f8
feat(deployment): implement rollback functionality and expand test suite
sabrydawood May 24, 2026
37ca5ac
feat(templates): implement project templates and finalize v3.0 GA
sabrydawood May 24, 2026
e3320ae
Merge branch 'master' of https://github.com/FutureSolutionDev/Deploy-…
sabrydawood May 24, 2026
b84aac5
build(deps): update sequelize and add dependency overrides
sabrydawood May 24, 2026
a092f0c
chore(db): drop legacy notification columns from user_settings
sabrydawood May 24, 2026
c79f2e6
fix(db): use raw SQL for dropping columns in migration 020
sabrydawood May 24, 2026
d7dd602
build(ui): update production assets and modulepreloads
sabrydawood May 24, 2026
45e337d
docs: mark v3.0 Foundation as released
sabrydawood May 24, 2026
eb996c4
fix(v3.0): address PR #80 review — CRITICAL + HIGH + MEDIUM
sabrydawood May 24, 2026
cdbd305
ci: add Redis service + run on master/version branches
sabrydawood May 24, 2026
3c996ec
fix(ci): align MariaDB service with .env.test + fix WorkspaceIconsParity
sabrydawood May 24, 2026
51a11ad
fix(tests): load .env.test via jest setupFiles (was loaded too late)
sabrydawood May 24, 2026
82eaaa2
debug(ci): add diagnostic to jest.env.setup so CI shows whether .env.…
sabrydawood May 24, 2026
ee85873
fix(ci): commit .env.test fixture so jest.env.setup can load it in CI
sabrydawood May 24, 2026
041ab79
fix(tests): sync v2.1 baseline tables before running migrations + cle…
sabrydawood May 24, 2026
58f1eda
fix(ci): unblock CI hang — migration 005 + jest forceExit
sabrydawood May 24, 2026
ae404f0
fix(tests): pass all integration suites in CI (verified locally first)
sabrydawood May 24, 2026
6b59ebe
fix(security): add rate-limit middleware to all v3.0 routes (CodeQL f…
sabrydawood May 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# === Server Configuration ===
NODE_ENV=development
PORT=9090
HOST=0.0.0.0

# === Database Configuration (MariaDB / MySQL 8) ===
DB_HOST=localhost
DB_PORT=3306
DB_NAME=deploy_center
DB_USERNAME=deploycenter
DB_PASSWORD=change_me_strong_password
DB_DIALECT=mariadb
DB_POOL_MAX=20
DB_POOL_MIN=5
DB_POOL_ACQUIRE=30000
DB_POOL_IDLE=10000
DB_AUTO_MIGRATE=true
# Opt-in dev escape hatch — when "true", DatabaseInitializer runs
# `sync({ alter: true })` to align column DDL with model definitions for
# tables the migration runner hasn't fully created. Leave OFF in production;
# migrations are the source of truth (Constitution Principle IV).
DB_FORCE_SYNC_ALTER=false
# Used only by docker-compose to bootstrap the MariaDB root user:
DB_ROOT_PASSWORD=change_me_root_password

# === JWT Configuration ===
# Generate with: openssl rand -hex 64
JWT_SECRET=replace_with_64_byte_hex
JWT_EXPIRY=7d
JWT_REFRESH_SECRET=replace_with_different_64_byte_hex
JWT_REFRESH_EXPIRY=30d

# === Encryption ===
# Master key for AES-256-GCM. Used by SSH keys, env vars (F-003), notif provider creds (F-006).
# Generate with: openssl rand -hex 32
ENCRYPTION_KEY=replace_with_32_byte_hex

# === Redis (NEW in v3.0 — required by BullMQ queue, F-001) ===
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0

# === GitHub Webhook ===
GITHUB_WEBHOOK_SECRET=replace_with_webhook_secret

# === Discord Notifications (legacy — v3.0 keeps backward compat; new channels via F-006) ===
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/your_webhook_here
DISCORD_ENABLED=false

# === Slack Notifications (legacy) ===
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/your_webhook_here
SLACK_ENABLED=false

# === Email Configuration (SMTP) ===
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=your_email@example.com
SMTP_PASSWORD=your_app_password_here
EMAIL_FROM=Deploy Center <noreply@example.com>
EMAIL_ENABLED=false

# === Telegram Bot (v2.1 legacy — out of v3.0 scope, preserved for compat) ===
TELEGRAM_BOT_TOKEN=your_bot_token_here
TELEGRAM_CHAT_ID=your_chat_id_here
TELEGRAM_ENABLED=false

# === Logging ===
LOG_LEVEL=info
LOG_DIR=./logs
LOG_MAX_FILES=30d
LOG_MAX_SIZE=20m

# === Rate Limiting ===
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX_REQUESTS=100

# === CORS ===
CORS_ORIGIN=http://localhost:9090,http://localhost:5173
CORS_CREDENTIALS=true

# === Session ===
SESSION_SECRET=replace_with_random_string

# === Deployment Configuration ===
BACKUP_DIR=./backups
BACKUP_RETENTION_DAYS=30
MAX_CONCURRENT_DEPLOYMENTS=5

# === Default Admin Bootstrap ===
DEFAULT_ADMIN_USERNAME=admin
DEFAULT_ADMIN_EMAIL=admin@example.com
DEFAULT_ADMIN_PASSWORD=change_me_admin_password

# === Health Check ===
HEALTH_CHECK_INTERVAL_MINUTES=5
HEALTH_CHECK_TIMEOUT_MS=5000

# === Cleanup Job ===
CLEANUP_LOGS_DAYS=90
CLEANUP_BACKUPS_DAYS=30
CLEANUP_INTERVAL_HOURS=24

# === Test gates (only honored when NODE_ENV=test) ===
# Set to "1" to run the 30-minute Socket.IO log-stream stability test
# (otherwise skipped). Don't enable in CI by default.
# RUN_LONG_STREAM_TEST=1
# Set to "1" to run the slow BullMQ retry-policy test which deliberately
# waits for the full backoff schedule (1s → 5s → 25s) and is too slow for
# the default unit run.
# RUN_SLOW_QUEUE_TEST=1
67 changes: 67 additions & 0 deletions .env.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Deploy Center — test environment. Loaded by jest via dotenv-cli or setupTestDb.ts.
# Isolated MySQL schema + isolated Redis logical DB so tests never touch dev data.

NODE_ENV=test
PORT=9091
HOST=127.0.0.1

# Isolated test schema — created by setupTestDb.ts before suite, dropped after.
DB_HOST=localhost
DB_PORT=3306
DB_NAME=deploy_center_test
DB_USERNAME=deploycenter
DB_PASSWORD=deploycenter_test_password
# Use 'mysql' dialect (mysql2 npm driver) NOT 'mariadb' — sequelize@6's
# mariadb dialect has a long-standing formatResults bug that throws
# `Cannot delete property 'meta' of [object Array]` on every INSERT,
# DROP COLUMN, and other operations whose result is an array. mysql2
# driver talks to MariaDB server fine (wire-protocol compatible) and
# doesn't have the bug.
DB_DIALECT=mysql
DB_POOL_MAX=5
DB_POOL_MIN=1
DB_POOL_ACQUIRE=10000
DB_POOL_IDLE=5000
DB_AUTO_MIGRATE=false

# Test-only short-lived JWTs.
JWT_SECRET=test_jwt_secret_not_for_production_use_only_in_tests_12345678901234567890
JWT_EXPIRY=1h
JWT_REFRESH_SECRET=test_jwt_refresh_secret_not_for_production_use_only_in_tests_1234567890
JWT_REFRESH_EXPIRY=2h

# Test encryption key (32-byte hex, deterministic for reproducible tests).
ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000

# Isolated Redis DB index — test helpers FLUSHDB only this index.
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=1

# Notifications disabled in tests; dispatchers mocked.
DISCORD_ENABLED=false
SLACK_ENABLED=false
EMAIL_ENABLED=false
TELEGRAM_ENABLED=false

# Quiet logs during tests.
LOG_LEVEL=error
LOG_DIR=./logs/test

# Disable rate limiting for tests.
RATE_LIMIT_WINDOW_MS=0
RATE_LIMIT_MAX_REQUESTS=10000

# Test admin bootstrap.
DEFAULT_ADMIN_USERNAME=testadmin
DEFAULT_ADMIN_EMAIL=testadmin@test.local
DEFAULT_ADMIN_PASSWORD=TestAdmin@123

# Background jobs disabled in tests (re-enable per-suite as needed).
HEALTH_CHECK_INTERVAL_MINUTES=0
CLEANUP_INTERVAL_HOURS=0

CORS_ORIGIN=http://localhost:9091
CORS_CREDENTIALS=true
SESSION_SECRET=test_session_secret_not_for_production
24 changes: 20 additions & 4 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
],
"rules": {
"@typescript-eslint/naming-convention": [
"error",
"warn",
{
"selector": "default",
"format": ["PascalCase", "camelCase"],
Expand Down Expand Up @@ -48,9 +48,25 @@
],
"@typescript-eslint/explicit-function-return-type": "warn",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/await-thenable": "error",
"@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }],
"@typescript-eslint/no-floating-promises": "warn",
"@typescript-eslint/no-misused-promises": "warn",
"@typescript-eslint/await-thenable": "warn",
"@typescript-eslint/no-unsafe-assignment": "off",
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/no-unsafe-call": "off",
"@typescript-eslint/no-unsafe-return": "off",
"@typescript-eslint/no-unsafe-argument": "off",
"@typescript-eslint/restrict-template-expressions": "off",
"@typescript-eslint/restrict-plus-operands": "off",
"@typescript-eslint/unbound-method": "warn",
"@typescript-eslint/require-await": "warn",
"@typescript-eslint/no-unnecessary-type-assertion": "warn",
"@typescript-eslint/ban-types": "warn",
"@typescript-eslint/no-implied-eval": "warn",
"@typescript-eslint/no-var-requires": "warn",
"@typescript-eslint/no-base-to-string": "warn",
"no-useless-escape": "warn",
"no-console": ["warn", { "allow": ["warn", "error"] }]
},
"env": {
Expand Down
4 changes: 1 addition & 3 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,10 @@ open_collective: # deploy-center # Not supported yet
# Ko-fi
ko_fi: # futuresolutionsdev # Not supported yet

# Buy Me a Coffee
custom: # ['https://www.buymeacoffee.com/futuresolutionsdev'] # Not supported yet

# Custom links
custom:
- https://futuresolutionsdev.com/#contact
- https://futuresolutionsdev.com/contact

Current contact methods:
- Email: licensing@futuresolutionsdev.com
Expand Down
52 changes: 34 additions & 18 deletions .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Build & Test

on:
push:
branches: [main, develop]
branches: [main, master, develop, "version/**"]
pull_request:
branches: [main, develop]
branches: [main, master, develop, "version/**"]

jobs:
build-and-test:
Expand All @@ -17,19 +17,37 @@ jobs:

services:
mariadb:
image: mariadb:10.6
image: mariadb:11
env:
MYSQL_ROOT_PASSWORD: root_password
MYSQL_DATABASE: deploy_center_test
MYSQL_USER: test_user
MYSQL_PASSWORD: test_password
MARIADB_ROOT_PASSWORD: root_password
# User + password MUST match `.env.test` because setupTestDb.ts
# loads .env.test with `override: true`, so the env block on the
# test step CANNOT override these. Keep all three in sync if any
# change. (Mismatch causes every integration suite to skip
# silently and the coverage gate to drop below threshold.)
MARIADB_DATABASE: deploy_center_test
MARIADB_USER: deploycenter
MARIADB_PASSWORD: deploycenter_test_password
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping"
--health-cmd="healthcheck.sh --connect --innodb_initialized"
--health-interval=10s
--health-timeout=5s
--health-retries=3
--health-retries=10
# v3.0 F-001 requires Redis for the BullMQ-backed queue. Without
# this service every integration test that touches the queue
# auto-skips via `if (!redisUp) return`, defeating the 40 % coverage
# gate.
redis:
image: redis:7-alpine
ports:
- 6379:6379
options: >-
--health-cmd="redis-cli ping"
--health-interval=10s
--health-timeout=5s
--health-retries=10

steps:
- name: 📥 Checkout code
Expand All @@ -50,16 +68,14 @@ jobs:
- name: 🧪 Run tests
run: npm test
env:
# NOTE: setupTestDb.ts loads .env.test with `override: true`, so
# the values here are mostly ignored for keys that .env.test also
# sets (DB_*, REDIS_*, JWT_*, ENCRYPTION_KEY, NODE_ENV). The
# MariaDB + Redis services above are configured to match what
# .env.test expects. We keep NODE_ENV here as a defensive default
# for the moment before .env.test loads (covers code that runs at
# import time).
NODE_ENV: test
DB_HOST: 127.0.0.1
DB_PORT: 3306
DB_NAME: deploy_center_test
DB_USER: test_user
DB_PASSWORD: test_password
DB_DIALECT: mariadb
JWT_SECRET: test-jwt-secret-for-ci-only
JWT_REFRESH_SECRET: test-refresh-secret-for-ci-only
ENCRYPTION_KEY: 0123456789abcdef0123456789abcdef

- name: 📊 Upload coverage reports
uses: codecov/codecov-action@v4
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Lint

on:
push:
branches: [main, develop]
branches: [main, master, develop, "version/**"]
pull_request:
branches: [main, develop]
branches: [main, master, develop, "version/**"]

jobs:
eslint:
Expand Down
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ out/
.env.*.local
.env.production
.env.development
.env.test
# NOTE: .env.test is intentionally COMMITTED — it's a test FIXTURE file
# with deterministic dummy values (all-zero encryption key, throwaway JWT
# secrets, test schema name) so CI can load it via jest.env.setup.ts.
# Not a real-credentials file.

# Logs
logs/
Expand Down Expand Up @@ -147,3 +150,4 @@ DEPLOYMENT_LOGS_OPTIMIZATION.md
RSYNC_PERMISSIONS_FIX.md
HUSKY_PRODUCTION_FIX.md
PRODUCTION_DEPLOYMENT_FIXES.md
docs/spec-kit
Loading
Loading