Skip to content

Commit 5807f7b

Browse files
authored
Merge pull request #6 from tobenot/cursor/design-flexible-backend-project-structure-649c
Design flexible backend project structure
2 parents a402daa + 0b9f766 commit 5807f7b

File tree

12 files changed

+272
-3
lines changed

12 files changed

+272
-3
lines changed

.github/README_TEMPLATE.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
This file exists in the template to remind consumers of the template update workflow.
2+
3+
- Update via `scripts/update-from-template.sh`
4+
- See `MIGRATION.md` for controlled areas and safe-overwrite guidance.
5+
- Non-blocking pre-commit reminders can be installed via `bash scripts/install-git-hooks.sh`.

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,4 +142,8 @@ vite.config.ts.timestamp-*
142142
/prisma/dev.db
143143
/prisma/dev.db-journal
144144
# Keep migrations in version control
145-
!/prisma/migrations
145+
!/prisma/migrations
146+
147+
# Template update cache and backups
148+
.template-cache/
149+
.template-backup/

MIGRATION.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
## Template Migration Guide
2+
3+
This repository is used as a Git template. Projects may selectively copy updates.
4+
5+
### Controlled (safe-to-overwrite) areas
6+
- `.github/`
7+
- `scripts/`
8+
- `src/framework/`
9+
- `src/server.ts` (only the marked template blocks)
10+
- `tsconfig.json`
11+
- `vercel.json`
12+
- Documentation files: `README.md`, `CONFIGURATION.md`, `CORS_GUIDE.md`, `CORS_SOLUTION_SUMMARY.md`
13+
14+
Business-specific code should live outside these areas (e.g. `src/app/**`, `src/modules/**`).
15+
16+
### Updating from template
17+
- Preview changes:
18+
```bash
19+
bash scripts/update-from-template.sh # latest
20+
bash scripts/update-from-template.sh vX.Y.Z # specific tag
21+
```
22+
- Apply with backup:
23+
```bash
24+
APPLY=1 bash scripts/update-from-template.sh vX.Y.Z
25+
```
26+
Backups are stored in `.template-backup/vX.Y.Z/`.
27+
28+
- The current project records template source in `template.lock`.
29+
30+
### Editing template-controlled code in a project
31+
If you need to modify controlled areas for a project, prefer sending changes back to the template and then updating via the script. A non-blocking pre-commit hook will remind you when you modify controlled paths.
32+
33+
### Template blocks in files
34+
For files like `src/server.ts`, only the code inside the `// <template:...>` blocks is intended to be overwritten by template updates. Avoid custom edits within these blocks.

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,4 +179,16 @@ If you have any questions or need help, please:
179179

180180
**Made with ❤️ by [tobenot](https://github.com/tobenot)**
181181

182-
*This project is maintained as an independent open-source effort to provide a solid foundation for web game backends.*
182+
*This project is maintained as an independent open-source effort to provide a solid foundation for web game backends.*
183+
184+
## Template Updates
185+
186+
- Use the bundled script to preview/apply template updates:
187+
- Preview latest: `bash scripts/update-from-template.sh`
188+
- Apply a specific tag with backup: `APPLY=1 bash scripts/update-from-template.sh vX.Y.Z`
189+
- The template source and version are recorded in `template.lock`.
190+
- Install a non-blocking pre-commit reminder:
191+
```bash
192+
bash scripts/install-git-hooks.sh
193+
```
194+
- Controlled areas (prefer updating via template): `.github/`, `scripts/`, `src/framework/`, template-marked blocks in `src/server.ts`, `tsconfig.json`, `vercel.json`, and docs listed in `MIGRATION.md`.

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
"version:minor": "node scripts/update-version.js minor",
2121
"version:major": "node scripts/update-version.js major",
2222
"get-next-version": "node scripts/get-next-version.js",
23+
"template:update": "bash scripts/update-from-template.sh",
24+
"template:apply": "APPLY=1 bash scripts/update-from-template.sh",
25+
"template:apply:blocks": "APPLY=1 BLOCKS_ONLY=1 bash scripts/update-from-template.sh",
26+
"hooks:install": "bash scripts/install-git-hooks.sh",
2327
"test": "echo \"Error: no test specified\" && exit 1"
2428
},
2529
"repository": {

scripts/apply-template-blocks.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#!/usr/bin/env node
2+
const fs = require('fs');
3+
const path = require('path');
4+
5+
function read(file) {
6+
return fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : null;
7+
}
8+
9+
function replaceBlock(target, source, name) {
10+
const start = new RegExp(`// \\<template:${name}\\>`);
11+
const end = new RegExp(`// \\</template:${name}\\>`);
12+
13+
const tStart = target.search(start);
14+
const tEnd = target.search(end);
15+
const sStart = source.search(start);
16+
const sEnd = source.search(end);
17+
18+
if (tStart === -1 || tEnd === -1 || sStart === -1 || sEnd === -1) {
19+
return { content: target, changed: false, reason: 'missing-markers' };
20+
}
21+
22+
const tPre = target.slice(0, tStart);
23+
const tPost = target.slice(target.indexOf('\n', tEnd) + 1 || tEnd + 1);
24+
25+
const sBlockStart = source.indexOf('\n', sStart) + 1;
26+
const sBlockEnd = source.search(end);
27+
const sBlock = source.slice(sBlockStart, sBlockEnd).replace(/^[\n\r]+|[\n\r]+$/g, '') + '\n';
28+
29+
const replaced = tPre + source.slice(sStart, sStart + ('// <template:'.length + name.length + 3)) // keep start marker line format
30+
? tPre + source.match(start)[0] + '\n' + sBlock + source.match(end)[0] + '\n' + tPost
31+
: tPre + source.match(start)[0] + '\n' + sBlock + source.match(end)[0] + '\n' + tPost;
32+
33+
// Simpler: construct explicitly using markers from target to preserve comments spacing
34+
const tStartLine = target.match(start)[0];
35+
const tEndLine = target.match(end)[0];
36+
const result = tPre + tStartLine + '\n' + sBlock + tEndLine + '\n' + tPost;
37+
38+
if (result !== target) {
39+
return { content: result, changed: true };
40+
}
41+
return { content: target, changed: false };
42+
}
43+
44+
function apply(templateFile, projectFile, blocks) {
45+
const src = read(templateFile);
46+
const dst = read(projectFile);
47+
if (!src || !dst) {
48+
console.error(`apply-template-blocks: skip (missing files). template=${!!src} target=${!!dst}`);
49+
process.exit(0);
50+
}
51+
52+
let updated = dst;
53+
let anyChanged = false;
54+
for (const b of blocks) {
55+
const { content, changed } = replaceBlock(updated, src, b);
56+
updated = content;
57+
anyChanged = anyChanged || changed;
58+
}
59+
60+
if (process.env.DRY_RUN === '1') {
61+
if (anyChanged) {
62+
console.log(`Would update blocks [${blocks.join(', ')}] in ${projectFile}`);
63+
} else {
64+
console.log(`No block changes needed for ${projectFile}`);
65+
}
66+
return;
67+
}
68+
69+
if (anyChanged) {
70+
fs.writeFileSync(projectFile, updated, 'utf8');
71+
console.log(`Updated ${projectFile} blocks: ${blocks.join(', ')}`);
72+
} else {
73+
console.log(`No changes applied to ${projectFile}`);
74+
}
75+
}
76+
77+
function main() {
78+
const [templateFile, projectFile] = process.argv.slice(2);
79+
if (!templateFile || !projectFile) {
80+
console.error('Usage: node scripts/apply-template-blocks.js <templateFile> <projectFile>');
81+
process.exit(1);
82+
}
83+
apply(templateFile, projectFile, ['bootstrap', 'startup']);
84+
}
85+
86+
if (require.main === module) {
87+
main();
88+
}

scripts/git-hooks/pre-commit

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/usr/bin/env bash
2+
# Non-blocking pre-commit tip for template-controlled areas
3+
4+
STAGED=$(git diff --name-only --cached)
5+
WARN=0
6+
7+
if echo "$STAGED" | grep -E '^(scripts/|src/framework/|\.github/|tsconfig\.json|vercel\.json)$' >/dev/null; then
8+
WARN=1
9+
fi
10+
if echo "$STAGED" | grep -E '^src/server\.ts$' >/dev/null; then
11+
WARN=1
12+
fi
13+
14+
if [ "$WARN" -eq 1 ]; then
15+
echo "\n⚠️ Heads-up: You're committing changes in template-controlled areas."
16+
echo " Consider applying such changes in the template repo and syncing via scripts/update-from-template.sh."
17+
echo " This is only a reminder; commit will proceed.\n"
18+
fi
19+
20+
exit 0

scripts/install-git-hooks.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
mkdir -p .git/hooks
5+
cp -f scripts/git-hooks/pre-commit .git/hooks/pre-commit
6+
chmod +x .git/hooks/pre-commit
7+
8+
echo "Installed non-blocking pre-commit hook."

scripts/update-from-template.sh

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#!/usr/bin/env bash
2+
# Lightweight template updater: preview with dry-run and apply with backup
3+
# Usage:
4+
# bash scripts/update-from-template.sh # preview latest
5+
# APPLY=1 bash scripts/update-from-template.sh # apply latest
6+
# APPLY=1 bash scripts/update-from-template.sh v1.2.3 # apply specific tag
7+
# Env:
8+
# BLOCKS_ONLY=1 # when applying, only update template blocks in certain files (e.g., src/server.ts)
9+
10+
set -euo pipefail
11+
12+
ROOT=$(pwd)
13+
CACHE="$ROOT/.template-cache"
14+
LOCK="$ROOT/template.lock"
15+
16+
# shellcheck disable=SC1090
17+
source "$LOCK" 2>/dev/null || true
18+
REPO="${repo:-https://github.com/tobenot/Basic-Web-Game-Backend}"
19+
TARGET="${1:-${version:-latest}}"
20+
21+
if [ ! -d "$CACHE" ]; then
22+
git clone --depth=1 "$REPO" "$CACHE"
23+
fi
24+
25+
git -C "$CACHE" fetch --tags --depth=1 origin
26+
27+
if [ "$TARGET" = "latest" ]; then
28+
LATEST_TAG=$(git -C "$CACHE" tag --sort=-v:refname | head -n1)
29+
if [ -z "$LATEST_TAG" ]; then
30+
TARGET=$(git -C "$CACHE" rev-parse --short HEAD)
31+
else
32+
TARGET="$LATEST_TAG"
33+
fi
34+
fi
35+
36+
echo "Using $REPO@$TARGET"
37+
38+
git -C "$CACHE" checkout --force "$TARGET" >/dev/null 2>&1 || true
39+
40+
# Preview changes
41+
RSYNC_COMMON=(
42+
-av
43+
)
44+
45+
INCLUDES=(
46+
--include='.github/***'
47+
--include='scripts/***'
48+
--include='src/framework/***'
49+
--include='src/server.ts'
50+
--include='tsconfig.json'
51+
--include='vercel.json'
52+
--include='README.md'
53+
--include='CONFIGURATION.md'
54+
--include='CORS_GUIDE.md'
55+
--include='CORS_SOLUTION_SUMMARY.md'
56+
--exclude='*'
57+
)
58+
59+
if [[ "${APPLY:-0}" = "1" ]]; then
60+
echo "Applying updates..."
61+
mkdir -p .template-backup/"$TARGET"
62+
if [[ "${BLOCKS_ONLY:-0}" = "1" ]]; then
63+
echo "BLOCKS_ONLY=1 -> replacing template blocks in specific files"
64+
# Backup target file
65+
mkdir -p ".template-backup/$TARGET/src"
66+
cp -a src/server.ts ".template-backup/$TARGET/src/server.ts" || true
67+
node scripts/apply-template-blocks.js "$CACHE/src/server.ts" "src/server.ts"
68+
printf "repo=%s\nversion=%s\n" "$REPO" "$TARGET" > "$LOCK"
69+
echo "Applied block updates. Backup at .template-backup/$TARGET"
70+
else
71+
rsync "${RSYNC_COMMON[@]}" \
72+
--delete \
73+
--backup --backup-dir=".template-backup/$TARGET" \
74+
"${INCLUDES[@]}" \
75+
"$CACHE"/ "$ROOT"/
76+
printf "repo=%s\nversion=%s\n" "$REPO" "$TARGET" > "$LOCK"
77+
echo "Applied $TARGET. Backup stored in .template-backup/$TARGET"
78+
fi
79+
else
80+
echo "Previewing changes (no files will be modified). Set APPLY=1 to apply."
81+
rsync -avnc --delete \
82+
"${INCLUDES[@]}" \
83+
"$CACHE"/ "$ROOT"/
84+
fi

src/framework/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
This directory hosts framework/bootstrap code managed by the template.
2+
3+
- Keep project-specific business logic in `src/app/**` or `src/modules/**`.
4+
- Changes here should usually be done in the template repository and synced into projects using `scripts/update-from-template.sh`.

0 commit comments

Comments
 (0)