# Split Monorepo Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Split monorepo into shop-server and shop-client repos on Gitea (192.168.1.110:3000) with shared/ in server repo. **Architecture:** Two independent repos. shared/ lives in shop-server. Client references it via relative alias `../shop-server/shared`. Clean init commits, no filter-branch. **Tech Stack:** git, bash, Gitea at 192.168.1.110:3000 **Prerequisite:** Создать пустые репозитории в Gitea через веб-интерфейс: - http://192.168.1.110:3000/admin/shop-server.git - http://192.168.1.110:3000/admin/shop-client.git --- ### Task 1: Prepare workspace **Files:** - Create: `/tmp/shop-repos/shop-server/` (empty dir) - Create: `/tmp/shop-repos/shop-client/` (empty dir) - [ ] **Step 1: Create workspace directories** ```bash mkdir -p /tmp/shop-repos/shop-server /tmp/shop-repos/shop-client ``` --- ### Task 2: shop-server — .gitignore **Files:** - Create: `/tmp/shop-repos/shop-server/.gitignore` - [ ] **Step 1: Write .gitignore** ``` node_modules dist *.log .env scripts/deploy.env server/prisma/dev.db server/prisma/dev.db-journal server/uploads/ uploads/.cache/ ``` - [ ] **Step 2: Copy server/, shared/, scripts/** ```bash mkdir -p /tmp/shop-repos/shop-server/scripts cp -r /mnt/d/my_projects/shop_deploy/server /tmp/shop-repos/shop-server/server cp -r /mnt/d/my_projects/shop_deploy/shared /tmp/shop-repos/shop-server/shared cp /mnt/d/my_projects/shop_deploy/scripts/backup-db.sh /tmp/shop-repos/shop-server/scripts/backup-db.sh cp /mnt/d/my_projects/shop_deploy/scripts/craftshop-backup.service /tmp/shop-repos/shop-server/scripts/craftshop-backup.service cp /mnt/d/my_projects/shop_deploy/scripts/craftshop-backup.timer /tmp/shop-repos/shop-server/scripts/craftshop-backup.timer cp /mnt/d/my_projects/shop_deploy/scripts/SERVER_SETUP.md /tmp/shop-repos/shop-server/scripts/SERVER_SETUP.md cp /mnt/d/my_projects/shop_deploy/scripts/craftshop-netbird.conf /tmp/shop-repos/shop-server/scripts/craftshop-netbird.conf ``` --- ### Task 3: shop-server — README.md + AGENTS.md **Files:** - Create: `/tmp/shop-repos/shop-server/README.md` - Create: `/tmp/shop-repos/shop-server/AGENTS.md` - [ ] **Step 1: Write README.md** ``` # shop-server — бэкенд магазина ручной работы Fastify + Prisma + SQLite. API, админка, загрузка изображений. ## Запуск cd server cp .env.example .env npm ci npx prisma migrate dev npm run dev Сервер: http://127.0.0.1:3333. Health: GET /health. ## Деплой См. scripts/SERVER_SETUP.md. ``` - [ ] **Step 2: Write AGENTS.md** ``` # AGENTS.md — shop-server ## Project structure - `server/` — Fastify + Prisma + SQLite backend - `shared/constants/` — JS + .d.ts shared with client (order statuses, delivery carriers, payment methods, upload limits) ## Developer commands | Command | What it does | |---|---| | `npm run dev` | node --env-file=.env --watch src/index.js (requires Node 20.6+) | | `npm run dev:classic` | node --watch src/index.js (loads .env via dotenv) | | `npm run lint` | ESLint (flat config) | | `npm run lint:fix` | ESLint with --fix | | `npm run format` | Prettier write all | | `npm run format:check` | Prettier check only | | `npm test` | vitest run | | `npm run db:reset:test` | Reset SQLite DB + re-run migrations + seed (uses .env) | ## Conventions - **Language**: Отвечай пользователю на русском. - **Single quotes**, no semicolons, trailing commas, 120 print width (Prettier + ESLint enforce). - **Alias**: @shared → shared/ (configured in vitest.config.js for tests). - **Admin access**: Only users with email matching ADMIN_EMAIL env var can access admin routes. Server auto-creates the admin user on startup. - **Server helpers**: slugify, parseMaterialsInput, mapProductForApi are decorated on fastify instance, accessed via request.server.*. ## Testing - Vitest with globals enabled. - Test files live in __tests__/ directories next to the code they test. ## OAuth - VK callback: {SERVER_PUBLIC_URL}/api/auth/oauth/vk/callback - Yandex callback: {SERVER_PUBLIC_URL}/api/auth/oauth/yandex/callback ## Infrastructure (deployment) - VPS runs Nginx Proxy Manager (NPM), connected via Netbird VPN to the server machine - Server machine runs the project - Traffic flow: Browser → Domain → VPS (NPM) → Netbird → Server machine (3333) - trustProxy: true on Fastify ## Notable quirks - .env is gitignored. Copy .env.example to .env for local dev. - db:reset:test runs prisma migrate reset --force, which destroys all data. ``` --- ### Task 4: shop-server — git init, commit, push **Files:** - None new - [ ] **Step 1: Init git repo and commit** ```bash cd /tmp/shop-repos/shop-server git init git add -A git commit -m "initial: server + shared" ``` - [ ] **Step 2: Add remote and push** ```bash cd /tmp/shop-repos/shop-server git remote add origin http://192.168.1.110:3000/admin/shop-server.git git push -u origin main ``` Expected: push successful, repo populated on Gitea. --- ### Task 5: shop-client — .gitignore **Files:** - Create: `/tmp/shop-repos/shop-client/.gitignore` - [ ] **Step 1: Write .gitignore** ``` node_modules dist dist-ssr *.log *.local .env .vscode/* !.vscode/extensions.json .idea .DS_Store *.suo *.ntvs* *.njsproj *.sln *.sw? ``` - [ ] **Step 2: Copy client/ directory** ```bash cp -r /mnt/d/my_projects/shop_deploy/client /tmp/shop-repos/shop-client/client ``` --- ### Task 6: shop-client — fix shared/ alias **Files:** - Modify: `/tmp/shop-repos/shop-client/client/vite.config.ts:6-15` - Modify: `/tmp/shop-repos/shop-client/client/tsconfig.app.json:3-7` - [ ] **Step 1: Fix vite.config.ts** Change lines 6-7, 15: Old: ```ts const rootDir = fileURLToPath(new URL('.', import.meta.url)) const projectRoot = path.resolve(rootDir, '..') // ... '@shared': path.resolve(projectRoot, 'shared'), ``` New: ```ts const rootDir = fileURLToPath(new URL('.', import.meta.url)) const projectRoot = path.resolve(rootDir, '..', '..', 'shop-server') // ... '@shared': path.resolve(projectRoot, 'shared'), ``` Full file after: ```ts import path from 'node:path' import { fileURLToPath } from 'node:url' import react from '@vitejs/plugin-react' import { defineConfig } from 'vite' const rootDir = fileURLToPath(new URL('.', import.meta.url)) const projectRoot = path.resolve(rootDir, '..', '..', 'shop-server') // https://vite.dev/config/ export default defineConfig({ plugins: [react()], resolve: { alias: { '@': path.resolve(rootDir, 'src'), '@shared': path.resolve(projectRoot, 'shared'), }, }, server: { fs: { allow: [path.resolve(rootDir, '..'), projectRoot], }, port: 5173, proxy: { '/api': { target: 'http://127.0.0.1:3333', changeOrigin: true, }, '/uploads': { target: 'http://127.0.0.1:3333', changeOrigin: true, }, '/uploads-resized': { target: 'http://127.0.0.1:3333', changeOrigin: true, }, }, }, build: { rollupOptions: { output: { manualChunks(id) { if (id.includes('node_modules')) { if (id.includes('react-router')) return 'vendor-react' if (id.includes('react-dom')) return 'vendor-react' if (id.includes('node_modules/react/')) return 'vendor-react' if (id.includes('@mui')) return 'vendor-mui' if (id.includes('@emotion')) return 'vendor-mui' if (id.includes('swiper')) return 'vendor-swiper' if (id.includes('@tanstack/react-query')) return 'vendor-query' if (id.includes('effector')) return 'vendor-effector' } }, }, }, }, }) ``` - [ ] **Step 2: Fix tsconfig.app.json** Change line 6: Old: ```json "@shared/*": ["../shared/*"] ``` New: ```json "@shared/*": ["../../shop-server/shared/*"] ``` Full file after: ```json { "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["src/*"], "@shared/*": ["../../shop-server/shared/*"] }, "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", "target": "es2023", "lib": ["ES2023", "DOM"], "module": "esnext", "types": ["vite/client"], "skipLibCheck": true, "strict": true, /* Bundler mode */ "moduleResolution": "bundler", "allowImportingTsExtensions": true, "verbatimModuleSyntax": true, "moduleDetection": "force", "noEmit": true, "jsx": "react-jsx", "ignoreDeprecations": "6.0", /* Linting */ "noUnusedLocals": true, "noUnusedParameters": true, "erasableSyntaxOnly": true, "noFallthroughCasesInSwitch": true }, "include": ["src"] } ``` --- ### Task 7: shop-client — README.md + AGENTS.md **Files:** - Create: `/tmp/shop-repos/shop-client/README.md` - Create: `/tmp/shop-repos/shop-client/AGENTS.md` - [ ] **Step 1: Write README.md** ``` # shop-client — витрина и админка магазина ручной работы React + Vite + TypeScript + MUI. FSD-архитектура, @tanstack/react-query. ## Запуск npm ci npm run dev Откройте http://localhost:5173. API проксируется на http://127.0.0.1:3333. Требуется shop-server рядом (../../shop-server/shared/ для @shared/ alias). ## Команды | Command | Description | |---|---| | npm run dev | Vite dev server | | npm run build | tsc + vite build | | npm run lint | ESLint | | npm run lint:fix | ESLint --fix | | npm run format | Prettier | | npm test | vitest run | ``` - [ ] **Step 2: Write AGENTS.md** ``` # AGENTS.md — shop-client ## Project structure FSD architecture: app/pages/widgets/features/entities/shared ## Developer commands | Command | What it does | |---|---| | `npm run dev` | Vite dev server on :5173, proxies /api and /uploads to 127.0.0.1:3333 | | `npm run build` | Runs tsc -b first, then vite build | | `npm run lint` | ESLint (flat config) | | `npm run lint:fix` | ESLint with --fix | | `npm run format` | Prettier write all | | `npm run format:check` | Prettier check only | | `npm test` | vitest run | | `npm run test:watch` | vitest watch mode | ## Conventions - **Language**: Отвечай пользователю на русском. - **Single quotes**, no semicolons, trailing commas, 120 print width (Prettier + ESLint enforce). - **FSD import boundaries** enforced by eslint-plugin-boundaries. Lower layers cannot import upper layers. - **Aliases**: @/ → client/src/, @shared/ → ../../shop-server/shared/ - **API requests**: Use apiClient (axios wrapper from shared/api/) with @tanstack/react-query. Invalidate queries after mutations. - **UI**: Prefer MUI components over custom HTML/CSS. - **no-console**: ESLint error; use console.warn/error/info only. ## Testing - vitest + jsdom + @testing-library/react - Setup file: src/testing/setup.ts - Test files live in __tests__/ directories next to the code they test. ## Important - shop-server must be cloned alongside shop-client for @shared/ alias to resolve. - Vite dev server relies on backend running at 127.0.0.1:3333. Start server first. ``` --- ### Task 8: shop-client — git init, commit, push **Files:** - None new - [ ] **Step 1: Init git repo and commit** ```bash cd /tmp/shop-repos/shop-client git init git add -A git commit -m "initial: client" ``` - [ ] **Step 2: Add remote and push** ```bash cd /tmp/shop-repos/shop-client git remote add origin http://192.168.1.110:3000/admin/shop-client.git git push -u origin main ``` Expected: push successful, repo populated on Gitea. --- ### Task 9: Verify **Files:** - None - [ ] **Step 1: Clone fresh and verify structure** ```bash cd /tmp git clone http://192.168.1.110:3000/admin/shop-server.git shop-server-test git clone http://192.168.1.110:3000/admin/shop-client.git shop-client-test ls shop-server-test/server/ ls shop-server-test/shared/constants/ ls shop-client-test/client/src/ ``` Expected: all directories present. - [ ] **Step 2: Verify client can resolve @shared alias (TypeScript)** ```bash cd /tmp/shop-client-test/client npm ci npx tsc --noEmit -p tsconfig.app.json 2>&1 | head -20 ``` Expected: no errors related to @shared/ imports. May have unrelated warnings — ignore those. - [ ] **Step 3: Cleanup test dirs** ```bash rm -rf /tmp/shop-repos /tmp/shop-server-test /tmp/shop-client-test ```