From 2046a32202d70753bb67d122b8a8748ac080ff6e Mon Sep 17 00:00:00 2001 From: cdricms <36056008+cdricms@users.noreply.github.com> Date: Sat, 18 Jan 2025 21:13:03 +0100 Subject: [PATCH] Should be able to deploy --- .env.template | 17 ++++++ backend/main.go | 4 +- docker-compose.yaml | 11 +++- frontend/Dockerfile | 66 ++++++++++++++++++----- frontend/Dockerfile.old | 25 +++++++++ frontend/app/(main)/about/page.tsx | 2 - frontend/eslint.config.mjs | 16 ------ frontend/lib/constants.ts | 2 +- frontend/middleware.ts | 7 ++- frontend/next.config.ts | 10 ++++ init.py | 84 ++++++++++++++++++++++++++++++ init.sh | 4 -- nginx.conf | 11 ---- nginx/default.conf | 20 +++++++ nginx/default.conf.template | 20 +++++++ 15 files changed, 248 insertions(+), 51 deletions(-) create mode 100644 .env.template create mode 100644 frontend/Dockerfile.old delete mode 100644 frontend/eslint.config.mjs create mode 100644 init.py delete mode 100755 init.sh delete mode 100644 nginx.conf create mode 100644 nginx/default.conf create mode 100644 nginx/default.conf.template diff --git a/.env.template b/.env.template new file mode 100644 index 0000000..ae23f5c --- /dev/null +++ b/.env.template @@ -0,0 +1,17 @@ +ENVIRONMENT=${ENVIRONMENT:-PRODUCTION} +POSTGRES_USER=${POSTGRES_USER:-latosa} +POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-1234} +POSTGRES_DB=${POSTGRES_DB:-latosa} +# Docker inner port container +POSTGRES_DOCKER_PORT=${POSTGRES_DOCKER_PORT:-5432} +BACKEND_DOCKER_PORT=${BACKEND_DOCKER_PORT:-3001} +FRONTEND_DOCKER_PORT=${FRONTEND_DOCKER_PORT:-3000} + +POSTGRES_PORT=${POSTGRES_PORT:-5432} +BACKEND_PORT=${BACKEND_PORT:-3001} +FRONTEND_PORT=${FRONTEND_PORT:-3000} + +FRONTEND_HOSTNAME=${FRONTEND_HOSTNAME:-latosa-frontend} +BACKEND_HOSTNAME=${BACKEND_HOSTNAME:-latosa-backend} +DATABASE_HOSTNAME=${DATABASE_HOSTNAME:-latosa-database} +SERVER_NAME=${SERVER_NAME:-localhost} diff --git a/backend/main.go b/backend/main.go index aa115ae..4bbeb6e 100644 --- a/backend/main.go +++ b/backend/main.go @@ -41,12 +41,14 @@ func main() { environ := os.Getenv("ENVIRONMENT") port := os.Getenv("BACKEND_DOCKER_PORT") + hostname := os.Getenv("DATABASE_HOSTNAME") if environ == "DEV" { port = os.Getenv("BACKEND_PORT") + hostname = "localhost" } dsn := core.DSN{ - Hostname: "localhost", + Hostname: hostname, Port: os.Getenv("POSTGRES_PORT"), DBName: os.Getenv("POSTGRES_DB"), User: os.Getenv("POSTGRES_USER"), diff --git a/docker-compose.yaml b/docker-compose.yaml index dbd3c6c..206c5cf 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,7 +1,12 @@ services: latosa-escrima.fr-frontend: container_name: latosa-frontend - image: cems.dev:5000/latosa-escrima.fr:latest + # image: cems.dev:5000/latosa-escrima.fr:latest + build: + context: ./frontend/ + dockerfile: Dockerfile + depends_on: + - latosa-escrima.fr-backend env_file: .env ports: - ${FRONTEND_PORT}:${FRONTEND_DOCKER_PORT} @@ -9,7 +14,10 @@ services: networks: - le-network latosa-escrima.fr-backend: + restart: always container_name: latosa-backend + depends_on: + - psql build: context: ./backend/ dockerfile: Dockerfile @@ -35,3 +43,4 @@ volumes: networks: le-network: + driver: bridge diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 1ba1333..33b45ca 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,24 +1,62 @@ -# Use Deno image -FROM denoland/deno:alpine +# syntax=docker.io/docker/dockerfile:1 -# Set working directory +FROM node:18-alpine AS base + +# Install dependencies only when needed +FROM base AS deps +# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. +RUN apk add --no-cache libc6-compat WORKDIR /app -# Copy project files +# Install dependencies based on the preferred package manager +COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./ +RUN \ + if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ + elif [ -f package-lock.json ]; then npm ci; \ + elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \ + else echo "Lockfile not found." && exit 1; \ + fi + + +# Rebuild the source code only when needed +FROM base AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules COPY . . +# Next.js collects completely anonymous telemetry data about general usage. +# Learn more here: https://nextjs.org/telemetry +# Uncomment the following line in case you want to disable telemetry during the build. +# ENV NEXT_TELEMETRY_DISABLED=1 + +RUN \ + if [ -f yarn.lock ]; then yarn run build; \ + elif [ -f package-lock.json ]; then npm run build; \ + elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \ + else echo "Lockfile not found." && exit 1; \ + fi + +# Production image, copy all the files and run next +FROM base AS runner +WORKDIR /app + ENV NODE_ENV=production -RUN deno install +# Uncomment the following line in case you want to disable telemetry during runtime. +ENV NEXT_TELEMETRY_DISABLED=1 -# Install Next.js dependencies -RUN deno task build +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs -# Move everything to the standalone -RUN cp -r public .next/standalone/public -RUN cp -r .next/static .next/standalone/.next/static -RUN mv .next/standalone/server.js .next/standalone/server.cjs +COPY --from=builder /app/public ./public -RUN rm -r ./node_modules +# Automatically leverage output traces to reduce image size +# https://nextjs.org/docs/advanced-features/output-file-tracing +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static -# Start the Next.js app -CMD ["deno", "run", "--allow-env", "--allow-read", "--allow-sys", "--allow-net", ".next/standalone/server.cjs"] +USER nextjs + +# server.js is created by next build from the standalone output +# https://nextjs.org/docs/pages/api-reference/config/next-config-js/output +ENV HOSTNAME="0.0.0.0" +CMD ["node", "server.js"] diff --git a/frontend/Dockerfile.old b/frontend/Dockerfile.old new file mode 100644 index 0000000..e3705ee --- /dev/null +++ b/frontend/Dockerfile.old @@ -0,0 +1,25 @@ +# Use Deno image +FROM node + +# Set working directory +WORKDIR /app + +# Copy project files +COPY . . + +ENV NODE_PATH=. +ENV NODE_ENV=production +RUN npm install + +# Install Next.js dependencies +RUN npm run build + +# Move everything to the standalone +RUN cp -r public .next/standalone/public +RUN cp -r .next/static .next/standalone/.next/static +RUN mv .next/standalone/server.js .next/standalone/server.cjs + +RUN rm -r ./node_modules + +# Start the Next.js app +CMD ["node", ".next/standalone/server.cjs"] diff --git a/frontend/app/(main)/about/page.tsx b/frontend/app/(main)/about/page.tsx index 2668b5a..025e9b5 100644 --- a/frontend/app/(main)/about/page.tsx +++ b/frontend/app/(main)/about/page.tsx @@ -1,8 +1,6 @@ "use server"; export default async function About() { - const res = await fetch("api"); - console.log(res); return (
diff --git a/frontend/eslint.config.mjs b/frontend/eslint.config.mjs deleted file mode 100644 index 1a9ff38..0000000 --- a/frontend/eslint.config.mjs +++ /dev/null @@ -1,16 +0,0 @@ -import { dirname } from "path"; -import { fileURLToPath } from "url"; -import { FlatCompat } from "@eslint/eslintrc"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -const compat = new FlatCompat({ - baseDirectory: __dirname, -}); - -const eslintConfig = [ - ...compat.extends("next/core-web-vitals", "next/typescript"), -]; - -export default eslintConfig; diff --git a/frontend/lib/constants.ts b/frontend/lib/constants.ts index 7ef418c..4b606d6 100644 --- a/frontend/lib/constants.ts +++ b/frontend/lib/constants.ts @@ -1 +1 @@ -export const API_URL = `http://localhost:${process.env.NEXT_PUBLIC_BACKEND_PORT}`; +export const API_URL = process.env.NEXT_PUBLIC_API_URL ?? ""; diff --git a/frontend/middleware.ts b/frontend/middleware.ts index 74eee25..404163b 100644 --- a/frontend/middleware.ts +++ b/frontend/middleware.ts @@ -5,6 +5,7 @@ import IUser from "./interfaces/IUser"; export async function middleware(request: NextRequest) { const sessionCookie = request.cookies.get("auth_token")?.value; + // console.log(sessionCookie); if (!sessionCookie) { return NextResponse.redirect( new URL( @@ -15,18 +16,22 @@ export async function middleware(request: NextRequest) { } try { + console.log(API_URL); const res = await fetch(`${API_URL}/users/me`, { headers: { Authorization: `Bearer ${sessionCookie}` }, }); const js: ApiResponse = await res.json(); - if (js.status === "Error") + if (js.status === "Error") { + console.log(js.message); return NextResponse.redirect( new URL( `/login?redirectTo=${encodeURIComponent(request.url)}`, request.url, ), ); + } } catch (e: any) { + console.log(e); return NextResponse.redirect( new URL( `/login?redirectTo=${encodeURIComponent(request.url)}`, diff --git a/frontend/next.config.ts b/frontend/next.config.ts index de9111d..1bf2e6b 100644 --- a/frontend/next.config.ts +++ b/frontend/next.config.ts @@ -1,8 +1,17 @@ import type { NextConfig } from "next"; +const apiUrl = + process.env.NODE_ENV !== "production" + ? `http://localhost:${process.env.BACKEND_PORT ?? 3001}` + : `https://${process.env.SERVER_NAME}/api`; + const nextConfig: NextConfig = { /* config options here */ output: "standalone", + // webpack: (config) => { + // config.resolve.alias["@"] = path.resolve(__dirname, "./"); + // return config; + // }, images: { remotePatterns: [ { @@ -13,6 +22,7 @@ const nextConfig: NextConfig = { }, env: { NEXT_PUBLIC_BACKEND_PORT: process.env.BACKEND_PORT, + NEXT_PUBLIC_API_URL: apiUrl, }, }; diff --git a/init.py b/init.py new file mode 100644 index 0000000..e639a94 --- /dev/null +++ b/init.py @@ -0,0 +1,84 @@ +import re +import os + +# Define template and output file paths +TEMPLATE_FILE = ".env.template" +OUTPUT_FILE = ".env" + +def load_template(template_path): + """ + Load the .env.template file. + """ + if not os.path.exists(template_path): + raise FileNotFoundError(f"Template file '{template_path}' not found.") + + with open(template_path, 'r') as file: + return file.readlines() + +def parse_variable(line): + """ + Extract the variable name and default value from a template line. + """ + match = re.match(r"(.*?)\$\{([^}]+)\}(.*)", line) + if match: + prefix, var_value, suffix = match.groups() + + # Extract variable name and default value (if any) + var_name, default_value = (var_value.split(":-") + [""])[:2] + return prefix, var_name, default_value, suffix + return None, None, None, None + +def prompt_variable(var_name, default_value): + """ + Prompt the user to input a value for the variable, using a default if no input is provided. + """ + user_input = input(f"Enter value for {var_name} [default: {default_value}]: ").strip() + return user_input if user_input else default_value + +def generate_env(template_lines): + """ + Generate the .env file content by substituting variables from the template. + """ + output_lines = ["# Generated .env file\n"] + for line in template_lines: + # Skip comments and empty lines + if line.strip().startswith("#") or not line.strip(): + output_lines.append(line) + continue + + # Parse and process variables + prefix, var_name, default_value, suffix = parse_variable(line) + if var_name: + # Get the default value from the template (already handled in the parsing) + value = prompt_variable(var_name, default_value) + output_lines.append(f"{prefix}{value}{suffix}\n") + else: + # Unprocessed line (e.g., no variables) + output_lines.append(line) + + return output_lines + +def write_env(output_path, lines): + """ + Write the generated .env file content to the output file. + """ + with open(output_path, 'w') as file: + file.writelines(lines) + print(f".env file has been successfully created at '{output_path}'.") + +def main(): + try: + # Load the template file + template_lines = load_template(TEMPLATE_FILE) + + # Generate the .env content + output_lines = generate_env(template_lines) + + # Write the content to the .env file + write_env(OUTPUT_FILE, output_lines) + + except Exception as e: + print(f"Error: {e}") + +if __name__ == "__main__": + main() diff --git a/init.sh b/init.sh deleted file mode 100755 index 05604a9..0000000 --- a/init.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/sh -printf "ENVIRONMENT=DEV\nPOSTGRES_USER=\nPOSTGRES_PASSWORD=\nPOSTGRES_DB=\n#\nDocker\ninner\nport\ncontainer\nPOSTGRES_DOCKER_PORT=5432\nBACKEND_DOCKER_PORT=3000\nFRONTEND_DOCKER_PORT=3000\nPOSTGRES_PORT=5432\nBACKEND_PORT=3001\nFRONTEND_PORT=3000\n" > .env -ln $(pwd)/.env $(pwd)/backend -ln $(pwd)/.env $(pwd)/frontend diff --git a/nginx.conf b/nginx.conf deleted file mode 100644 index 198bdb4..0000000 --- a/nginx.conf +++ /dev/null @@ -1,11 +0,0 @@ -server { - server_name TO BE SET; - - location / { - proxy_pass http://localhost:FRONTEND_PORT; # Set frontend port based on what you have exposed - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} diff --git a/nginx/default.conf b/nginx/default.conf new file mode 100644 index 0000000..0764da9 --- /dev/null +++ b/nginx/default.conf @@ -0,0 +1,20 @@ +server { + listen 80; + server_name localhost; + + location / { + proxy_pass http://latosa-frontend:3000; # Set frontend port based on what you have exposed + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /api { + proxy_pass http://latosa-backend:3001; # Set frontend port based on what you have exposed + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} diff --git a/nginx/default.conf.template b/nginx/default.conf.template new file mode 100644 index 0000000..e507f9e --- /dev/null +++ b/nginx/default.conf.template @@ -0,0 +1,20 @@ +server { + listen 80; + server_name ${SERVER_NAME}; + + location / { + proxy_pass http://${FRONTEND_HOSTNAME}:${FRONTEND_PORT}; # Set frontend port based on what you have exposed + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /api { + proxy_pass http://${BACKEND_HOSTNAME}:${BACKEND_PORT}; # Set frontend port based on what you have exposed + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +}