feat: Implement monorepo-like structure and Cloudflare Pages deployment
Refactor the project to support multiple applications, centralized build process,
and deployment to Cloudflare Pages.
- **Restructuring:**
- Moved static HTML pages (game.html, german.html) and the SnapMark React app source into .
- Moved the main landing page (index.html) into .
- Consolidated output to a directory.
- **Build System:**
- Created to automate:
- Building Vite-based apps (e.g., SnapMark).
- Processing static HTML apps (injecting dynamic navigation).
- Generating static client-side redirects for old URLs (/game.html -> /hexarow/).
- **Navigation:**
- Implemented a dynamic navigation bar across all pages.
- **Deployment:**
- Removed Durable Objects and worker-specific configurations.
- Configured and for Cloudflare Pages deployment.
- Updated
> cffy.in@0.0.1 deploy
> wrangler pages deploy dist --project-name cffyin
⛅️ wrangler 4.54.0
───────────────────
Uploading... (7/7)
✨ Success! Uploaded 0 files (7 already uploaded) (0.34 sec)
🌎 Deploying...
✨ Deployment complete! Take a peek over at https://a00cadbf.cffyin.pages.dev
✨ Deployment alias URL: https://master.cffyin.pages.dev to target the Cloudflare Pages project.
This commit is contained in:
parent
8ed79cbd0b
commit
74388a1669
15
GEMINI.md
Normal file
15
GEMINI.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Project Context
|
||||
|
||||
This app has been modified to be on the skeleton of a cloudflare app so it can be deployed on cloudflare.
|
||||
|
||||
## Status
|
||||
- Fixed `wrangler deploy` error: "Worker written as a service-worker".
|
||||
- Removed Durable Objects and Workers logic to convert to a **Static Cloudflare Pages** site.
|
||||
- Restructured project:
|
||||
- `src/apps/`: Contains all sub-applications (hexarow, german-trainer, snapmark).
|
||||
- `src/landing/`: Contains the main landing page.
|
||||
- `dist/`: Generated build output served by Cloudflare.
|
||||
- Created `scripts/build.js` to manage builds, inject a standard Navigation Bar, and generate static redirects.
|
||||
- Updated `wrangler.jsonc` and `package.json` for Cloudflare Pages deployment.
|
||||
- Successfully deployed to `https://cffyin.pages.dev` (and alias `master.cffyin.pages.dev`).
|
||||
|
||||
4
PLAN.md
Normal file
4
PLAN.md
Normal file
@ -0,0 +1,4 @@
|
||||
# Plan
|
||||
|
||||
- [x] Analyze Logs and Create Documentation <!-- id: 0 -->
|
||||
- [x] Fix Deployment Issues <!-- id: 1 -->
|
||||
1552
package-lock.json
generated
Normal file
1552
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
15
package.json
15
package.json
@ -1,15 +1,16 @@
|
||||
{
|
||||
"name": "muddy-frost-c06d",
|
||||
"version": "0.0.0",
|
||||
"name": "cffy.in",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"deploy": "wrangler deploy",
|
||||
"dev": "wrangler dev",
|
||||
"start": "wrangler dev",
|
||||
"cf-typegen": "wrangler types"
|
||||
"deploy": "wrangler pages deploy dist --project-name cffyin",
|
||||
"dev": "wrangler pages dev dist",
|
||||
"start": "wrangler pages dev dist",
|
||||
"cf-typegen": "wrangler types",
|
||||
"build": "node scripts/build.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.5.2",
|
||||
"wrangler": "^4.54.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
24
public/snapmark/.gitignore
vendored
24
public/snapmark/.gitignore
vendored
@ -1,24 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
149
scripts/build.js
Normal file
149
scripts/build.js
Normal file
@ -0,0 +1,149 @@
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const { execSync } = require('node:child_process');
|
||||
|
||||
const SRC_DIR = path.join(__dirname, '../src');
|
||||
const DIST_DIR = path.join(__dirname, '../dist');
|
||||
const APPS_DIR = path.join(SRC_DIR, 'apps');
|
||||
|
||||
// Standard Navigation Template
|
||||
// Note: We use absolute paths (/) so they work from any depth, assuming Cloudflare Pages root.
|
||||
const NAV_TEMPLATE = `
|
||||
<nav class="bg-white/70 backdrop-blur-sm border-b-2 border-amber-800 p-3 sticky top-0 z-20">
|
||||
<div class="max-w-6xl mx-auto flex justify-between items-center font-impact text-amber-900">
|
||||
<a href="/" class="text-2xl font-black hover:text-amber-700 transition-colors">cffyin</a>
|
||||
<div class="flex gap-6 text-lg items-center">
|
||||
<a href="/" class="hover:underline">Home</a>
|
||||
<a href="/german-trainer/" class="hover:underline">German Trainer</a>
|
||||
<a href="/hexarow/" class="hover:underline">Hex-a-Row</a>
|
||||
<a href="/snapmark/" class="hover:underline">SnapMark</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
`;
|
||||
|
||||
function cleanDist() {
|
||||
if (fs.existsSync(DIST_DIR)) {
|
||||
fs.rmSync(DIST_DIR, { recursive: true, force: true });
|
||||
}
|
||||
fs.mkdirSync(DIST_DIR);
|
||||
}
|
||||
|
||||
function processHtml(filePath, outPath) {
|
||||
let content = fs.readFileSync(filePath, 'utf-8');
|
||||
|
||||
// Simple regex-based injection.
|
||||
// 1. Remove existing <nav>...</nav> (if any) to avoid duplication.
|
||||
// 2. Inject new NAV_TEMPLATE at the start of <body>.
|
||||
|
||||
// Remove existing nav
|
||||
content = content.replace(/<nav[\s\S]*?<\/nav>/i, '');
|
||||
|
||||
// Inject new nav
|
||||
if (content.includes('<body')) {
|
||||
content = content.replace(/<body([^>]*)>/i, `<body$1>
|
||||
${NAV_TEMPLATE}`);
|
||||
} else {
|
||||
// Fallback if no body tag found (unlikely for valid HTML)
|
||||
content = NAV_TEMPLATE + content;
|
||||
}
|
||||
|
||||
fs.writeFileSync(outPath, content);
|
||||
console.log(`Processed ${filePath} -> ${outPath}`);
|
||||
}
|
||||
|
||||
function buildLanding() {
|
||||
const landingSrc = path.join(__dirname, '..', 'src', 'landing', 'index.html');
|
||||
const landingDist = path.join(DIST_DIR, 'index.html');
|
||||
if (fs.existsSync(landingSrc)) {
|
||||
processHtml(landingSrc, landingDist);
|
||||
} else {
|
||||
console.error("Landing page not found!");
|
||||
}
|
||||
}
|
||||
|
||||
function buildApps() {
|
||||
if (!fs.existsSync(APPS_DIR)) return;
|
||||
|
||||
const apps = fs.readdirSync(APPS_DIR);
|
||||
|
||||
apps.forEach(app => {
|
||||
const appSrc = path.join(APPS_DIR, app);
|
||||
const appDist = path.join(DIST_DIR, app);
|
||||
|
||||
if (!fs.statSync(appSrc).isDirectory()) return;
|
||||
|
||||
fs.mkdirSync(appDist, { recursive: true });
|
||||
|
||||
// Check if it's a Vite app (has package.json and vite.config.ts)
|
||||
if (fs.existsSync(path.join(appSrc, 'package.json')) && fs.existsSync(path.join(appSrc, 'vite.config.ts'))) {
|
||||
console.log(`Building Vite app: ${app}...`);
|
||||
try {
|
||||
// Install dependencies if needed
|
||||
if (!fs.existsSync(path.join(appSrc, 'node_modules'))) {
|
||||
console.log(`Installing dependencies for ${app}...`);
|
||||
execSync('npm install', { cwd: appSrc, stdio: 'inherit' });
|
||||
}
|
||||
|
||||
// Build
|
||||
execSync('npm run build', { cwd: appSrc, stdio: 'inherit' });
|
||||
|
||||
// Copy dist contents
|
||||
const viteDist = path.join(appSrc, 'dist');
|
||||
if (fs.existsSync(viteDist)) {
|
||||
fs.cpSync(viteDist, appDist, { recursive: true });
|
||||
} else {
|
||||
console.error(`Vite build failed to produce 'dist' for ${app}`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`Failed to build ${app}:`, e);
|
||||
}
|
||||
} else {
|
||||
// Standard HTML app
|
||||
console.log(`Processing HTML app: ${app}...`);
|
||||
const files = fs.readdirSync(appSrc);
|
||||
files.forEach(file => {
|
||||
const srcFile = path.join(appSrc, file);
|
||||
const distFile = path.join(appDist, file);
|
||||
|
||||
if (file.endsWith('.html')) {
|
||||
processHtml(srcFile, distFile);
|
||||
} else {
|
||||
fs.cpSync(srcFile, distFile, { recursive: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createRedirect(filename, target) {
|
||||
const content = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0; url=${target}" />
|
||||
<script>window.location.href = "${target}";</script>
|
||||
<title>Redirecting...</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>If you are not redirected automatically, follow this <a href="${target}">link</a>.</p>
|
||||
</body>
|
||||
</html>`;
|
||||
const filePath = path.join(DIST_DIR, filename);
|
||||
fs.writeFileSync(filePath, content);
|
||||
console.log(`Created static redirect: ${filename} -> ${target}`);
|
||||
}
|
||||
|
||||
function main() {
|
||||
console.log("Starting Build...");
|
||||
cleanDist();
|
||||
buildLanding();
|
||||
buildApps();
|
||||
|
||||
// Create static redirects for legacy paths
|
||||
createRedirect('game.html', '/hexarow/');
|
||||
createRedirect('german.html', '/german-trainer/');
|
||||
|
||||
console.log("Build Complete!");
|
||||
}
|
||||
|
||||
main();
|
||||
11
src/apps/german-trainer/index.html
Normal file
11
src/apps/german-trainer/index.html
Normal file
File diff suppressed because one or more lines are too long
1
src/apps/hexarow/index.html
Normal file
1
src/apps/hexarow/index.html
Normal file
File diff suppressed because one or more lines are too long
1
src/landing/index.html
Normal file
1
src/landing/index.html
Normal file
File diff suppressed because one or more lines are too long
10850
worker-configuration.d.ts
vendored
10850
worker-configuration.d.ts
vendored
File diff suppressed because it is too large
Load Diff
@ -1,63 +1,9 @@
|
||||
/**
|
||||
* For more details on how to configure Wrangler, refer to:
|
||||
* https://developers.cloudflare.com/workers/wrangler/configuration/
|
||||
*/
|
||||
{
|
||||
"$schema": "node_modules/wrangler/config-schema.json",
|
||||
"name": "muddy-frost-c06d",
|
||||
"main": "src/index.ts",
|
||||
"name": "cffyin",
|
||||
"pages_build_output_dir": "./dist",
|
||||
"compatibility_date": "2025-12-13",
|
||||
"migrations": [
|
||||
{
|
||||
"new_sqlite_classes": [
|
||||
"MyDurableObject"
|
||||
],
|
||||
"tag": "v1"
|
||||
}
|
||||
],
|
||||
"assets": {
|
||||
// The path to the directory containing the `index.html` file to be served at `/`
|
||||
"directory": "./public"
|
||||
},
|
||||
"durable_objects": {
|
||||
"bindings": [
|
||||
{
|
||||
"class_name": "MyDurableObject",
|
||||
"name": "MY_DURABLE_OBJECT"
|
||||
}
|
||||
]
|
||||
},
|
||||
"observability": {
|
||||
"enabled": true
|
||||
}
|
||||
/**
|
||||
* Smart Placement
|
||||
* Docs: https://developers.cloudflare.com/workers/configuration/smart-placement/#smart-placement
|
||||
*/
|
||||
// "placement": { "mode": "smart" }
|
||||
/**
|
||||
* Bindings
|
||||
* Bindings allow your Worker to interact with resources on the Cloudflare Developer Platform, including
|
||||
* databases, object storage, AI inference, real-time communication and more.
|
||||
* https://developers.cloudflare.com/workers/runtime-apis/bindings/
|
||||
*/
|
||||
/**
|
||||
* Environment Variables
|
||||
* https://developers.cloudflare.com/workers/wrangler/configuration/#environment-variables
|
||||
*/
|
||||
// "vars": { "MY_VARIABLE": "production_value" }
|
||||
/**
|
||||
* Note: Use secrets to store sensitive data.
|
||||
* https://developers.cloudflare.com/workers/configuration/secrets/
|
||||
*/
|
||||
/**
|
||||
* Static Assets
|
||||
* https://developers.cloudflare.com/workers/static-assets/binding/
|
||||
*/
|
||||
// "assets": { "directory": "./public/", "binding": "ASSETS" }
|
||||
/**
|
||||
* Service Bindings (communicate between multiple Workers)
|
||||
* https://developers.cloudflare.com/workers/wrangler/configuration/#service-bindings
|
||||
*/
|
||||
// "services": [{ "binding": "MY_SERVICE", "service": "my-service" }]
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user