diff --git a/client/package-lock.json b/client/package-lock.json
index 36195c7..432624e 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -48,7 +48,7 @@
"@csstools/postcss-oklab-function": "^3.0.16",
"@tanstack/eslint-plugin-query": "^5.43.1",
"@types/js-cookie": "^3.0.6",
- "@types/node": "^20.14.10",
+ "@types/node": "20.17.6",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^7.13.1",
@@ -61,8 +61,9 @@
"postcss": "^8",
"prettier": "^3.3.2",
"prettier-plugin-tailwindcss": "^0.6.5",
+ "sonner": "^1.6.1",
"tailwindcss": "^3.4.1",
- "typescript": "^5.5.2"
+ "typescript": "5.7.2"
}
},
"node_modules/@alloc/quick-lru": {
@@ -2180,12 +2181,13 @@
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "20.14.10",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz",
- "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==",
+ "version": "20.17.6",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.6.tgz",
+ "integrity": "sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "undici-types": "~5.26.4"
+ "undici-types": "~6.19.2"
}
},
"node_modules/@types/prop-types": {
@@ -7481,6 +7483,7 @@
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/sonner/-/sonner-1.7.0.tgz",
"integrity": "sha512-W6dH7m5MujEPyug3lpI2l3TC3Pp1+LTgK0Efg+IHDrBbtEjyCmCHHo6yfNBOsf1tFZ6zf+jceWwB38baC8yO9g==",
+ "dev": true,
"license": "MIT",
"peerDependencies": {
"react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc",
@@ -7982,7 +7985,9 @@
}
},
"node_modules/typescript": {
- "version": "5.5.3",
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz",
+ "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -8008,7 +8013,9 @@
}
},
"node_modules/undici-types": {
- "version": "5.26.5",
+ "version": "6.19.8",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
+ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
"dev": true,
"license": "MIT"
},
diff --git a/client/package.json b/client/package.json
index bbdfce3..e2a7627 100644
--- a/client/package.json
+++ b/client/package.json
@@ -12,7 +12,7 @@
"format": "prettier -w src",
"format:check": "prettier -c src",
"typecheck": "tsc --noEmit",
- "prepare": "cd .. && husky client/.husky"
+ "prepare": "if [ \"$NODE_ENV\" != \"production\" ]; then cd .. && husky client/.husky; fi"
},
"dependencies": {
"@hookform/resolvers": "^3.9.0",
@@ -55,7 +55,7 @@
"@csstools/postcss-oklab-function": "^3.0.16",
"@tanstack/eslint-plugin-query": "^5.43.1",
"@types/js-cookie": "^3.0.6",
- "@types/node": "^20.14.10",
+ "@types/node": "20.17.6",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^7.13.1",
@@ -68,7 +68,8 @@
"postcss": "^8",
"prettier": "^3.3.2",
"prettier-plugin-tailwindcss": "^0.6.5",
+ "sonner": "^1.6.1",
"tailwindcss": "^3.4.1",
- "typescript": "^5.5.2"
+ "typescript": "5.7.2"
}
}
diff --git a/client/src/components/main/header/Navbar.tsx b/client/src/components/main/header/Navbar.tsx
index fd219dc..7bb6b8c 100644
--- a/client/src/components/main/header/Navbar.tsx
+++ b/client/src/components/main/header/Navbar.tsx
@@ -25,7 +25,7 @@ function Links({ isHiddenWhenLg }: { isHiddenWhenLg: boolean }) {
Upcoming Events
-
+
About Us
diff --git a/docker-compose.yml b/docker-compose.yml
index 46ee443..58f9c67 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -3,16 +3,18 @@ services:
container_name: coexist-db
image: postgres:16
restart: unless-stopped
+ env_file: ./server/.env
volumes:
- - ${LOCAL_WORKSPACE_FOLDER:-.}/data/db:/var/lib/postgresql/data
+ - db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 3s
timeout: 3s
retries: 5
- env_file: ./server/.env
- ports:
- - 5432:5432
+ # Remove port mapping in production for security
+ #ports:
+ # - "5432:5432"
+
server:
container_name: coexist-server
build:
@@ -20,10 +22,14 @@ services:
dockerfile: ./docker/server/Dockerfile
restart: unless-stopped
env_file: ./server/.env
+ depends_on:
+ - db
ports:
- - 8000:8000
- volumes:
- - ${LOCAL_WORKSPACE_FOLDER:-.}/server:/app
+ - "8000:8000"
+ # Remove code volume mounts in production
+ #volumes:
+ # - ./server:/app
+
client:
container_name: coexist-client
build:
@@ -31,11 +37,13 @@ services:
dockerfile: ./docker/client/Dockerfile
restart: unless-stopped
env_file: ./client/.env
+ depends_on:
+ - server
ports:
- - 3000:3000
- volumes:
- - ${LOCAL_WORKSPACE_FOLDER:-.}/client:/app
- - ignore:/app/node_modules
+ - "3000:3000"
+ # Remove code volume mounts in production
+ #volumes:
+ # - ./client:/app
volumes:
- ignore:
\ No newline at end of file
+ db-data:
diff --git a/docker/client/Dockerfile b/docker/client/Dockerfile
index e7492aa..7de3587 100644
--- a/docker/client/Dockerfile
+++ b/docker/client/Dockerfile
@@ -8,9 +8,6 @@ COPY /docker/client/entrypoint.sh /entrypoint.sh
COPY ./client/package.json ./client/package-lock.json ./
-# Install ALL Dependencies
-RUN npm install
-
# Copy Application code into a directory called `app`
COPY ./client /app
@@ -21,5 +18,3 @@ COPY ./client /app
# CMD commands get executed at container runtime!
RUN chmod +x /entrypoint.sh
CMD ["/entrypoint.sh"]
-
-# TODO: Production
\ No newline at end of file
diff --git a/docker/client/entrypoint.sh b/docker/client/entrypoint.sh
index 0173c5c..64ace39 100644
--- a/docker/client/entrypoint.sh
+++ b/docker/client/entrypoint.sh
@@ -1,58 +1,39 @@
-#!/bin/bash
+#!/bin/sh
-echo "${APP_NAME^^} - NextJS CONTAINER STARTING..."
-echo $APP_NAME
+set -e
-# Display Docker Image / CI / Release details
-echo "Image Build Date/Time: " "$(cat /app/build_timestamp.txt)" "UTC"
+echo "$APP_NAME - NextJS CONTAINER STARTING..."
+echo "APP_NAME: $APP_NAME"
+
+# Display Build Date/Time if available
+if [ -f /app/build_timestamp.txt ]; then
+ echo "Image Build Date/Time: $(cat /app/build_timestamp.txt) UTC"
+fi
echo "-----------------------------------------------------------"
echo "APP_ENV: ${APP_ENV}"
-# # ====================================================================================
-# # Debug / Sanity check info
-# # ====================================================================================
-# echo " "
-# echo "======= Current Dir / Files (Debug) ============================================================================="
-# pwd
-# ls -al
-
-# echo " "
-# echo "======= Env Vars (Debug) ========================================================================================"
-# if [ "${APP_ENV^^}" != "PRODUCTION" ]; then
-# # Only print environment vars in non-prod environments to prevent sensitive variables being sent to logging system
-# printenv
-# fi
-
-# echo " "
-# echo "======= Linux version (Debug) ==================================================================================="
-# cat /etc/os-release
-
-# echo " "
-# echo "======= Node Path & Version (Debug) ==========================================================================="
-# node -v
-
-# Check for required env vars, exit as failure if missing these critical env vars.
-if [[ -z "${APP_ENV}" ]]; then
+# Check for required env vars
+if [ -z "${APP_ENV}" ]; then
echo "█████████████████████████████████████████████████████████████████████████████████████████████████████████████"
- echo "█ CRITICAL ERROR: Missing 'APP_ENV' environment variables."
+ echo "█ CRITICAL ERROR: Missing 'APP_ENV' environment variable."
echo "█████████████████████████████████████████████████████████████████████████████████████████████████████████████"
- echo "APP_ENV=" $APP_ENV
- echo "░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░"
- exit
+ echo "APP_ENV=${APP_ENV}"
+ exit 1
fi
-# CI TEST DOWN THE TRACK
-
-# ====================================================================================
-# Run inbuilt nextjs server if ENV is LOCAL
-# ====================================================================================
-if [ "${APP_ENV^^}" = "DEVELOPMENT" ]; then
- # Install dependencies (idk why it's not installing the latest ones in the docker image)
+# Start the application based on APP_ENV
+if [ "${APP_ENV}" = "PRODUCTION" ]; then
+ echo "Starting Next.js application in production mode"
+ # Build and run for prod
npm install
- # Run developments
- echo " "
- echo "======= Starting inbuilt nextjs webserver ==================================================================="
- npm run dev
- exit
-fi
\ No newline at end of file
+ npm run build
+ npm start
+elif [ "${APP_ENV}" = "DEVELOPMENT" ]; then
+ echo "Starting Next.js application in development mode"
+ npm install
+ exec npm run dev
+else
+ echo "Unknown APP_ENV: ${APP_ENV}"
+ exit 1
+fi
diff --git a/docker/server/Dockerfile b/docker/server/Dockerfile
index 3e886bd..0f74719 100644
--- a/docker/server/Dockerfile
+++ b/docker/server/Dockerfile
@@ -1,21 +1,41 @@
+# Use the official Python image
FROM python:3.12-slim
-RUN apt-get update && apt-get install --yes --no-install-recommends postgresql-client g++ libssl-dev && rm -rf /var/lib/apt/lists/*
-
-RUN pip install --upgrade pip && pip install poetry
-
-RUN poetry config virtualenvs.in-project false
-RUN poetry config virtualenvs.create false
+# Install system dependencies
+RUN apt-get update && \
+ apt-get install --yes --no-install-recommends \
+ build-essential \
+ libssl-dev \
+ libpq-dev \
+ postgresql-client && \
+ rm -rf /var/lib/apt/lists/*
+# Set work directory
WORKDIR /app
-COPY ./docker/server/entrypoint.sh /entrypoint.sh
+# Install Poetry
+RUN pip install --upgrade pip && \
+ pip install poetry
+# Configure Poetry
+RUN poetry config virtualenvs.create false
+
+# Copy the application files
COPY ./server/pyproject.toml ./server/poetry.lock ./
+RUN poetry install --no-dev --no-interaction --no-ansi
+
+COPY ./server/ ./
-RUN poetry install
+# Collect static files
+RUN python manage.py collectstatic --noinput --verbosity 2
-COPY ./server ./
+# Create log directory
+RUN mkdir -p /var/log/accesslogs && \
+ chmod -R 755 /var/log/accesslogs
+# Copy entrypoint script
+COPY ./docker/server/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
-CMD ["/entrypoint.sh"]
\ No newline at end of file
+
+# Start the application
+ENTRYPOINT ["/entrypoint.sh"]
diff --git a/docker/server/entrypoint.sh b/docker/server/entrypoint.sh
index 5d36abf..2dd3032 100644
--- a/docker/server/entrypoint.sh
+++ b/docker/server/entrypoint.sh
@@ -1,5 +1,9 @@
#!/bin/bash
+set -e
+
+export DJANGO_SETTINGS_MODULE=api.settings
+
# Wait until Database is available before continuing
printf "\n" && echo "Checking Database is up"
# using psql
@@ -17,9 +21,11 @@ python manage.py migrate --noinput
echo "Collecting static files"
python manage.py collectstatic --noinput
-# Create Django Superuser
-echo "Creating Django Superuser"
-python manage.py createsuperuser --noinput
+# Check if superuser exists
+if [ "$APP_ENV" = "DEVELOPMENT" ]; then
+ echo "Creating Django Superuser"
+ python manage.py createsuperuser --noinput || true
+fi
# Run inbuilt Django server if ENV is development
if [ "${APP_ENV^^}" = "DEVELOPMENT" ]; then
diff --git a/server/.env.production b/server/.env.production
index 30d7605..5c6ba5f 100644
--- a/server/.env.production
+++ b/server/.env.production
@@ -2,7 +2,10 @@ APP_NAME=DjangoAPI
APP_ENV=PRODUCTION
+# =============
# TO BE CHANGED
+# =============
+
API_SECRET_KEY="supersecretkey"
API_ALLOWED_HOSTS=".localhost 127.0.0.1 [::1]"
diff --git a/server/api/settings.py b/server/api/settings.py
index 87f084d..df31234 100644
--- a/server/api/settings.py
+++ b/server/api/settings.py
@@ -11,6 +11,8 @@
"""
import os
+import datetime
+
from pathlib import Path
from dotenv import load_dotenv
@@ -155,18 +157,17 @@
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.2/howto/static-files/
-PROJECT_ROOT = os.path.dirname(
- os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-) # <- '/' directory
+STATIC_URL = "/static"
+
+PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
-STATIC_URL = "/static/"
+# Will need to configure this for actual deployment
+STATIC_ROOT = "/var/www/example.com/static/"
-# STATIC_ROOT is where the static files get copied to when "collectstatic" is run.
-# STATIC_ROOT = os.path.join(PROJECT_ROOT, "server", "static")
+STATICFILES_DIRS = [
+ "/static"
+]
-# This is where to _find_ static files when 'collectstatic' is run.
-# These files are then copied to the STATIC_ROOT location.
-STATICFILES_DIRS = (os.path.join(PROJECT_ROOT, "server", "static"),)
# Default primary key field type
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
@@ -176,6 +177,6 @@
# DRF Simple JWT Configuration
SIMPLE_JWT = {
- # "ACCESS_TOKEN_LIFETIME": datetime.timedelta(seconds=10),
- # "REFRESH_TOKEN_LIFETIME": datetime.timedelta(seconds=30),
-}
+ "ACCESS_TOKEN_LIFETIME": datetime.timedelta(seconds=10),
+ "REFRESH_TOKEN_LIFETIME": datetime.timedelta(seconds=30),
+}
\ No newline at end of file