Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ yarn-error.log*

# Environment
.env
.env.prod
.env.local
.env.development.local
.env.test.local
Expand Down Expand Up @@ -78,3 +79,4 @@ cypress/screenshots/
cypress/reports/
cypress/results/
cypress/.cache/
frontend/public/logo-new.png
2 changes: 1 addition & 1 deletion backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
FROM base AS production

# Create non-root user
RUN useradd -m -u 1000 appuser && \
RUN adduser -D -u 1000 appuser && \
mkdir -p /app/media /app/staticfiles && \
chown -R appuser:appuser /app

Expand Down
1 change: 1 addition & 0 deletions backend/apps/core/views/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ def complete_setup_wizard(request):
role="admin",
is_staff=True,
is_active=True,
is_approved=True,
timezone=data.get("timezone", "UTC"),
)

Expand Down
2 changes: 1 addition & 1 deletion backend/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

# Production Security
if not DEBUG:
SECURE_SSL_REDIRECT = True
SECURE_SSL_REDIRECT = os.getenv("SECURE_SSL_REDIRECT", "False") == "True"
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000 # 1 year
Expand Down
11 changes: 11 additions & 0 deletions backend/config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,16 @@
# path('admin/', admin.site.urls),
]

from django.urls import re_path
from django.views.static import serve

if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
else:
urlpatterns += [
re_path(
r"^media/(?P<path>.*)$",
serve,
{"document_root": settings.MEDIA_ROOT},
)
]
26 changes: 26 additions & 0 deletions docker-compose.override.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Development overrides: Host volumes + dev servers
# This file is automatically loaded by 'docker compose up' during development.
# Do NOT use this file in production.

services:
backend:
build:
target: development
command: python manage.py runserver 0.0.0.0:8000
volumes:
- ./backend:/app
environment:
DEBUG: "True"

q_cluster:
build:
target: development
volumes:
- ./backend:/app

frontend:
command: npm run dev
volumes:
- ./frontend:/app
- frontend_node_modules:/app/node_modules
- frontend_next:/app/.next
25 changes: 25 additions & 0 deletions docker-compose.prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Production stack: Optimized images with standalone builds
# Usage: docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

services:
# Django Backend
backend:
build:
target: production
env_file: .env.prod
environment:
DEBUG: "False"

# Background Task Worker
q_cluster:
build:
target: production
env_file: .env.prod

# Next.js Frontend
frontend:
build:
target: production
command: node server.js
environment:
NODE_ENV: production
60 changes: 16 additions & 44 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Full development stack: PostgreSQL + Redis + Django-Q worker + Django + Next.js
# Usage: docker compose up (or: docker compose watch)
# See docker-compose.simple.yml for SQLite only (no Postgres/Redis/background worker)
# Base stack: PostgreSQL + Django-Q worker + Django + Next.js
# This file defines the core services and their connections.
# Use docker-compose.override.yml (dev) or docker-compose.prod.yml (prod) for specific roles.

services:
# PostgreSQL Database
Expand All @@ -23,54 +23,39 @@ services:
build:
context: ./backend
dockerfile: Dockerfile
target: development # Use development stage for local dev with runserver
command: daphne -b 0.0.0.0 -p 8000 config.asgi:application
volumes:
- ./backend:/app
ports:
- "8000:8000"
volumes:
- media_data:/app/media
- static_data:/app/staticfiles
environment:
DEBUG: "True" # <-- Enabled for dev debugging & preventing SSL redirects
DEBUG: "False"
DATABASE_URL: postgresql://studio_user:studio_password@db:5432/studiosync

# Cloudflare R2 / S3 Configuration
SECRET_KEY: ${SECRET_KEY:-dev-secret-key-change-in-production}
ALLOWED_HOSTS: ${ALLOWED_HOSTS:-localhost,127.0.0.1,backend}
CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS:-http://localhost:3000,http://10.0.0.250:3000}
# AWS / Stripe Config
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID:-}
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY:-}
AWS_STORAGE_BUCKET_NAME: ${AWS_STORAGE_BUCKET_NAME:-}
AWS_S3_ENDPOINT_URL: ${AWS_S3_ENDPOINT_URL:-}
AWS_S3_USE_SSL: ${AWS_S3_USE_SSL:-}
SECRET_KEY: ${SECRET_KEY:-dev-secret-key-change-in-production}
ALLOWED_HOSTS: ${ALLOWED_HOSTS:-localhost,127.0.0.1}
CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS:-http://localhost:3000,http://10.0.0.250:3000}

# Stripe Payment Processing
STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY:-}
STRIPE_PUBLISHABLE_KEY: ${STRIPE_PUBLISHABLE_KEY:-}
STRIPE_WEBHOOK_SECRET: ${STRIPE_WEBHOOK_SECRET:-}
depends_on:
db:
condition: service_healthy
develop:
watch:
- action: sync
path: ./backend
target: /app
ignore:
- venv/
- __pycache__/
- .venv/
- action: rebuild
path: ./backend/requirements.txt

# Background Task Worker (Postgres-backed)
# Background Task Worker
q_cluster:
build:
context: ./backend
dockerfile: Dockerfile
target: development
command: python manage.py qcluster
volumes:
- ./backend:/app
- media_data:/app/media
- static_data:/app/staticfiles
environment:
DATABASE_URL: postgresql://studio_user:studio_password@db:5432/studiosync
SECRET_KEY: ${SECRET_KEY:-dev-secret-key-change-in-production}
Expand All @@ -85,30 +70,17 @@ services:
build:
context: ./frontend
dockerfile: Dockerfile
command: npm run dev
volumes:
- ./frontend:/app
- frontend_node_modules:/app/node_modules
- frontend_next:/app/.next
ports:
- "3000:3000"
environment:
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-/api}
INTERNAL_API_URL: http://backend:8000/api
depends_on:
- backend
develop:
watch:
- action: sync
path: ./frontend
target: /app
ignore:
- node_modules/
- .next/
- action: rebuild
path: ./frontend/package.json

volumes:
postgres_data:
frontend_node_modules:
frontend_next:
media_data:
static_data:
47 changes: 39 additions & 8 deletions frontend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,16 +1,47 @@
FROM node:20-alpine

# Base stage for dependencies
FROM node:20-alpine AS base
WORKDIR /app

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm install

# Copy application code
# Development stage
FROM base AS development
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]

# Build stage
FROM base AS builder
COPY . .
# Disable telemetry during build
ENV NEXT_TELEMETRY_DISABLED=1
ARG INTERNAL_API_URL=http://backend:8000/api
ENV INTERNAL_API_URL=$INTERNAL_API_URL
RUN npm run build

# Production stage
FROM node:20-alpine AS production
WORKDIR /app

ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
ENV INTERNAL_API_URL=http://backend:8000/api

# Create a non-root user
RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs

# Copy the standalone output and static files
# Note: output 'standalone' must be enabled in next.config.js
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000

CMD ["npm", "run", "dev"]
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"

CMD ["node", "server.js"]
2 changes: 1 addition & 1 deletion frontend/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const metadata: Metadata = {
title: 'StudioSync - Music Studio Management',
description: 'Sync your studio, students, and schedule — all in one place',
icons: {
icon: process.env.NODE_ENV === 'development' ? '/logo-dev.svg' : '/favicon.ico',
icon: process.env.NODE_ENV === 'development' ? '/logo-dev.svg' : '/logo.png',
apple: '/logo.png'
}
}
Expand Down
2 changes: 1 addition & 1 deletion frontend/app/setup/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export default function SetupPage() {
<div className="flex items-center space-x-2">
<div className="relative h-10 w-10">
<Image
src="/logo-dev.svg"
src="/logo.png"
alt="StudioSync Logo"
fill
className="object-contain"
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/Logo.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Image from 'next/image'

export const Logo = ({ className = "w-8 h-8" }: { className?: string }) => {
const src = '/logo-dev.svg'
const src = '/logo.png'

return (
<div className={`relative ${className}`}>
Expand Down
1 change: 1 addition & 0 deletions frontend/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ const nextConfig = {
];
},
trailingSlash: true,
output: 'standalone',
};

module.exports = nextConfig;
Binary file modified frontend/public/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions frontend/public/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading