Files
scriptshare-cursor-clone/Dockerfile

113 lines
6.9 KiB
Docker
Raw Normal View History

# Build stage
FROM node:18-alpine AS builder
# Install build dependencies for native modules (bcrypt, etc.)
RUN apk add --no-cache python3 make g++ libc6-compat
WORKDIR /app
# Copy package files first for better Docker layer caching
COPY package*.json ./
# Install dependencies with proper npm cache handling
RUN npm ci --only=production=false --silent
# Copy source code
COPY . .
# Set build-time environment variables
ARG VITE_APP_NAME="ScriptShare"
ARG VITE_APP_URL="https://scriptshare.example.com"
ARG VITE_ANALYTICS_ENABLED="false"
# Export as environment variables for Vite build
ENV VITE_APP_NAME=$VITE_APP_NAME
ENV VITE_APP_URL=$VITE_APP_URL
ENV VITE_ANALYTICS_ENABLED=$VITE_ANALYTICS_ENABLED
# Remove problematic packages from package.json to prevent them from being bundled
RUN sed -i '/"mysql2"/d' package.json
RUN sed -i '/"drizzle-orm"/d' package.json
RUN sed -i '/"bcrypt"/d' package.json
RUN sed -i '/"jsonwebtoken"/d' package.json
RUN sed -i '/"@types\/bcrypt"/d' package.json
RUN sed -i '/"@types\/jsonwebtoken"/d' package.json
RUN sed -i '/"nanoid"/d' package.json
# Reinstall dependencies without server packages
RUN npm install
# Remove problematic server-side API files for frontend-only build
RUN rm -rf src/lib/api || true
RUN rm -rf src/lib/db || true
# Create mock API layer for frontend demo
RUN mkdir -p src/lib/api src/lib/db
# Create mock database files
RUN echo "export const db = {};" > src/lib/db/index.ts
RUN echo "export const users = {}; export const scripts = {}; export const ratings = {}; export const scriptVersions = {}; export const scriptAnalytics = {}; export const scriptCollections = {}; export const collectionScripts = {};" > src/lib/db/schema.ts
# Create comprehensive mock API files using printf for reliable multiline content
# Mock API index with proper types
RUN printf 'export const generateId = () => Math.random().toString(36).substr(2, 9);\nexport class ApiError extends Error {\n constructor(message: string, public status: number) {\n super(message);\n this.status = status;\n }\n}' > src/lib/api/index.ts
# Mock auth API with complete interface
RUN printf 'export const authApi = {\n login: async (data: any) => ({ token: "demo", user: { id: "1", username: "demo" } }),\n register: async (data: any) => ({ token: "demo", user: { id: "1", username: "demo" } }),\n changePassword: async (data: any) => ({}),\n refreshToken: async () => ({ token: "demo" })\n};' > src/lib/api/auth.ts
# Mock scripts API with all required methods
RUN printf 'export interface ScriptFilters {\n search?: string;\n categories?: string[];\n compatibleOs?: string[];\n sortBy?: string;\n limit?: number;\n isApproved?: boolean;\n}\nexport interface UpdateScriptData {\n name?: string;\n description?: string;\n content?: string;\n}\nexport const scriptsApi = {\n getScripts: async (filters?: ScriptFilters) => ({ scripts: [], total: 0 }),\n getScriptById: async (id: string) => null,\n getPopularScripts: async () => [],\n getRecentScripts: async () => [],\n createScript: async (data: any) => ({ id: "mock" }),\n updateScript: async (id: string, data: UpdateScriptData, userId: string) => ({ id }),\n deleteScript: async (id: string, userId: string) => ({}),\n moderateScript: async (id: string, isApproved: boolean, moderatorId: string) => ({ id, isApproved }),\n incrementViewCount: async (id: string) => ({}),\n incrementDownloadCount: async (id: string) => ({})\n};' > src/lib/api/scripts.ts
# Mock ratings API with complete interface
RUN printf 'export const ratingsApi = {\n submitRating: async (data: any) => ({ scriptId: data.scriptId }),\n rateScript: async (data: any) => ({ scriptId: data.scriptId }),\n getUserRating: async (scriptId: string, userId?: string) => null,\n getScriptRatings: async (scriptId: string) => [],\n getScriptRatingStats: async (scriptId: string) => ({ averageRating: 0, totalRatings: 0, distribution: {} }),\n deleteRating: async (scriptId: string, userId: string) => ({})\n};' > src/lib/api/ratings.ts
# Mock analytics API with complete interface
RUN printf 'export interface AnalyticsFilters {\n startDate?: Date;\n endDate?: Date;\n}\nexport const analyticsApi = {\n trackEvent: async (data: any) => ({}),\n getAnalytics: async () => ({ views: [], downloads: [], topScripts: [], userGrowth: [] }),\n getAnalyticsEvents: async (filters?: AnalyticsFilters) => [],\n getScriptAnalytics: async (scriptId: string) => ({ views: [], downloads: [] }),\n getPlatformAnalytics: async () => ({ totalUsers: 0, totalScripts: 0 }),\n getUserAnalytics: async (userId: string) => ({ views: [], downloads: [] })\n};' > src/lib/api/analytics.ts
# Mock collections API with complete interface
RUN printf 'export interface UpdateCollectionData {\n name?: string;\n description?: string;\n}\nexport const collectionsApi = {\n getCollections: async () => [],\n getCollectionById: async (id: string) => null,\n getUserCollections: async (userId: string) => [],\n getPublicCollections: async () => [],\n createCollection: async (data: any) => ({ id: "mock" }),\n updateCollection: async (id: string, data: UpdateCollectionData) => ({ id }),\n deleteCollection: async (id: string) => ({}),\n addScriptToCollection: async (collectionId: string, scriptId: string) => ({}),\n removeScriptFromCollection: async (collectionId: string, scriptId: string) => ({}),\n isScriptInCollection: async (collectionId: string, scriptId: string) => false\n};' > src/lib/api/collections.ts
# Mock users API with complete interface
RUN printf 'export interface UpdateUserData {\n username?: string;\n displayName?: string;\n bio?: string;\n}\nexport const usersApi = {\n getUser: async (id: string) => null,\n getUserById: async (id: string) => null,\n getAllUsers: async () => [],\n searchUsers: async (query: string) => [],\n createUser: async (data: any) => ({ id: "mock" }),\n updateUser: async (id: string, data: UpdateUserData) => ({ id }),\n updateUserPermissions: async (id: string, permissions: any) => ({ id })\n};' > src/lib/api/users.ts
# Build the application (frontend only with mocks)
RUN npm run build
# Verify build output exists
RUN ls -la /app/dist
# Production stage
FROM nginx:alpine
# Install curl for health checks
RUN apk add --no-cache curl
# Copy built files from builder stage
COPY --from=builder /app/dist /usr/share/nginx/html
# Copy nginx configuration
COPY nginx.conf /etc/nginx/nginx.conf
# Create nginx pid directory
RUN mkdir -p /var/run/nginx
# Set proper permissions
RUN chown -R nginx:nginx /usr/share/nginx/html
RUN chown -R nginx:nginx /var/cache/nginx
RUN chown -R nginx:nginx /var/log/nginx
RUN chown -R nginx:nginx /var/run/nginx
# Switch to non-root user for security
USER nginx
# Expose port 80
EXPOSE 80
# Add healthcheck
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD curl -f http://localhost/health || exit 1
# Start nginx
CMD ["nginx", "-g", "daemon off;"]