Skip to content

Commit b456f19

Browse files
authored
Merge pull request #4 from piewared/feature/MAKE_POSTGRES_OPTIONAL
feat: comprehensive CLI refactoring and infrastructure improvements
2 parents cdede4a + b730f0b commit b456f19

135 files changed

Lines changed: 12117 additions & 2144 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ PRODUCTION_BASE_URL=http://localhost:8000
88
DEVELOPMENT_BASE_URL=http://localhost:8000
99

1010
### ----------------- Database Settings ----------------- ###
11-
PRODUCTION_DATABASE_URL=postgresql://appuser@postgres:5432/appdb?sslmode=require
11+
PRODUCTION_DATABASE_URL=postgresql://postgres:5432?sslmode=require
1212
DEVELOPMENT_DATABASE_URL=postgresql://appuser:devpass@localhost:5433/appdb
1313

14+
PG_SUPERUSER=postgres
15+
PG_DB=postgres
1416
APP_DB=appdb
1517
APP_DB_OWNER=appowner
1618
APP_DB_USER=appuser
@@ -27,6 +29,7 @@ DEVELOPMENT_TEMPORAL_URL=localhost:7234
2729
PRODUCTION_TEMPORAL_URL=temporal:7233
2830

2931
TEMPORAL_DB_USER=temporaluser
32+
TEMPORAL_DB_OWNER=temporalowner
3033
TEMPORAL_DB=temporal
3134
TEMPORAL_VIS_DB=temporal_visibility
3235
### ----------------- OIDC Configuration ----------------- ###

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
- name: Start dev environment services
3838
run: |
3939
# Deploy dev environment (blocks until services are ready)
40-
uv run api-forge-cli deploy up --no-start-server dev
40+
uv run api-forge-cli dev up --no-start-server
4141
4242
- name: Run tests
4343
run: |
@@ -70,7 +70,7 @@ jobs:
7070
- name: Stop dev environment
7171
if: always()
7272
run: |
73-
uv run api-forge-cli deploy down dev
73+
uv run api-forge-cli dev down
7474
7575
- name: Upload coverage to Codecov
7676
uses: codecov/codecov-action@v3

README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Build your next SaaS backend, internal API gateway, or microservice with a pre-c
1717
* **FastAPI** – high-performance Python web framework
1818
* **SQLAlchemy** and **SQLModel** – ORM and typed models for data persistence
1919
* **Pydantic** – data validation and type safety
20-
* **PostgreSQL** – production-ready relational database
20+
* **PostgreSQL** – production-ready relational database (bundled or external)
2121
* **Redis** – caching, sessions, and rate limiting
2222
* **Temporal** – background workflows and reliable task orchestration
2323
* **Docker** – containerized development and deployment
@@ -195,6 +195,13 @@ api-forge-cli deploy down prod --volumes
195195
api-forge-cli deploy up k8s
196196
api-forge-cli deploy status k8s
197197
api-forge-cli deploy down k8s
198+
199+
# Database management (Kubernetes)
200+
api-forge-cli k8s db init # Initialize database with roles/schema
201+
api-forge-cli k8s db verify # Verify database setup and credentials
202+
api-forge-cli k8s db sync # Sync local password files to database
203+
api-forge-cli k8s db status # Show database health metrics
204+
api-forge-cli k8s db backup # Create database backup
198205
```
199206

200207
### Entity scaffolding

alembic.ini

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Alembic configuration file for database migrations
2+
3+
[alembic]
4+
# Path to migration scripts
5+
script_location = migrations
6+
7+
# Template used to generate migration files
8+
file_template = %%(year)d%%(month).2d%%(day).2d_%%(hour).2d%%(minute).2d_%%(slug)s
9+
10+
# Timezone for migration timestamps
11+
timezone = UTC
12+
13+
# Truncate slug to 40 characters
14+
truncate_slug_length = 40
15+
16+
# Revision environment configuration
17+
# This is used by env.py to configure the migration environment
18+
[alembic:env]
19+
# sqlalchemy.url is set dynamically in env.py from DATABASE_URL
20+
21+
# Logging configuration
22+
[loggers]
23+
keys = root,sqlalchemy,alembic
24+
25+
[handlers]
26+
keys = console
27+
28+
[formatters]
29+
keys = generic
30+
31+
[logger_root]
32+
level = WARN
33+
handlers = console
34+
qualname =
35+
36+
[logger_sqlalchemy]
37+
level = WARN
38+
handlers =
39+
qualname = sqlalchemy.engine
40+
41+
[logger_alembic]
42+
level = INFO
43+
handlers =
44+
qualname = alembic
45+
46+
[handler_console]
47+
class = StreamHandler
48+
args = (sys.stderr,)
49+
level = NOTSET
50+
formatter = generic
51+
52+
[formatter_generic]
53+
format = %(levelname)-5.5s [%(name)s] %(message)s
54+
datefmt = %H:%M:%S

config.yaml

Lines changed: 86 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,40 @@ config:
55
enabled: true
66
per_endpoint: true
77
per_method: true
8-
98
database:
109
url: "${DATABASE_URL:-postgresql+asyncpg://appuser@postgres:5432/appdb}"
10+
pg_superuser: "${PG_SUPERUSER:-postgres}"
11+
pg_db: "${PG_DB:-postgres}"
1112
app_db: "${APP_DB:-appdb}"
1213
owner_user: "${APP_DB_OWNER:-appowner}"
1314
user: "${APP_DB_USER:-appuser}"
1415
ro_user: "${APP_DB_RO_USER:-backupuser}"
16+
temporal_user: "${TEMPORAL_DB_USER:-temporaluser}"
17+
temporal_owner: "${TEMPORAL_DB_OWNER:-temporalowner}"
1518
pool_size: 20
1619
max_overflow: 10
1720
pool_timeout: 30
1821
pool_recycle: 1800
1922
environment_mode: "${APP_ENVIRONMENT:-development}"
20-
password_file_path: "${DATABASE_PASSWORD_FILE_PATH:-/run/secrets/postgres_app_user_pw}"
21-
password_env_var: "${DATABASE_PASSWORD_ENV_VAR:-POSTGRES_APP_USER_PW}"
23+
bundled_postgres:
24+
enabled: true
25+
password_file_path: "${DATABASE_PASSWORD_FILE_PATH:-/run/secrets/postgres_app_user_pw}"
26+
password_env_var: "${DATABASE_PASSWORD_ENV_VAR:-POSTGRES_APP_USER_PW}"
2227
temporal:
2328
enabled: true
2429
url: "${TEMPORAL_URL:-temporal:7233}"
25-
namespace: "default"
26-
task_queue: "default"
30+
db_user: "${TEMPORAL_DB_USER:-temporaluser}"
31+
db_owner: "${TEMPORAL_DB_OWNER:-temporalowner}"
32+
namespace: default
33+
task_queue: default
2734
workflows:
28-
execution_timeout_s: 86400 # 24 hours. Maximum time a workflow execution can run. Includes retries and continue as new.
29-
run_timeout_s: 7200 # 2 hours. Maximum time for a single run of a workflow (before retries).
30-
task_timeout_s: 10 # 10 seconds. Maximum time to complete a workflow task.
35+
execution_timeout_s: 86400
36+
run_timeout_s: 7200
37+
task_timeout_s: 10
3138
activities:
32-
start_to_close_timeout_s: 1200 # 20 minutes. Limits the maximum execution time of a single execution of an activity.
33-
schedule_to_close_timeout_s: 3600 # 1 hour. Limits the total time allotted for an activity from scheduling to completion. Includes retries.
34-
heartbeat_timeout_s: 300 # 5 minutes. If an activity does not report progress within this time, it is considered failed.
39+
start_to_close_timeout_s: 1200
40+
schedule_to_close_timeout_s: 3600
41+
heartbeat_timeout_s: 300
3542
retry:
3643
maximum_attempts: 5
3744
initial_interval_seconds: 5
@@ -47,11 +54,13 @@ config:
4754
max_workflow_tasks_per_second: 100
4855
max_concurrent_workflow_tasks: 100
4956
sticky_queue_schedule_to_start_timeout_ms: 10000
50-
worker_build_id: "api-worker-1"
57+
worker_build_id: api-worker-1
5158
redis:
5259
enabled: true
5360
url: "${REDIS_URL:-redis://localhost:6379}"
5461
password: "${REDIS_PASSWORD:-}"
62+
password_file_path: "${REDIS_PASSWORD_FILE_PATH:-/app/keys/redis_password}"
63+
password_env_var: "${REDIS_PASSWORD_ENV_VAR:-REDIS_PASSWORD}"
5564
max_connections: 10
5665
decode_responses: true
5766
socket_timeout: 5
@@ -61,53 +70,53 @@ config:
6170
google:
6271
enabled: true
6372
dev_only: false
64-
authorization_endpoint: "https://accounts.google.com/o/oauth2/v2/auth"
65-
token_endpoint: "https://oauth2.googleapis.com/token"
66-
userinfo_endpoint: "https://openidconnect.googleapis.com/v1/userinfo"
67-
end_session_endpoint: "https://accounts.google.com/logout"
68-
issuer: "https://accounts.google.com"
69-
jwks_uri: "https://www.googleapis.com/oauth2/v3/certs"
73+
authorization_endpoint: https://accounts.google.com/o/oauth2/v2/auth
74+
token_endpoint: https://oauth2.googleapis.com/token
75+
userinfo_endpoint: https://openidconnect.googleapis.com/v1/userinfo
76+
end_session_endpoint: https://accounts.google.com/logout
77+
issuer: https://accounts.google.com
78+
jwks_uri: https://www.googleapis.com/oauth2/v3/certs
7079
scopes:
71-
- openid
72-
- profile
73-
- email
80+
- openid
81+
- profile
82+
- email
7483
client_id: your-google-client-id
7584
client_secret: "${OIDC_GOOGLE_CLIENT_SECRET}"
7685
redirect_uri: "${OIDC_GOOGLE_REDIRECT_URI:-http://localhost:8000/auth/google/callback}"
7786
microsoft:
7887
enabled: true
7988
dev_only: false
80-
authorization_endpoint: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize"
81-
token_endpoint: "https://login.microsoftonline.com/common/oauth2/v2.0/token"
82-
userinfo_endpoint: "https://graph.microsoft.com/oidc/userinfo"
83-
end_session_endpoint: "https://login.microsoftonline.com/common/oauth2/v2.0/logout"
84-
issuer: "https://login.microsoftonline.com"
85-
jwks_uri: "https://login.microsoftonline.com/common/discovery/v2.0/keys"
89+
authorization_endpoint: https://login.microsoftonline.com/common/oauth2/v2.0/authorize
90+
token_endpoint: https://login.microsoftonline.com/common/oauth2/v2.0/token
91+
userinfo_endpoint: https://graph.microsoft.com/oidc/userinfo
92+
end_session_endpoint: https://login.microsoftonline.com/common/oauth2/v2.0/logout
93+
issuer: https://login.microsoftonline.com
94+
jwks_uri: https://login.microsoftonline.com/common/discovery/v2.0/keys
8695
scopes:
87-
- openid
88-
- profile
89-
- email
96+
- openid
97+
- profile
98+
- email
9099
client_id: your-microsoft-client-id
91100
client_secret: "${OIDC_MICROSOFT_CLIENT_SECRET}"
92101
redirect_uri: "${OIDC_MICROSOFT_REDIRECT_URI:-http://localhost:8000/auth/microsoft/callback}"
93102
keycloak:
94103
enabled: true
95104
dev_only: true
96-
issuer: "http://localhost:8080/realms/test-realm"
105+
issuer: http://localhost:8080/realms/test-realm
97106
openid_configuration_endpoint: "${OIDC_KEYCLOAK_ISSUER:-http://localhost:8080/realms/test-realm}/.well-known/openid-configuration"
98107
client_id: test-client
99108
client_secret: test-client-secret
100109
scopes:
101-
- openid
102-
- profile
103-
- email
110+
- openid
111+
- profile
112+
- email
104113
redirect_uri: "${OIDC_KEYCLOAK_REDIRECT_URI:-http://localhost:8000/auth/web/callback}"
105114
jwks_uri: "${OIDC_KEYCLOAK_JWKS_URI:-http://localhost:8080/realms/test-realm/protocol/openid-connect/certs}"
106115
end_session_endpoint: "${OIDC_KEYCLOAK_END_SESSION_ENDPOINT:-http://localhost:8080/realms/test-realm/protocol/openid-connect/logout}"
107116
userinfo_endpoint: "${OIDC_KEYCLOAK_USERINFO_ENDPOINT:-http://localhost:8080/realms/test-realm/protocol/openid-connect/userinfo}"
108117
authorization_endpoint: "${OIDC_KEYCLOAK_AUTHORIZATION_ENDPOINT:-http://localhost:8080/realms/test-realm/protocol/openid-connect/auth}"
109118
token_endpoint: "${OIDC_KEYCLOAK_TOKEN_ENDPOINT:-http://localhost:8080/realms/test-realm/protocol/openid-connect/token}"
110-
default_provider: "keycloak"
119+
default_provider: keycloak
111120
global_redirect_uri: "${OIDC_REDIRECT_URI:-http://localhost:8000/auth/callback}"
112121
allowed_redirect_hosts: []
113122
allowed_audiences: []
@@ -116,74 +125,67 @@ config:
116125
persist_in_session_store: false
117126
max_session_lifetime_seconds: 86400
118127
jwt:
119-
# JWT Validation Settings
120-
allowed_algorithms:
121-
- "RS256"
122-
- "RS512"
123-
- "ES256"
124-
- "ES384"
125-
- "HS256" # Only if you have shared secrets
126-
# Audiences that your API accepts (who the tokens are intended for)
127-
gen_issuer: "${BASE_URL:-my-api-issuer}" # Issuer name to use when generating tokens
128+
allowed_algorithms:
129+
- RS256
130+
- RS512
131+
- ES256
132+
- ES384
133+
- HS256
134+
gen_issuer: "${BASE_URL:-my-api-issuer}"
128135
audiences:
129-
- "${JWT_AUDIENCE:-api://default}"
130-
- "${JWT_AUDIENCE_SECONDARY:-http://localhost:8000}"
131-
# Clock skew tolerance in seconds (accounts for time differences between servers)
136+
- "${JWT_AUDIENCE:-api://default}"
137+
- "${JWT_AUDIENCE_SECONDARY:-http://localhost:8000}"
132138
clock_skew: 60
133-
# Token validation settings
134139
verify_signature: true
135-
verify_exp: true # Verify expiration
136-
verify_nbf: true # Verify not-before
137-
verify_iat: true # Verify issued-at
140+
verify_exp: true
141+
verify_nbf: true
142+
verify_iat: true
138143
require_exp: true
139144
require_iat: true
140-
# Claim mappings (how to extract user info from JWT tokens)
141145
claims:
142-
user_id: "${JWT_CLAIM_USER_ID:-sub}" # Usually 'sub' (subject)
143-
email: "${JWT_CLAIM_EMAIL:-email}" # Email claim
144-
roles: "${JWT_CLAIM_ROLES:-roles}" # Roles/permissions
145-
groups: "${JWT_CLAIM_GROUPS:-groups}" # User groups
146-
scope: "${JWT_CLAIM_SCOPE:-scope}" # OAuth scopes
147-
name: "${JWT_CLAIM_NAME:-name}" # User's full name
146+
user_id: "${JWT_CLAIM_USER_ID:-sub}"
147+
email: "${JWT_CLAIM_EMAIL:-email}"
148+
roles: "${JWT_CLAIM_ROLES:-roles}"
149+
groups: "${JWT_CLAIM_GROUPS:-groups}"
150+
scope: "${JWT_CLAIM_SCOPE:-scope}"
151+
name: "${JWT_CLAIM_NAME:-name}"
148152
preferred_username: "${JWT_CLAIM_USERNAME:-preferred_username}"
149153
jwks_cache_ttl_seconds: 3600
150154
jwks_cache_max_entries: 16
151155
logging:
152156
level: "${LOG_LEVEL:-DEBUG}"
153-
format: "${LOG_FORMAT:-json}" # Options: "json", "plain"
157+
format: "${LOG_FORMAT:-json}"
154158
file: "${LOG_FILE:-logs/app.log}"
155159
max_size_mb: 5
156160
backup_count: 5
157161
app:
158-
environment: "${APP_ENVIRONMENT:-development}" # Options: "development", "testing", "production"
162+
environment: "${APP_ENVIRONMENT:-development}"
159163
host: "${APP_HOST:-localhost}"
160164
port: "${APP_PORT:-8000}"
161-
session_max_age: ${SESSION_MAX_AGE:-3600} # Session max age in seconds
162-
session_signing_secret: "${SESSION_SIGNING_SECRET}" # Secret for signing session JWTs
163-
csrf_signing_secret: "${CSRF_SIGNING_SECRET}" # Secret for signing CSRF tokens
165+
session_max_age: "${SESSION_MAX_AGE:-3600}"
166+
session_signing_secret: "${SESSION_SIGNING_SECRET}"
167+
csrf_signing_secret: "${CSRF_SIGNING_SECRET}"
164168
cors:
165169
origins:
166-
- "${CLIENT_ORIGIN:-http://localhost:3000}"
170+
- "${CLIENT_ORIGIN:-http://localhost:3000}"
167171
allow_credentials: true
168172
allow_methods:
169-
- GET
170-
- POST
171-
- PUT
172-
- DELETE
173-
- OPTIONS
173+
- GET
174+
- POST
175+
- PUT
176+
- DELETE
177+
- OPTIONS
174178
allow_headers:
175-
- Authorization
176-
- Content-Type
177-
- X-Requested-With
178-
- Accept
179-
- Origin
180-
- User-Agent
181-
- DNT
182-
- Cache-Control
183-
- X-Mx-ReqToken
184-
- Keep-Alive
185-
- X-Requested-With
186-
- If-Modified-Since
187-
- X-CSRF-Token
188-
189-
179+
- Authorization
180+
- Content-Type
181+
- X-Requested-With
182+
- Accept
183+
- Origin
184+
- User-Agent
185+
- DNT
186+
- Cache-Control
187+
- X-Mx-ReqToken
188+
- Keep-Alive
189+
- X-Requested-With
190+
- If-Modified-Since
191+
- X-CSRF-Token

0 commit comments

Comments
 (0)