mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-18 11:59:26 +00:00
feat: implement runtime configuration and API client enhancements
This commit is contained in:
parent
4663256eed
commit
78cabbc665
6 changed files with 225 additions and 4 deletions
|
|
@ -61,15 +61,31 @@ ENV PORT=80 LEARNHOUSE_PORT=9000 HOSTNAME=0.0.0.0
|
|||
COPY ./extra/start.sh /app/start.sh
|
||||
RUN chmod +x /app/start.sh
|
||||
|
||||
# Add enhanced URL and domain patching
|
||||
# Add runtime configuration and enhanced patching
|
||||
RUN echo '#!/bin/bash\n\
|
||||
echo "Generating runtime configuration..."\n\
|
||||
mkdir -p /app/web/public\n\
|
||||
cat > /app/web/public/runtime-config.js << EOF\n\
|
||||
window.RUNTIME_CONFIG = {\n\
|
||||
LEARNHOUSE_API_URL: "${NEXT_PUBLIC_LEARNHOUSE_API_URL:-}",\n\
|
||||
LEARNHOUSE_BACKEND_URL: "${NEXT_PUBLIC_LEARNHOUSE_BACKEND_URL:-}",\n\
|
||||
LEARNHOUSE_DOMAIN: "${NEXT_PUBLIC_LEARNHOUSE_DOMAIN:-}",\n\
|
||||
LEARNHOUSE_DEFAULT_ORG: "${NEXT_PUBLIC_LEARNHOUSE_DEFAULT_ORG:-default}",\n\
|
||||
LEARNHOUSE_MULTI_ORG: "${NEXT_PUBLIC_LEARNHOUSE_MULTI_ORG:-false}",\n\
|
||||
LEARNHOUSE_TOP_DOMAIN: "${NEXT_PUBLIC_LEARNHOUSE_TOP_DOMAIN:-}"\n\
|
||||
}\n\
|
||||
EOF\n\
|
||||
\n\
|
||||
echo "Runtime configuration generated successfully"\n\
|
||||
\n\
|
||||
echo "Enhanced patching of NextAuth cookies and domains..."\n\
|
||||
find /app/web/.next -type f -name "*.js" -exec sed -i "s/domain:[^,}]*,/domain: undefined,/g" {} \\;\n\
|
||||
find /app/web/.next -type f -name "*.js" -exec sed -i "s/domain: *process.env.LEARNHOUSE_COOKIE_DOMAIN/domain: undefined/g" {} \\;\n\
|
||||
find /app/web/.next -type f -name "*.js" -exec sed -i "s/\.domain\s*=\s*[^;]*;/\.domain = undefined;/g" {} \\;\n\
|
||||
echo "Cookie domain patches complete."\n\
|
||||
\n\
|
||||
echo "Starting application..."\n\
|
||||
sh /app/start.sh' > /app/patched-start.sh && chmod +x /app/patched-start.sh
|
||||
sh /app/start.sh' > /app/runtime-config-start.sh && chmod +x /app/runtime-config-start.sh
|
||||
|
||||
# Use the patched start script
|
||||
CMD ["/app/patched-start.sh"]
|
||||
# Use the runtime config script
|
||||
CMD ["/app/runtime-config-start.sh"]
|
||||
75
Dockerfile_nextjs
Normal file
75
Dockerfile_nextjs
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# syntax=docker.io/docker/dockerfile:1
|
||||
|
||||
FROM node:18-alpine AS base
|
||||
|
||||
# Step 1. Rebuild the source code only when needed
|
||||
FROM base AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies based on the preferred package manager
|
||||
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./
|
||||
# Omit --production flag for TypeScript devDependencies
|
||||
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; \
|
||||
# Allow install without lockfile, so example works even without Node.js installed locally
|
||||
else echo "Warning: Lockfile not found. It is recommended to commit lockfiles to version control." && yarn install; \
|
||||
fi
|
||||
|
||||
COPY src ./src
|
||||
COPY public ./public
|
||||
COPY next.config.js .
|
||||
COPY tsconfig.json .
|
||||
|
||||
# Environment variables must be present at build time
|
||||
# https://github.com/vercel/next.js/discussions/14030
|
||||
ARG ENV_VARIABLE
|
||||
ENV ENV_VARIABLE=${ENV_VARIABLE}
|
||||
ARG NEXT_PUBLIC_ENV_VARIABLE
|
||||
ENV NEXT_PUBLIC_ENV_VARIABLE=${NEXT_PUBLIC_ENV_VARIABLE}
|
||||
|
||||
# Next.js collects completely anonymous telemetry data about general usage. Learn more here: https://nextjs.org/telemetry
|
||||
# Uncomment the following line to disable telemetry at build time
|
||||
# ENV NEXT_TELEMETRY_DISABLED 1
|
||||
|
||||
# Build Next.js based on the preferred package manager
|
||||
RUN \
|
||||
if [ -f yarn.lock ]; then yarn build; \
|
||||
elif [ -f package-lock.json ]; then npm run build; \
|
||||
elif [ -f pnpm-lock.yaml ]; then pnpm build; \
|
||||
else npm run build; \
|
||||
fi
|
||||
|
||||
# Note: It is not necessary to add an intermediate step that does a full copy of `node_modules` here
|
||||
|
||||
# Step 2. Production image, copy all the files and run next
|
||||
FROM base AS runner
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Don't run production as root
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nextjs
|
||||
USER nextjs
|
||||
|
||||
COPY --from=builder /app/public ./public
|
||||
|
||||
# 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
|
||||
|
||||
# Environment variables must be redefined at run time
|
||||
ARG ENV_VARIABLE
|
||||
ENV ENV_VARIABLE=${ENV_VARIABLE}
|
||||
ARG NEXT_PUBLIC_ENV_VARIABLE
|
||||
ENV NEXT_PUBLIC_ENV_VARIABLE=${NEXT_PUBLIC_ENV_VARIABLE}
|
||||
|
||||
# Uncomment the following line to disable telemetry at run time
|
||||
# ENV NEXT_TELEMETRY_DISABLED 1
|
||||
|
||||
# Note: Don't expose ports here, Compose will handle that for us
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
16
apps/web/pages/_document.js
Normal file
16
apps/web/pages/_document.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { Html, Head, Main, NextScript } from 'next/document';
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<Head>
|
||||
{/* Load runtime configuration before any app code */}
|
||||
<script src="/runtime-config.js" />
|
||||
</Head>
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
59
apps/web/utils/apiClient.js
Normal file
59
apps/web/utils/apiClient.js
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import { getApiUrl } from './runtimeConfig';
|
||||
|
||||
/**
|
||||
* Creates an API client that uses runtime configuration
|
||||
* @returns {Object} API client object with fetch method
|
||||
*/
|
||||
export function createApiClient() {
|
||||
return {
|
||||
/**
|
||||
* Fetch data from the API with the correct runtime URL
|
||||
* @param {string} path - API path to fetch from
|
||||
* @param {Object} options - Fetch options
|
||||
* @returns {Promise<any>} - JSON response
|
||||
*/
|
||||
fetch: async (path, options = {}) => {
|
||||
const url = getApiUrl(path);
|
||||
console.log(`[API] Fetching from: ${url}`);
|
||||
|
||||
const response = await fetch(url, options);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`API request failed: ${response.status}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
},
|
||||
|
||||
/**
|
||||
* Post data to the API with the correct runtime URL
|
||||
* @param {string} path - API path to post to
|
||||
* @param {Object} data - Data to post
|
||||
* @param {Object} options - Additional fetch options
|
||||
* @returns {Promise<any>} - JSON response
|
||||
*/
|
||||
post: async (path, data, options = {}) => {
|
||||
const url = getApiUrl(path);
|
||||
console.log(`[API] Posting to: ${url}`);
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers,
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
...options,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`API request failed: ${response.status}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Create a singleton instance for easier imports
|
||||
export const apiClient = createApiClient();
|
||||
52
apps/web/utils/runtimeConfig.js
Normal file
52
apps/web/utils/runtimeConfig.js
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* Utility functions to access runtime configuration values
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gets configuration from runtime values (window.RUNTIME_CONFIG) with fallback to environment variables
|
||||
* This ensures dynamic configuration at runtime instead of build time
|
||||
*/
|
||||
export function useRuntimeConfig() {
|
||||
// For server-side rendering, use environment variables
|
||||
if (typeof window === 'undefined') {
|
||||
return {
|
||||
apiUrl: process.env.NEXT_PUBLIC_LEARNHOUSE_API_URL,
|
||||
backendUrl: process.env.NEXT_PUBLIC_LEARNHOUSE_BACKEND_URL,
|
||||
domain: process.env.NEXT_PUBLIC_LEARNHOUSE_DOMAIN,
|
||||
defaultOrg: process.env.NEXT_PUBLIC_LEARNHOUSE_DEFAULT_ORG || 'default',
|
||||
multiOrg: process.env.NEXT_PUBLIC_LEARNHOUSE_MULTI_ORG === 'true',
|
||||
topDomain: process.env.NEXT_PUBLIC_LEARNHOUSE_TOP_DOMAIN
|
||||
};
|
||||
}
|
||||
|
||||
// For client-side, prioritize runtime config over baked-in environment variables
|
||||
const config = window.RUNTIME_CONFIG || {};
|
||||
return {
|
||||
apiUrl: config.LEARNHOUSE_API_URL || process.env.NEXT_PUBLIC_LEARNHOUSE_API_URL,
|
||||
backendUrl: config.LEARNHOUSE_BACKEND_URL || process.env.NEXT_PUBLIC_LEARNHOUSE_BACKEND_URL,
|
||||
domain: config.LEARNHOUSE_DOMAIN || process.env.NEXT_PUBLIC_LEARNHOUSE_DOMAIN,
|
||||
defaultOrg: config.LEARNHOUSE_DEFAULT_ORG || process.env.NEXT_PUBLIC_LEARNHOUSE_DEFAULT_ORG || 'default',
|
||||
multiOrg: config.LEARNHOUSE_MULTI_ORG === 'true' || process.env.NEXT_PUBLIC_LEARNHOUSE_MULTI_ORG === 'true',
|
||||
topDomain: config.LEARNHOUSE_TOP_DOMAIN || process.env.NEXT_PUBLIC_LEARNHOUSE_TOP_DOMAIN
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the API URL with the correct domain
|
||||
* @param {string} path - The API path
|
||||
* @returns {string} - The complete API URL
|
||||
*/
|
||||
export function getApiUrl(path) {
|
||||
const config = useRuntimeConfig();
|
||||
return `${config.apiUrl || `https://${config.domain}/api/v1/`}${path}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the backend URL with the correct domain
|
||||
* @param {string} path - The backend path
|
||||
* @returns {string} - The complete backend URL
|
||||
*/
|
||||
export function getBackendUrl(path) {
|
||||
const config = useRuntimeConfig();
|
||||
return `${config.backendUrl || `https://${config.domain}/`}${path}`;
|
||||
}
|
||||
|
|
@ -7,6 +7,9 @@ services:
|
|||
- NEXT_PUBLIC_LEARNHOUSE_API_URL=${NEXT_PUBLIC_LEARNHOUSE_API_URL}
|
||||
- NEXT_PUBLIC_LEARNHOUSE_BACKEND_URL=${NEXT_PUBLIC_LEARNHOUSE_BACKEND_URL}
|
||||
- NEXT_PUBLIC_LEARNHOUSE_DOMAIN=${NEXT_PUBLIC_LEARNHOUSE_DOMAIN}
|
||||
- NEXT_PUBLIC_LEARNHOUSE_DEFAULT_ORG=${NEXT_PUBLIC_LEARNHOUSE_DEFAULT_ORG}
|
||||
- NEXT_PUBLIC_LEARNHOUSE_MULTI_ORG=${NEXT_PUBLIC_LEARNHOUSE_MULTI_ORG}
|
||||
- NEXT_PUBLIC_LEARNHOUSE_TOP_DOMAIN=${NEXT_PUBLIC_LEARNHOUSE_TOP_DOMAIN}
|
||||
# Mount the persistent volume to the actual uploads directory (/app/api/content)
|
||||
volumes:
|
||||
- app-uploads:/app/api/content
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue