-
Notifications
You must be signed in to change notification settings - Fork 0
Setup Unified Dockerfile and staging environment with updated URLs #68
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
598c157
ce213ee
01b2074
cef2e6c
e22dcb9
11af3ea
4db2dd0
bbee838
1c0a69f
237f4bd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,38 @@ | ||
| # Dependencies | ||
| **/node_modules | ||
| **/.venv | ||
| **/.git | ||
| **/__pycache__ | ||
|
|
||
| # Build outputs | ||
| **/.next | ||
| **/__pycache__ | ||
| **/*.pyc | ||
| **/*.pyo | ||
|
|
||
| # Version control | ||
| **/.git | ||
| **/.gitignore | ||
|
|
||
| # Test artifacts | ||
| **/.pytest_cache | ||
| **/tests | ||
| **/test_*.py | ||
| **/*_test.py | ||
| **/.coverage | ||
|
|
||
| # Dev-only data | ||
| **/chroma_db | ||
| **/uploads | ||
| **/sql_app.db | ||
|
|
||
| # Secrets — pass at runtime via env_file or environment | ||
| **/.env | ||
| **/.env.* | ||
|
|
||
| # Misc | ||
| **/bloats | ||
| **/.vscode | ||
| **/.claude | ||
| **/postman_collection.json | ||
| **/lint_results*.txt | ||
| **/test_output.txt | ||
| **/test.txt | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| # ============================================================================= | ||
| # FRONTEND — build stages | ||
| # ============================================================================= | ||
|
|
||
| FROM node:20-alpine AS frontend-deps | ||
| WORKDIR /app | ||
| COPY frontend/package.json frontend/package-lock.json ./ | ||
| RUN npm ci | ||
|
|
||
| FROM frontend-deps AS frontend-builder | ||
| WORKDIR /app | ||
| COPY frontend/ . | ||
|
|
||
| ARG NEXT_PUBLIC_ENVIRONMENT | ||
| ARG NEXT_PUBLIC_API_URL | ||
| ARG NEXT_PUBLIC_BACKEND_URL_PROD | ||
| ARG NEXT_PUBLIC_FRONTEND_URL_PROD | ||
| ARG NEXT_PUBLIC_BACKEND_URL_STAGING | ||
| ARG NEXT_PUBLIC_FRONTEND_URL_STAGING | ||
| ARG NEXT_PUBLIC_BACKEND_URL_DEV | ||
| ARG NEXT_PUBLIC_FRONTEND_URL_DEV | ||
|
|
||
| ENV NEXT_PUBLIC_ENVIRONMENT=$NEXT_PUBLIC_ENVIRONMENT \ | ||
| NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL \ | ||
| NEXT_PUBLIC_BACKEND_URL_PROD=$NEXT_PUBLIC_BACKEND_URL_PROD \ | ||
| NEXT_PUBLIC_FRONTEND_URL_PROD=$NEXT_PUBLIC_FRONTEND_URL_PROD \ | ||
| NEXT_PUBLIC_BACKEND_URL_STAGING=$NEXT_PUBLIC_BACKEND_URL_STAGING \ | ||
| NEXT_PUBLIC_FRONTEND_URL_STAGING=$NEXT_PUBLIC_FRONTEND_URL_STAGING \ | ||
| NEXT_PUBLIC_BACKEND_URL_DEV=$NEXT_PUBLIC_BACKEND_URL_DEV \ | ||
| NEXT_PUBLIC_FRONTEND_URL_DEV=$NEXT_PUBLIC_FRONTEND_URL_DEV | ||
|
|
||
| RUN npm run build | ||
|
|
||
| FROM node:20-alpine AS frontend | ||
| WORKDIR /app | ||
|
|
||
| RUN addgroup --system --gid 1001 nodejs && \ | ||
| adduser --system --uid 1001 nextjs | ||
|
|
||
| COPY --from=frontend-builder /app/public ./public | ||
| COPY --from=frontend-builder /app/.next/standalone ./ | ||
| COPY --from=frontend-builder /app/.next/static ./.next/static | ||
|
|
||
| RUN chown -R nextjs:nodejs /app | ||
| USER nextjs | ||
|
|
||
| ENV NODE_ENV=production \ | ||
| PORT=3000 \ | ||
| HOSTNAME="0.0.0.0" | ||
|
|
||
| EXPOSE 3000 | ||
|
|
||
| HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \ | ||
| CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1 | ||
|
|
||
| CMD ["node", "server.js"] | ||
|
|
||
|
|
||
| # ============================================================================= | ||
| # BACKEND — build stages | ||
| # ============================================================================= | ||
|
|
||
| FROM python:3.10-slim AS backend-builder | ||
| WORKDIR /app | ||
|
|
||
| RUN pip install --no-cache-dir uv | ||
| ENV UV_HTTP_TIMEOUT=300 | ||
|
|
||
| COPY backend/pyproject.toml backend/uv.lock ./ | ||
| RUN uv sync --frozen --no-cache | ||
|
|
||
| FROM python:3.10-slim AS backend | ||
| WORKDIR /app | ||
|
|
||
| RUN pip install --no-cache-dir uv | ||
|
|
||
| COPY --from=backend-builder /app/.venv /app/.venv | ||
|
Comment on lines
+75
to
+77
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Installing |
||
|
|
||
| ENV PATH="/app/.venv/bin:$PATH" \ | ||
| PYTHONUNBUFFERED=1 \ | ||
| PYTHONDONTWRITEBYTECODE=1 | ||
|
|
||
| COPY backend/ . | ||
|
|
||
| RUN adduser --disabled-password --gecos '' appuser && \ | ||
| chown -R appuser:appuser /app && \ | ||
| chmod +x /app/start.sh | ||
| USER appuser | ||
|
|
||
| EXPOSE 8000 | ||
|
|
||
| HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \ | ||
| CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')" || exit 1 | ||
|
|
||
| CMD ["/app/start.sh"] | ||
This file was deleted.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -85,24 +85,37 @@ class LocalConfig(BaseConfig): | |||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||
| CORS_ALLOW_ORIGIN_REGEX: str = ".*" | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| class StagingConfig(BaseConfig): | ||||||||||||||||||||||||||||||
| FRONTEND_URI: str = "https://taimakoai.onrender.com" | ||||||||||||||||||||||||||||||
| FRONTEND_REDIRECT_URI: str = "https://taimakoai.onrender.com/auth/callback" | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| CORS_ORIGINS: List[str] = [ | ||||||||||||||||||||||||||||||
| "https://taimakoai.onrender.com", | ||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||
|
Comment on lines
+88
to
+94
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hardcoding the staging frontend and backend URLs directly in
Suggested change
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ALLOWED_HOSTS: List[str] = [ | ||||||||||||||||||||||||||||||
| "taimako.onrender.com", | ||||||||||||||||||||||||||||||
| "taimakoai.onrender.com", | ||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||
|
Comment on lines
+96
to
+99
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The ALLOWED_HOSTS: List[str] = [
"taimako.onrender.com",
] |
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| class ProductionConfig(BaseConfig): | ||||||||||||||||||||||||||||||
| FRONTEND_URI: str = os.getenv("FRONTEND_LIVE_URI", "https://taimako.dubem.xyz") | ||||||||||||||||||||||||||||||
| FRONTEND_REDIRECT_URI: str = f"{os.getenv('FRONTEND_LIVE_URI', 'https://taimako.dubem.xyz')}/auth/callback" | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Paystack | ||||||||||||||||||||||||||||||
| PAYSTACK_SECRET_KEY: str = os.getenv("PAYSTACK_LIVE_SECRET_KEY", "") | ||||||||||||||||||||||||||||||
| PAYSTACK_PUBLIC_KEY: str = os.getenv("PAYSTACK_LIVE_PUBLIC_KEY", "") | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| CORS_ORIGINS: List[str] = [ | ||||||||||||||||||||||||||||||
| "https://taimako.dubem.xyz", | ||||||||||||||||||||||||||||||
| "https://www.taimako.dubem.xyz" | ||||||||||||||||||||||||||||||
| "https://taimako.dubem.xyz", | ||||||||||||||||||||||||||||||
| "https://www.taimako.dubem.xyz", | ||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||
| CORS_ALLOW_ORIGIN_REGEX: str = r"https?://.*" | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ALLOWED_HOSTS: List[str] = [ | ||||||||||||||||||||||||||||||
| "taimako.dubem.xyz", | ||||||||||||||||||||||||||||||
| "taimako.dubem.xyz", | ||||||||||||||||||||||||||||||
| "api.taimako.dubem.xyz", | ||||||||||||||||||||||||||||||
| "*.taimako.dubem.xyz" | ||||||||||||||||||||||||||||||
| "*.taimako.dubem.xyz", | ||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||
| # USE_HTTPS_REDIRECT: bool = True # Uncomment if handling SSL termination directly | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
@@ -112,8 +125,7 @@ def get_settings(): | |||||||||||||||||||||||||||||
| if env == "production": | ||||||||||||||||||||||||||||||
| return ProductionConfig() | ||||||||||||||||||||||||||||||
| elif env == "staging": | ||||||||||||||||||||||||||||||
| # Treating staging similar to prod for now, or can be its own class | ||||||||||||||||||||||||||||||
| return ProductionConfig() | ||||||||||||||||||||||||||||||
| return StagingConfig() | ||||||||||||||||||||||||||||||
| return LocalConfig() | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| settings = get_settings() | ||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -10,15 +10,15 @@ | |||||||||||
| POSTGRES_PORT = os.getenv("POSTGRES_PORT", "5432") | ||||||||||||
| POSTGRES_DB = os.getenv("POSTGRES_DB") | ||||||||||||
|
|
||||||||||||
| # Build PostgreSQL URL if all required parts are present | ||||||||||||
| if all([POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB]): | ||||||||||||
| # DATABASE_URL takes priority — cloud platforms (Render, Railway, Heroku) provide this directly | ||||||||||||
| if os.getenv("DATABASE_URL"): | ||||||||||||
| SQLALCHEMY_DATABASE_URL = os.getenv("DATABASE_URL") | ||||||||||||
|
Comment on lines
+14
to
+15
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Many cloud platforms (such as Render, Heroku, and Railway) provide the database connection string via the
Suggested change
|
||||||||||||
| # Build from individual vars when DATABASE_URL is absent (docker-compose local dev) | ||||||||||||
| elif all([POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB]): | ||||||||||||
| SQLALCHEMY_DATABASE_URL = ( | ||||||||||||
| f"postgresql://{POSTGRES_USER}:{POSTGRES_PASSWORD}@{POSTGRES_HOST}:{POSTGRES_PORT}/{POSTGRES_DB}" | ||||||||||||
| ) | ||||||||||||
| # Allow direct DATABASE_URL override (common on Heroku, Render, Railway, etc.) | ||||||||||||
| elif os.getenv("DATABASE_URL"): | ||||||||||||
| SQLALCHEMY_DATABASE_URL = os.getenv("DATABASE_URL") | ||||||||||||
| # Final fallback: SQLite for quick local development, cause why not, eh? | ||||||||||||
| # Final fallback: SQLite for quick local development | ||||||||||||
| else: | ||||||||||||
| DATABASE_PATH = os.getenv("DATABASE_PATH", "./sql_app.db") | ||||||||||||
| SQLALCHEMY_DATABASE_URL = f"sqlite:///{DATABASE_PATH}" | ||||||||||||
|
|
||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
bloats/directory is added to.dockerignore, which correctly prevents it from being copied into the Docker images. However, committing temporary scratchpad scripts, draft proposals, and sensitive files (such assecurity_incident_report.mdandemergent_ventures_proposal.md) to the Git repository is a bad practice. It increases repository bloat and risks leaking sensitive information. It is highly recommended to remove thebloats/directory from version control entirely and add it to.gitignore.