diff --git a/.env.example b/.env.example index c7115a0e9..e7a86f20f 100644 --- a/.env.example +++ b/.env.example @@ -12,3 +12,7 @@ DB_DATABASE=vitnode # ! Frontend # NEXT_PUBLIC_GRAPHQL_URL=http://backend:8080 NEXT_PUBLIC_BACKEND_URL=http://localhost:8080 + + +# ! Debug +# NEXT_PUBLIC_DEBUG=true \ No newline at end of file diff --git a/backend/src/config.ts b/backend/src/config.ts index 4b6161aa4..077192480 100644 --- a/backend/src/config.ts +++ b/backend/src/config.ts @@ -1,18 +1,24 @@ import { promises } from "fs"; import { join } from "path"; +import { parseFrontendUrlFromEnv } from "./functions/envs"; export const configForAppModule = () => { + const frontend_url = parseFrontendUrlFromEnv(); + const data = { login_token_secret: process.env.LOGIN_TOKEN_SECRET ?? "", - frontend_url: process.env.NEXT_PUBLIC_FRONTEND_URL - ? `https://${process.env.NEXT_PUBLIC_FRONTEND_URL}` - : "http://localhost:3000", + frontend_url: frontend_url.url, port: parseInt(process.env.PORT, 10) || 8080, password_salt: 10, cookies: { - domain: process.env.NEXT_PUBLIC_FRONTEND_URL - ? process.env.NEXT_PUBLIC_FRONTEND_URL - : "localhost", + domain: + frontend_url.hostname === "localhost" + ? "localhost" + : frontend_url.hostname + .replace(/:\d+$/, "") + .split(".") + .slice(-2) + .join("."), login_token: { expiresIn: 7, // 7 days expiresInRemember: 90, // 90 days diff --git a/backend/src/functions/envs.ts b/backend/src/functions/envs.ts new file mode 100644 index 000000000..7d23fef64 --- /dev/null +++ b/backend/src/functions/envs.ts @@ -0,0 +1,12 @@ +export const parseFrontendUrlFromEnv = () => { + const envUrl = process.env.NEXT_PUBLIC_FRONTEND_URL; + const frontendUrl = envUrl ? envUrl : "http://localhost:3000"; + const urlObj = new URL(frontendUrl); + + return { + url: frontendUrl, + protocol: urlObj.protocol, + hostname: urlObj.hostname, + port: urlObj.port + }; +}; diff --git a/backend/src/main.ts b/backend/src/main.ts index 30bfd4bab..cf7db9670 100644 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-console */ import { NestFactory } from "@nestjs/core"; import { AppModule } from "./app.module"; import cookieParser from "cookie-parser"; @@ -11,7 +12,7 @@ async function bootstrap() { credentials: true, origin: [ process.env.NEXT_PUBLIC_FRONTEND_URL - ? `https://${process.env.NEXT_PUBLIC_FRONTEND_URL}` + ? process.env.NEXT_PUBLIC_FRONTEND_URL : "http://localhost:3000", "https://sandbox.embed.apollographql.com" ] @@ -26,7 +27,24 @@ async function bootstrap() { ); await app.listen(process.env.PORT ?? "8080", null, () => { - // eslint-disable-next-line no-console + if (process.env.NEXT_PUBLIC_DEBUG) { + console.warn( + "WARNING: Debug mode is enabled. Do not use this in production." + ); + console.log( + "DEBUG ENV: NEXT_PUBLIC_FRONTEND_URL -", + process.env.NEXT_PUBLIC_FRONTEND_URL + ); + console.log("DEBUG ENV: DB_HOST -", process.env.DB_HOST); + console.log("DEBUG ENV: DB_PORT -", process.env.DB_PORT); + console.log("DEBUG ENV: DB_PASSWORD -", process.env.DB_PASSWORD); + console.log("DEBUG ENV: DB_DATABASE -", process.env.DB_DATABASE); + console.log( + "DEBUG ENV: LOGIN_TOKEN_SECRET -", + process.env.LOGIN_TOKEN_SECRET + ); + } + console.log( `Application is running on: http://localhost:${process.env.PORT ?? 8080}/graphql` ); diff --git a/backend/src/plugins/core/sessions/sign_in/sign_in.service.ts b/backend/src/plugins/core/sessions/sign_in/sign_in.service.ts index 3c0ca213b..6914d5402 100644 --- a/backend/src/plugins/core/sessions/sign_in/sign_in.service.ts +++ b/backend/src/plugins/core/sessions/sign_in/sign_in.service.ts @@ -111,6 +111,12 @@ export class SignInCoreSessionsService { device_id: device.id }); + console.log( + "cookies.domain", + this.configService.getOrThrow("cookies.domain") + ); + console.log("frontend_url", this.configService.getOrThrow("frontend_url")); + // Set cookie for session res.cookie( this.configService.getOrThrow("cookies.login_token.name"), diff --git a/backend/src/utils/actions/helpers/manifest.ts b/backend/src/utils/actions/helpers/manifest.ts index b7299ef11..fd5b2dc45 100644 --- a/backend/src/utils/actions/helpers/manifest.ts +++ b/backend/src/utils/actions/helpers/manifest.ts @@ -6,6 +6,7 @@ import * as dotenv from "dotenv"; import { objectToArray, updateObject } from "./update-object"; import { ABSOLUTE_PATHS, getConfigFile } from "@/config"; +import { parseFrontendUrlFromEnv } from "@/functions/envs"; dotenv.config({ path: join(process.cwd(), "..", ".env") @@ -62,6 +63,36 @@ interface ManifestType { theme_color?: string; } +const generateDefaultManifest = ({ + lang_code, + frontend_url, + site_name, + site_short_name +}: { + lang_code: string; + frontend_url: string; + site_name: string; + site_short_name: string; +}): ManifestType => ({ + id: `${frontend_url}/${lang_code}/`, + name: site_name, + short_name: site_short_name, + lang: lang_code, + description: "", + display: "standalone", + theme_color: "#2463eb", + background_color: "#09090b", + start_url: `${frontend_url}/${lang_code}/`, + orientation: "any", + icons: [ + { + src: "/icons/favicon.ico", + sizes: "any", + type: "image/x-icon" + } + ] +}); + export const generateManifest = async () => { const config = await getConfigFile(); const languages = ( @@ -69,41 +100,31 @@ export const generateManifest = async () => { ) .filter(dirent => dirent.isDirectory()) .map(dirent => dirent.name); - const frontendUrl = process.env.NEXT_PUBLIC_FRONTEND_URL - ? `https://${process.env.NEXT_PUBLIC_FRONTEND_URL}` - : "http://localhost:3000"; + const frontend_url = parseFrontendUrlFromEnv().url; - languages.forEach(language => { - const defaultManifest: ManifestType = { - id: `${frontendUrl}/${language}/`, - name: config.settings.general.site_name, - short_name: config.settings.general.site_short_name, - lang: language, - description: "", - display: "standalone", - theme_color: "#2463eb", - background_color: "#09090b", - start_url: `${frontendUrl}/${language}/`, - orientation: "any", - icons: [ - { - src: "/icons/favicon.ico", - sizes: "any", - type: "image/x-icon" - } - ] - }; - const path = join(ABSOLUTE_PATHS.uploads.public, "assets", language); + languages.forEach(lang_code => { + const defaultManifest: ManifestType = generateDefaultManifest({ + lang_code, + frontend_url, + site_name: config.settings.general.site_name, + site_short_name: config.settings.general.site_short_name + }); + const path = join(ABSOLUTE_PATHS.uploads.public, "assets", lang_code); const filePath = join(path, "manifest.webmanifest"); if (fs.existsSync(filePath)) { const data = fs.readFileSync(filePath, "utf8"); const manifest: ManifestType = JSON.parse(data); - + const start_url = `${frontend_url}/${lang_code}`; const updatedManifest = objectToArray( - updateObject(manifest, { - ...defaultManifest - }) + updateObject( + { + ...manifest, + start_url: `${start_url}${manifest.start_url.replace(start_url, "")}`, + id: `${start_url}${manifest.start_url.replace(start_url, "")}` + }, + defaultManifest + ) ); fs.writeFile( diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index fdaa72966..5720e3004 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -6,8 +6,8 @@ services: image: postgres:16.3-alpine restart: unless-stopped environment: - POSTGRES_USER: root - POSTGRES_PASSWORD: root + POSTGRES_USER: ${DB_USER-root} + POSTGRES_PASSWORD: ${DB_PASSWORD-root} POSTGRES_DB: vitnode command: ["postgres", "-c", "log_statement=all"] # log all sql queries volumes: diff --git a/docker-compose.yml b/docker-compose.yml index 4299a5d18..95476e52d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,8 +28,8 @@ services: - DB_PASSWORD=${DB_PASSWORD} - DB_DATABASE=${DB_DATABASE:-vitnode} - LOGIN_TOKEN_SECRET=${LOGIN_TOKEN_SECRET} - # - NEXT_PUBLIC_FRONTEND_URL=${NEXT_PUBLIC_FRONTEND_URL} - command: sh -c "pnpm start:prod" + - NEXT_PUBLIC_FRONTEND_URL=${NEXT_PUBLIC_FRONTEND_URL:-http://localhost:3000} + command: sh -c "pnpm config:database && pnpm start:prod" ports: - "8080:8080" depends_on: @@ -50,14 +50,24 @@ services: build: context: ./frontend dockerfile: Dockerfile + args: + - NEXT_PUBLIC_DEBUG=${NEXT_PUBLIC_DEBUG:-false} + - NEXT_PUBLIC_GRAPHQL_URL=${NEXT_PUBLIC_GRAPHQL_URL:-http://backend:8080} + - NEXT_PUBLIC_BACKEND_URL=${NEXT_PUBLIC_BACKEND_URL:-http://localhost:8080} + - NEXT_PUBLIC_FRONTEND_URL=${NEXT_PUBLIC_FRONTEND_URL:-http://localhost:3000} restart: unless-stopped command: sh -c "pnpm start:prod" + # environment: + # - NEXT_PUBLIC_DEBUG=${NEXT_PUBLIC_DEBUG:-false} + # - NEXT_PUBLIC_GRAPHQL_URL=${NEXT_PUBLIC_GRAPHQL_URL:-http://backend:8080} + # - NEXT_PUBLIC_BACKEND_URL=${NEXT_PUBLIC_BACKEND_URL:-http://localhost:8080} + # - NEXT_PUBLIC_FRONTEND_URL=${NEXT_PUBLIC_FRONTEND_URL:-http://localhost:3000} ports: - "3000:3000" depends_on: - backend volumes: - # - ./frontend/config:/app/config # config + - ./frontend/config:/app/config # config - ./frontend/themes:/app/themes # themes - ./frontend/langs:/app/langs # langs - ./frontend/app:/app/app # app @@ -68,4 +78,4 @@ services: - vitnode networks: - vitnode: \ No newline at end of file + vitnode: diff --git a/docker-prod.sh b/docker-prod.sh new file mode 100644 index 000000000..f3fa16970 --- /dev/null +++ b/docker-prod.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# Run docker-compose +docker compose -f ./docker-compose.yml -p vitnode up -d --build + +# Copy config.json from container to local +docker cp vitnode_frontend:/app/config.json ./frontend/config/config.json \ No newline at end of file diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 3d6ad2a76..187154f98 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -14,11 +14,14 @@ WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . -# # Set the API URI -# ARG NEXT_PUBLIC_GRAPHQL_URL -# ENV NEXT_PUBLIC_GRAPHQL_URL=${NEXT_PUBLIC_GRAPHQL_URL} -# ARG NEXT_PUBLIC_BACKEND_URL -# ENV NEXT_PUBLIC_BACKEND_URL=${NEXT_PUBLIC_BACKEND_URL} +ARG NEXT_PUBLIC_GRAPHQL_URL +ENV NEXT_PUBLIC_GRAPHQL_URL=${NEXT_PUBLIC_GRAPHQL_URL} +ARG NEXT_PUBLIC_BACKEND_URL +ENV NEXT_PUBLIC_BACKEND_URL=${NEXT_PUBLIC_BACKEND_URL} +ARG NEXT_PUBLIC_FRONTEND_URL +ENV NEXT_PUBLIC_FRONTEND_URL=${NEXT_PUBLIC_FRONTEND_URL} +ARG NEXT_PUBLIC_DEBUG +ENV NEXT_PUBLIC_DEBUG=${NEXT_PUBLIC_DEBUG} RUN pnpm build @@ -34,6 +37,7 @@ ENV NODE_ENV production # RUN adduser --system --uid 1001 nextjs COPY --from=builder /app/public ./public +COPY --from=builder /app/config/config.json ./config.json # Set the correct permission for prerender cache RUN mkdir .next diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx index 865e35bf2..6b37cda3d 100644 --- a/frontend/app/layout.tsx +++ b/frontend/app/layout.tsx @@ -1,3 +1,4 @@ +/* eslint-disable no-console */ import type { Metadata } from "next"; import type { ReactNode } from "react"; @@ -23,5 +24,14 @@ interface Props { } export default function RootLayout({ children }: Props) { + if (process.env.NEXT_PUBLIC_DEBUG === "true") { + console.log( + "NEXT_PUBLIC_FRONTEND_URL", + process.env.NEXT_PUBLIC_FRONTEND_URL + ); + console.log("NEXT_PUBLIC_BACKEND_URL", process.env.NEXT_PUBLIC_BACKEND_URL); + console.log("NEXT_PUBLIC_GRAPHQL_URL", process.env.NEXT_PUBLIC_GRAPHQL_URL); + } + return children; } diff --git a/frontend/package.json b/frontend/package.json index 6bb8512c9..d13f245e9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,7 +11,7 @@ "test": "vitest", "test:once": "vitest --run", "start": "next start", - "start:prod": "pnpm config:init && node server.js", + "start:prod": "node server.js", "lint": "eslint .", "lint:fix": "eslint . --fix", "codegen": "cd .. && pnpm codegen", diff --git a/package.json b/package.json index c9066b2e7..0d3814853 100644 --- a/package.json +++ b/package.json @@ -19,10 +19,8 @@ "preinstall": "npx only-allow pnpm", "config:init": "cd frontend && pnpm config:init && cd ..", "codegen": "cd backend && pnpm codegen && cd ..", - "docker:test": "pnpm config:init && docker-compose -f ./docker-compose-prod-backend.yml -p vitnode-test up -d --build && docker-compose -f ./docker-compose-prod-frontend.yml -p vitnode-test up -d --build && pnpm config:finish", - "docker:prod": "sudo pnpm config:init && sudo docker compose -f ./docker-compose-prod-backend.yml -p vitnode-prod up -d --build && sudo docker compose -f ./docker-compose-prod-frontend.yml -p vitnode-prod up -d --build && sudo pnpm config:finish", "docker:dev": "docker-compose -f ./docker-compose-dev.yml -p vitnode-dev up -d", - "docker": "docker-compose -f ./docker-compose.yml -p vitnode up -d", + "docker:prod": "sh docker-prod.sh", "docker:clear": "sudo docker system prune -a" }, "engines": {