feat: setup project for docker deploy

This commit is contained in:
2025-07-23 22:00:35 +00:00
parent 98369ef531
commit 715bc90340
12 changed files with 139 additions and 10 deletions

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
# Environments
.env
.env.*.local
.venv
env/
venv/

10
backend/Dockerfile Normal file
View File

@@ -0,0 +1,10 @@
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

View File

@@ -1,5 +1,6 @@
from pydantic_settings import BaseSettings
from typing import Literal, List
import os
class Settings(BaseSettings):
database_url: str
@@ -9,12 +10,11 @@ class Settings(BaseSettings):
allow_origins: str
class Config:
env_file = ".env"
env_file = os.path.join(os.path.dirname(__file__), ".env")
env_file_encoding = "utf-8"
def get_allow_origins(self) -> List[str]:
return [origin.strip() for origin in self.allow_origins.split(",")]
# Singleton instance of settings
settings = Settings()
settings = Settings()

View File

@@ -19,9 +19,8 @@ app.add_middleware(
async def startup():
await database.connect()
# only create missing tables in dev environment
if settings.environment == "dev":
Base.metadata.create_all(bind=engine)
# create tables if they don't exist
Base.metadata.create_all(bind=engine)
@app.on_event("shutdown")
async def shutdown():

View File

@@ -22,7 +22,7 @@ async def redirect_to_original_url(shortcode: str):
raise HTTPException(status_code=404, detail=str(e)) from e
@router.post("/")
@router.post("/api/shorten")
async def create_url_shortcode(url_payload: UrlPayload):
url = str(url_payload.url)

View File

@@ -1,4 +1,3 @@
# Frontend environment variables #
REACT_APP_API_BASE_URL=http://localhost:8000
REACT_APP_BASE_URL=http://localhost:8000

8
frontend/Dockerfile Normal file
View File

@@ -0,0 +1,8 @@
FROM node:20-alpine AS builder
WORKDIR /app
COPY . .
RUN npm install && npm run build
FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html

View File

@@ -5,7 +5,7 @@ const baseUrl = process.env.REACT_APP_API_BASE_URL
export async function shortenUrlApi(
payload: ShortenUrlRequest
): Promise<ShortenUrlResponse> {
const response = await fetch(`${baseUrl}/`, {
const response = await fetch(`${baseUrl}/api/shorten`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),

View File

@@ -6,7 +6,7 @@ import { UrlState } from './types';
export const shortenUrl = createAsyncThunk(
'url/shortenUrl',
async (url: string) => {
const baseUrl = process.env.REACT_APP_BASE_URL
const baseUrl = process.env.REACT_APP_API_BASE_URL
const data = await shortenUrlApi({ url });
return `${baseUrl}/${data.shortcode}`;
}

112
generate_configs.py Normal file
View File

@@ -0,0 +1,112 @@
import secrets
import random
from pathlib import Path
# === Prompt user input === #
react_api_url = input("🔧 Enter the REACT_APP_API_BASE_URL (e.g. https://minxa.wtf): ").strip()
allowed_origins = input("🔒 Enter allowed CORS origins for the backend comma-separated (e.g. https://minxa.lol,https://minxo.lol): ").strip()
# Ask for environment and validate input
valid_envs = ["dev", "stage", "prod"]
while True:
environment = input("🌎 Enter environment (dev, stage, prod): ").strip().lower()
if environment in valid_envs:
break
print("❌ Invalid input. Please enter one of: dev, stage, prod")
# === Backend values === #
db_user = "minxa"
db_password = secrets.token_urlsafe(16)
db_name = "minxadb"
db_host = "minxa-db"
db_port = 5432
hashids_salt = secrets.token_urlsafe(32)
alphabet = list("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789")
random.shuffle(alphabet)
encoder_alphabet = "".join(alphabet)
# === Generate backend .env === #
backend_env = f"""# Auto-generated backend .env
DATABASE_URL=postgresql+asyncpg://{db_user}:{db_password}@{db_host}:{db_port}/{db_name}
ENVIRONMENT={environment}
HASHIDS_SALT={hashids_salt}
ENCODER_ALPHABET={encoder_alphabet}
ALLOWED_ORIGINS={allowed_origins}
"""
backend_env_path = Path("backend/.env")
backend_env_path.parent.mkdir(parents=True, exist_ok=True)
backend_env_path.write_text(backend_env)
print(f"✅ backend/.env written")
# === Generate frontend .env === #
frontend_env = f"""# Auto-generated frontend .env
REACT_APP_API_BASE_URL={react_api_url}
"""
frontend_env_path = Path("frontend/.env")
frontend_env_path.parent.mkdir(parents=True, exist_ok=True)
frontend_env_path.write_text(frontend_env)
print(f"✅ frontend/.env written")
# === Generate docker-compose.generated.yml === #
compose_yml = f"""version: "3.9"
services:
minxa-db:
image: postgres:16
environment:
POSTGRES_USER: {db_user}
POSTGRES_PASSWORD: {db_password}
POSTGRES_DB: {db_name}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "{db_user}"]
interval: 5s
timeout: 5s
retries: 5
networks:
- appnet
minxa-backend:
build:
context: ./backend
env_file:
- ./backend/.env
depends_on:
minxa-db:
condition: service_healthy
ports:
- "8000:8000"
networks:
- appnet
minxa-frontend:
build:
context: ./frontend
env_file:
- ./frontend/.env
depends_on:
- minxa-backend
ports:
- "3000:80"
networks:
- appnet
volumes:
postgres_data:
networks:
appnet:
"""
compose_path = Path("docker-compose.generated.yml")
compose_path.write_text(compose_yml)
print(f"✅ docker-compose.generated.yml written")
print("\n🎉 All files generated! Run your stack with:\n")
print("docker compose -f docker-compose.generated.yml up --build")