diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..cbae169 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +node_modules +dist +.git +*.log +apps/web/dist +apps/server/dist +apps/server/uploads +.vscode +.env diff --git a/Dockerfile.server b/Dockerfile.server new file mode 100644 index 0000000..b213382 --- /dev/null +++ b/Dockerfile.server @@ -0,0 +1,28 @@ +FROM node:20-alpine + +WORKDIR /app + +# Install dependencies at the root +COPY package*.json ./ +COPY apps/server/package*.json ./apps/server/ +COPY apps/web/package*.json ./apps/web/ + +RUN npm install + +# Copy source and prisma +COPY apps/server ./apps/server + +# Generate Prisma client +WORKDIR /app/apps/server +RUN npx prisma generate + +# Build server +RUN npm run build + +# Make start script executable +RUN chmod +x ./start.sh + +EXPOSE 3001 + +# Run the server using the start script +CMD ["./start.sh"] diff --git a/Dockerfile.web b/Dockerfile.web new file mode 100644 index 0000000..3f1402b --- /dev/null +++ b/Dockerfile.web @@ -0,0 +1,31 @@ +# Stage 1: Build React app +FROM node:20-alpine AS build + +WORKDIR /app + +# ROOT package.json +COPY package*.json ./ +COPY apps/server/package*.json ./apps/server/ +COPY apps/web/package*.json ./apps/web/ + +RUN npm install + +# Copy source +COPY . . + +# Build web frontend +WORKDIR /app/apps/web +RUN npm run build + +# Stage 2: Serve with Nginx +FROM nginx:alpine + +# Copy built assets +COPY --from=build /app/apps/web/dist /usr/share/nginx/html + +# Custom Nginx config to proxy /api and /socket.io to server:3001 +COPY docker/nginx/default.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/apps/server/src/routes/auth.ts b/apps/server/src/routes/auth.ts index e34a921..eaa222e 100644 --- a/apps/server/src/routes/auth.ts +++ b/apps/server/src/routes/auth.ts @@ -46,22 +46,26 @@ router.post('/register', registerLimiter, async (req, res) => { } if (!username || !password) { + console.log('Registration failed: missing username or password'); res.status(400).json({ error: 'Username и пароль обязательны' }); return; } if (!/^[a-zA-Z0-9_]{3,20}$/.test(username)) { + console.log('Registration failed: invalid username format', username); res.status(400).json({ error: 'Username: 3-20 символов, только латиница, цифры, _' }); return; } if (password.length < config.minPasswordLength) { + console.log('Registration failed: password too short'); res.status(400).json({ error: `Пароль должен быть не менее ${config.minPasswordLength} символов` }); return; } // Password must contain at least one letter and one digit if (!/[a-zA-Zа-яА-Я]/.test(password) || !/\d/.test(password)) { + console.log('Registration failed: password missing letters or digits'); res.status(400).json({ error: 'Пароль должен содержать буквы и цифры' }); return; } diff --git a/apps/server/start.sh b/apps/server/start.sh new file mode 100644 index 0000000..0fcd89b --- /dev/null +++ b/apps/server/start.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# Wait for DB +echo "Waiting for database..." +# Simple sleep for now, or use docker-compose healthcheck +sleep 5 + +# Create DB schema (push for dev, or migrate deploy for prod) +echo "Running Prisma DB push/seed..." +npx prisma db push --accept-data-loss +npx prisma db seed + +# Start server +echo "Starting server..." +npm start diff --git a/apps/server/uploads/3d03f534-e466-460b-b48d-ff9e94049e9d.jpg b/apps/server/uploads/3d03f534-e466-460b-b48d-ff9e94049e9d.jpg new file mode 100644 index 0000000..7ae04c5 Binary files /dev/null and b/apps/server/uploads/3d03f534-e466-460b-b48d-ff9e94049e9d.jpg differ diff --git a/apps/server/uploads/777a5af2-4064-449d-a659-6b21e6bc48d0.webp b/apps/server/uploads/777a5af2-4064-449d-a659-6b21e6bc48d0.webp new file mode 100644 index 0000000..d07464c Binary files /dev/null and b/apps/server/uploads/777a5af2-4064-449d-a659-6b21e6bc48d0.webp differ diff --git a/apps/server/uploads/7ee26807-9bc0-44c9-b664-b17078a3efd3.avif b/apps/server/uploads/7ee26807-9bc0-44c9-b664-b17078a3efd3.avif new file mode 100644 index 0000000..af597d5 Binary files /dev/null and b/apps/server/uploads/7ee26807-9bc0-44c9-b664-b17078a3efd3.avif differ diff --git a/apps/web/src/lib/i18n.ts b/apps/web/src/lib/i18n.ts index 4888707..e6095f1 100644 --- a/apps/web/src/lib/i18n.ts +++ b/apps/web/src/lib/i18n.ts @@ -198,6 +198,11 @@ const translations = { createAccount: 'Создать аккаунт', testAccounts: 'Тестовые аккаунты: evgeniy, anastasia, artem, polina', passwordForAll: 'Пароль для всех:', + dontHaveAccount: 'Нет аккаунта?', + alreadyHaveAccount: 'Уже есть аккаунт?', + passwordRequirements: 'Минимум 8 символов, буквы и цифры', + registerNow: 'Зарегистрироваться', + loginNow: 'Войти', // File/upload fileTooLarge: 'Файл слишком большой (макс. 50МБ)', dropFileHere: 'Отпустите файл здесь', @@ -439,6 +444,11 @@ const translations = { createAccount: 'Create account', testAccounts: 'Test accounts: evgeniy, anastasia, artem, polina', passwordForAll: 'Password for all:', + dontHaveAccount: "Don't have an account?", + alreadyHaveAccount: 'Already have an account?', + passwordRequirements: 'At least 8 characters, letters and numbers', + registerNow: 'Register', + loginNow: 'Login', fileTooLarge: 'File too large (max 50MB)', dropFileHere: 'Drop file here', uploading: 'Uploading...', diff --git a/apps/web/src/pages/AuthPage.tsx b/apps/web/src/pages/AuthPage.tsx index c96547b..04279e4 100644 --- a/apps/web/src/pages/AuthPage.tsx +++ b/apps/web/src/pages/AuthPage.tsx @@ -58,7 +58,8 @@ export default function AuthPage() { className="relative z-10 w-full max-w-md mx-4" >
{t('modernMessengerShort')}
-+ {isLogin ? t('login') : t('register')} +
+ {isLogin ? t('dontHaveAccount') : t('alreadyHaveAccount')}{' '} + +
+