вы
This commit is contained in:
@@ -15,3 +15,4 @@ server/uploads/
|
||||
|
||||
# Plans and design docs
|
||||
.agents
|
||||
server/prisma/prisma/dev.db
|
||||
|
||||
Generated
+71
-3
@@ -708,9 +708,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/wasi-threads": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
|
||||
"integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz",
|
||||
"integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
@@ -2214,6 +2214,40 @@
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/core": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
|
||||
"integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/wasi-threads": "1.2.1",
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
|
||||
"integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
|
||||
"integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.2.tgz",
|
||||
@@ -3826,6 +3860,40 @@
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-wasm32-wasi/node_modules/@emnapi/core": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
|
||||
"integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/wasi-threads": "1.2.1",
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-wasm32-wasi/node_modules/@emnapi/runtime": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
|
||||
"integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
|
||||
"integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@unrs/resolver-binding-win32-arm64-msvc": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz",
|
||||
|
||||
@@ -0,0 +1,485 @@
|
||||
# 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
|
||||
```
|
||||
@@ -0,0 +1,111 @@
|
||||
# Split monorepo into shop-server & shop-client
|
||||
|
||||
> Дата: 2026-06-11
|
||||
|
||||
## Цель
|
||||
|
||||
Разделить монорепу `shop_deploy` на два независимых git-репозитория, размещённых на Gitea (192.168.1.110:3000), с автоматическим деплоем через Gitea CI/CD.
|
||||
|
||||
## Репозитории
|
||||
|
||||
### shop-server
|
||||
|
||||
**URL:** `http://192.168.1.110:3000/admin/shop-server.git`
|
||||
|
||||
**Содержимое:**
|
||||
```
|
||||
.gitignore
|
||||
README.md
|
||||
AGENTS.md
|
||||
.gitea/workflows/deploy.yml
|
||||
server/ # Fastify + Prisma backend
|
||||
shared/ # Каноничное место shared/constants/
|
||||
scripts/
|
||||
backup-db.sh
|
||||
craftshop-backup.service
|
||||
craftshop-backup.timer
|
||||
SERVER_SETUP.md
|
||||
```
|
||||
|
||||
### shop-client
|
||||
|
||||
**URL:** `http://192.168.1.110:3000/admin/shop-client.git`
|
||||
|
||||
**Содержимое:**
|
||||
```
|
||||
.gitignore
|
||||
README.md
|
||||
AGENTS.md
|
||||
.gitea/workflows/deploy.yml
|
||||
client/ # React + Vite frontend
|
||||
```
|
||||
|
||||
**Не переносятся** в новые репо: `.cursor/`, `.opencode/`, `.agents/`, `REFACTORING_PLAN.md`, `skills-lock.json`, `node_modules/`, `docs/`, `.superpowers/` — остаются в исторической монорепе.
|
||||
|
||||
## Доступ к shared/
|
||||
|
||||
`shared/` живёт в **shop-server** как каноничный источник.
|
||||
|
||||
### shop-client: alias на shared/
|
||||
|
||||
`client/vite.config.ts`:
|
||||
```ts
|
||||
'@shared': path.resolve(projectRoot, '..', 'shop-server', 'shared'),
|
||||
```
|
||||
|
||||
`client/tsconfig.app.json`:
|
||||
```json
|
||||
"@shared/*": ["../../shop-server/shared/*"]
|
||||
```
|
||||
|
||||
Это работает при условии, что оба репо клонированы рядом:
|
||||
```
|
||||
~/projects/
|
||||
shop-server/
|
||||
server/ shared/
|
||||
shop-client/
|
||||
client/
|
||||
```
|
||||
|
||||
В CI Gitea делает checkout обоих репо в одной workspace.
|
||||
|
||||
### shop-server
|
||||
|
||||
Изменений не требуется — `shared/` уже внутри репо. Сервер импортирует `shared/` через относительные пути (`../../shared/...`).
|
||||
|
||||
## CI/CD (Gitea Actions)
|
||||
|
||||
Runner: self-hosted на машине, где работает приложение (192.168.1.88 или аналог). Триггер: push в `main`.
|
||||
|
||||
### shop-server CI (.gitea/workflows/deploy.yml)
|
||||
|
||||
1. `actions/checkout` shop-server
|
||||
2. `npm ci` → `npx prisma generate` → `npm test`
|
||||
3. Rsync `server/` → `/opt/craftshop/server/` (исключая node_modules, uploads, .env, *.db)
|
||||
4. Rsync `shared/` → `/opt/craftshop/shared/`
|
||||
5. `npx prisma migrate deploy` на сервере
|
||||
6. `systemctl restart craftshop-api`
|
||||
|
||||
### shop-client CI (.gitea/workflows/deploy.yml)
|
||||
|
||||
1. `actions/checkout` shop-client
|
||||
2. `actions/checkout` shop-server в `../shop-server/`
|
||||
3. `npm ci` → `npm test` → `npm run build`
|
||||
4. Rsync `client/dist/` → `/opt/craftshop/www/`
|
||||
|
||||
## Что удаляется
|
||||
|
||||
- `deploy-auto.sh` — заменён Gitea CI
|
||||
- `.deployed-commit` — отслеживание заменено на push-триггер
|
||||
- `scripts/deploy.env` — не нужен (CI конфигурируется в Gitea)
|
||||
- `scripts/craftshop-netbird.conf` — nginx настраивается один раз по `SERVER_SETUP.md`
|
||||
|
||||
## Этапы реализации
|
||||
|
||||
1. Создать пустые репо в Gitea
|
||||
2. Инициализировать shop-server: скопировать server/, shared/, scripts/ в чистый git-репо, сделать init-коммит, запушить
|
||||
3. Инициализировать shop-client: скопировать client/ в чистый git-репо, поправить alias на shared/, сделать init-коммит, запушить
|
||||
4. Настроить self-hosted runner на целевой машине
|
||||
5. Создать `.gitea/workflows/deploy.yml` в каждом репо
|
||||
6. Проверить деплой через push
|
||||
7. Удалить deploy-скрипты из исторической монорепы (опционально)
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
Выполнять от **root** на свежем Debian/Ubuntu LXC.
|
||||
|
||||
для теста
|
||||
|
||||
---
|
||||
|
||||
## 1. Базовые пакеты и Node.js
|
||||
|
||||
Generated
+596
-631
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -30,7 +30,7 @@
|
||||
"dotenv": "^17.4.2",
|
||||
"fastify": "^5.8.5",
|
||||
"nodemailer": "^8.0.7",
|
||||
"sharp": "0.32.6"
|
||||
"sharp": "^0.35.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^10.0.1",
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user