create-dwy 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/bin/index.js +31 -0
  2. package/package.json +29 -0
  3. package/src/create.js +93 -0
  4. package/src/sync.js +75 -0
  5. package/src/utils.js +61 -0
  6. package/templates/claude-global/commands/.gitkeep +0 -0
  7. package/templates/claude-global/hooks/.gitkeep +0 -0
  8. package/templates/claude-global/rules/git-security.md +144 -0
  9. package/templates/claude-global/rules/python-code-style.md +875 -0
  10. package/templates/claude-global/rules/server-security.md +376 -0
  11. package/templates/claude-global/skills/.gitkeep +0 -0
  12. package/templates/project/web/.claude/commands/deploy.md +4 -0
  13. package/templates/project/web/.dwy.hbs +5 -0
  14. package/templates/project/web/CLAUDE.md.hbs +52 -0
  15. package/templates/project/web/backend/.python-version +1 -0
  16. package/templates/project/web/backend/Dockerfile +17 -0
  17. package/templates/project/web/backend/alembic/env.py.hbs +44 -0
  18. package/templates/project/web/backend/alembic/script.py.mako +24 -0
  19. package/templates/project/web/backend/alembic/versions/.gitkeep +0 -0
  20. package/templates/project/web/backend/alembic.ini.hbs +36 -0
  21. package/templates/project/web/backend/pyproject.toml.hbs +52 -0
  22. package/templates/project/web/backend/src/app/__init__.py +0 -0
  23. package/templates/project/web/backend/src/app/config.py.hbs +12 -0
  24. package/templates/project/web/backend/src/app/main.py.hbs +40 -0
  25. package/templates/project/web/backend/src/app/models/__init__.py +0 -0
  26. package/templates/project/web/backend/src/app/models/base.py +5 -0
  27. package/templates/project/web/backend/src/app/routers/__init__.py +0 -0
  28. package/templates/project/web/backend/src/app/routers/health.py +11 -0
  29. package/templates/project/web/backend/src/app/schemas/__init__.py +0 -0
  30. package/templates/project/web/backend/src/app/services/__init__.py +0 -0
  31. package/templates/project/web/backend/tests/__init__.py +0 -0
  32. package/templates/project/web/backend/tests/conftest.py.hbs +16 -0
  33. package/templates/project/web/deploy.sh.hbs +113 -0
  34. package/templates/project/web/docker-compose.dev.yml.hbs +19 -0
  35. package/templates/project/web/docker-compose.prod.yml.hbs +31 -0
  36. package/templates/project/web/frontend/index.html.hbs +12 -0
  37. package/templates/project/web/frontend/package.json.hbs +28 -0
  38. package/templates/project/web/frontend/src/App.vue +3 -0
  39. package/templates/project/web/frontend/src/api/client.ts +13 -0
  40. package/templates/project/web/frontend/src/components/.gitkeep +0 -0
  41. package/templates/project/web/frontend/src/main.ts +10 -0
  42. package/templates/project/web/frontend/src/router/index.ts +14 -0
  43. package/templates/project/web/frontend/src/stores/auth.ts +20 -0
  44. package/templates/project/web/frontend/src/style.css +1 -0
  45. package/templates/project/web/frontend/src/views/HomeView.vue +5 -0
  46. package/templates/project/web/frontend/tsconfig.app.json +14 -0
  47. package/templates/project/web/frontend/tsconfig.json +4 -0
  48. package/templates/project/web/frontend/vite.config.ts.hbs +16 -0
@@ -0,0 +1,11 @@
1
+ """Health check endpoint."""
2
+
3
+ from fastapi import APIRouter
4
+
5
+ router = APIRouter(tags=["health"])
6
+
7
+
8
+ @router.get("/api/health", summary="Health check")
9
+ async def health_check() -> dict:
10
+ """Return service status."""
11
+ return {"status": "ok"}
@@ -0,0 +1,16 @@
1
+ """Test fixtures."""
2
+
3
+ import pytest
4
+ from httpx import ASGITransport, AsyncClient
5
+
6
+ from app.main import app
7
+
8
+
9
+ @pytest.fixture
10
+ async def client():
11
+ """Async HTTP client for testing."""
12
+ async with AsyncClient(
13
+ transport=ASGITransport(app=app),
14
+ base_url="http://test",
15
+ ) as ac:
16
+ yield ac
@@ -0,0 +1,113 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+
4
+ PROJECT_NAME="{{projectName}}"
5
+ SSH_ALIAS="{{sshAlias}}"
6
+ REMOTE_DIR="/opt/{{projectName}}"
7
+ BACKEND_PORT={{backendPort}}
8
+ HEALTH_CHECK_PATH="/api/health"
9
+
10
+ RED='\033[0;31m'
11
+ GREEN='\033[0;32m'
12
+ YELLOW='\033[1;33m'
13
+ NC='\033[0m'
14
+
15
+ log() { echo -e "${GREEN}[DEPLOY]${NC} $1"; }
16
+ warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
17
+ err() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }
18
+
19
+ push_code() {
20
+ log "推送代码到 Gitee..."
21
+ git push origin master || err "git push 失败"
22
+ }
23
+
24
+ pull_remote() {
25
+ log "远程拉取代码..."
26
+ ssh $SSH_ALIAS "cd $REMOTE_DIR && git pull origin master" || err "远程 git pull 失败"
27
+ }
28
+
29
+ build_frontend() {
30
+ log "构建前端..."
31
+ cd frontend && pnpm build || err "前端构建失败"
32
+ cd ..
33
+ }
34
+
35
+ upload_frontend() {
36
+ log "上传前端构建产物..."
37
+ tar czf /tmp/frontend-dist.tar.gz -C frontend dist/
38
+ scp /tmp/frontend-dist.tar.gz $SSH_ALIAS:$REMOTE_DIR/ || err "上传失败"
39
+ ssh $SSH_ALIAS "cd $REMOTE_DIR && mkdir -p frontend && tar xzf frontend-dist.tar.gz -C frontend/ && rm frontend-dist.tar.gz"
40
+ rm /tmp/frontend-dist.tar.gz
41
+ }
42
+
43
+ run_migrations() {
44
+ log "执行数据库迁移..."
45
+ ssh $SSH_ALIAS "cd $REMOTE_DIR && docker compose -f docker-compose.prod.yml run --rm backend alembic upgrade head" || err "迁移失败"
46
+ }
47
+
48
+ restart_backend() {
49
+ log "重建后端容器..."
50
+ ssh $SSH_ALIAS "cd $REMOTE_DIR && docker compose -f docker-compose.prod.yml up -d --build backend"
51
+ }
52
+
53
+ verify() {
54
+ log "验证部署..."
55
+ sleep 3
56
+
57
+ HEALTH=$(ssh $SSH_ALIAS "curl -s http://localhost${HEALTH_CHECK_PATH}" 2>/dev/null)
58
+ if echo "$HEALTH" | grep -q '"ok"'; then
59
+ log "API 健康检查: ✅"
60
+ else
61
+ err "API 健康检查失败: $HEALTH"
62
+ fi
63
+
64
+ GIT_CODE=$(ssh $SSH_ALIAS "curl -s -o /dev/null -w '%{http_code}' http://localhost/.git/config" 2>/dev/null)
65
+ ENV_CODE=$(ssh $SSH_ALIAS "curl -s -o /dev/null -w '%{http_code}' http://localhost/.env" 2>/dev/null)
66
+ if [ "$GIT_CODE" = "404" ] && [ "$ENV_CODE" = "404" ]; then
67
+ log "安全检查: ✅ (.git/.env 已屏蔽)"
68
+ else
69
+ warn "安全检查: .git=$GIT_CODE .env=$ENV_CODE"
70
+ fi
71
+
72
+ log "部署完成!"
73
+ }
74
+
75
+ case "${1:-all}" in
76
+ --backend)
77
+ log "=== 仅部署后端 ==="
78
+ push_code
79
+ pull_remote
80
+ run_migrations
81
+ restart_backend
82
+ verify
83
+ ;;
84
+ --frontend)
85
+ log "=== 仅部署前端 ==="
86
+ build_frontend
87
+ upload_frontend
88
+ verify
89
+ ;;
90
+ --restart)
91
+ log "=== 仅重启容器 ==="
92
+ ssh $SSH_ALIAS "cd $REMOTE_DIR && docker compose -f docker-compose.prod.yml up -d --force-recreate"
93
+ verify
94
+ ;;
95
+ --check)
96
+ log "=== 安全检查 ==="
97
+ verify
98
+ ;;
99
+ all|"")
100
+ log "=== 全量部署 ==="
101
+ push_code
102
+ pull_remote
103
+ build_frontend
104
+ upload_frontend
105
+ run_migrations
106
+ restart_backend
107
+ verify
108
+ ;;
109
+ *)
110
+ echo "用法: ./deploy.sh [--backend|--frontend|--restart|--check]"
111
+ exit 1
112
+ ;;
113
+ esac
@@ -0,0 +1,19 @@
1
+ services:
2
+ db:
3
+ image: postgres:16-alpine
4
+ ports:
5
+ - "127.0.0.1:{{devDbPort}}:5432"
6
+ environment:
7
+ POSTGRES_USER: dev
8
+ POSTGRES_PASSWORD: dev123
9
+ POSTGRES_DB: {{dbName}}
10
+ volumes:
11
+ - pgdata:/var/lib/postgresql/data
12
+
13
+ redis:
14
+ image: redis:7-alpine
15
+ ports:
16
+ - "127.0.0.1:{{devRedisPort}}:6379"
17
+
18
+ volumes:
19
+ pgdata:
@@ -0,0 +1,31 @@
1
+ services:
2
+ backend:
3
+ build: ./backend
4
+ ports:
5
+ - "127.0.0.1:{{backendPort}}:8000"
6
+ env_file: .env
7
+ depends_on:
8
+ db:
9
+ condition: service_healthy
10
+ restart: unless-stopped
11
+
12
+ db:
13
+ image: postgres:16-alpine
14
+ environment:
15
+ POSTGRES_USER: $POSTGRES_USER
16
+ POSTGRES_PASSWORD: $POSTGRES_PASSWORD
17
+ POSTGRES_DB: $POSTGRES_DB
18
+ volumes:
19
+ - pgdata:/var/lib/postgresql/data
20
+ healthcheck:
21
+ test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
22
+ interval: 5s
23
+ retries: 5
24
+ restart: unless-stopped
25
+
26
+ redis:
27
+ image: redis:7-alpine
28
+ restart: unless-stopped
29
+
30
+ volumes:
31
+ pgdata:
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>{{projectName}}</title>
7
+ </head>
8
+ <body>
9
+ <div id="app"></div>
10
+ <script type="module" src="/src/main.ts"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "{{projectName}}-frontend",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vue-tsc -b && vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "@danweiyuan/core": "^0.1.0",
13
+ "@danweiyuan/eui": "^0.2.0",
14
+ "axios": "^1.13.0",
15
+ "lucide-vue-next": "^0.500.0",
16
+ "pinia": "^3.0.0",
17
+ "vue": "^3.5.0",
18
+ "vue-router": "^4.6.0"
19
+ },
20
+ "devDependencies": {
21
+ "@tailwindcss/vite": "^4.2.0",
22
+ "@vitejs/plugin-vue": "^6.0.0",
23
+ "tailwindcss": "^4.2.0",
24
+ "typescript": "^5.9.0",
25
+ "vite": "^8.0.0",
26
+ "vue-tsc": "^2.0.0"
27
+ }
28
+ }
@@ -0,0 +1,3 @@
1
+ <template>
2
+ <router-view />
3
+ </template>
@@ -0,0 +1,13 @@
1
+ import axios from 'axios'
2
+
3
+ const client = axios.create({ baseURL: '/api' })
4
+
5
+ client.interceptors.request.use((config) => {
6
+ const token = localStorage.getItem('token')
7
+ if (token) {
8
+ config.headers.Authorization = `Bearer ${token}`
9
+ }
10
+ return config
11
+ })
12
+
13
+ export default client
@@ -0,0 +1,10 @@
1
+ import { createApp } from 'vue'
2
+ import { createPinia } from 'pinia'
3
+ import App from './App.vue'
4
+ import router from './router'
5
+ import './style.css'
6
+
7
+ const app = createApp(App)
8
+ app.use(createPinia())
9
+ app.use(router)
10
+ app.mount('#app')
@@ -0,0 +1,14 @@
1
+ import { createRouter, createWebHistory } from 'vue-router'
2
+
3
+ const router = createRouter({
4
+ history: createWebHistory(),
5
+ routes: [
6
+ {
7
+ path: '/',
8
+ name: 'home',
9
+ component: () => import('../views/HomeView.vue'),
10
+ },
11
+ ],
12
+ })
13
+
14
+ export default router
@@ -0,0 +1,20 @@
1
+ import { defineStore } from 'pinia'
2
+ import { ref } from 'vue'
3
+
4
+ export const useAuthStore = defineStore('auth', () => {
5
+ const token = ref<string | null>(localStorage.getItem('token'))
6
+ const user = ref<Record<string, unknown> | null>(null)
7
+
8
+ function setToken(t: string) {
9
+ token.value = t
10
+ localStorage.setItem('token', t)
11
+ }
12
+
13
+ function logout() {
14
+ token.value = null
15
+ user.value = null
16
+ localStorage.removeItem('token')
17
+ }
18
+
19
+ return { token, user, setToken, logout }
20
+ })
@@ -0,0 +1 @@
1
+ @import "tailwindcss";
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <div class="min-h-screen flex items-center justify-center">
3
+ <h1 class="text-3xl font-bold">Welcome</h1>
4
+ </div>
5
+ </template>
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
7
+ "strict": true,
8
+ "jsx": "preserve",
9
+ "noEmit": true,
10
+ "paths": { "@/*": ["./src/*"] },
11
+ "baseUrl": "."
12
+ },
13
+ "include": ["src/**/*.ts", "src/**/*.vue"]
14
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "files": [],
3
+ "references": [{ "path": "./tsconfig.app.json" }]
4
+ }
@@ -0,0 +1,16 @@
1
+ import { defineConfig } from 'vite'
2
+ import vue from '@vitejs/plugin-vue'
3
+ import tailwindcss from '@tailwindcss/vite'
4
+
5
+ export default defineConfig({
6
+ plugins: [vue(), tailwindcss()],
7
+ server: {
8
+ port: 5173,
9
+ proxy: {
10
+ '/api': {
11
+ target: 'http://127.0.0.1:8000',
12
+ changeOrigin: true,
13
+ },
14
+ },
15
+ },
16
+ })