feat: scaffold Astro project with en/fr i18n routing
Sets output to static (Astro default) with path-based i18n routing (/en/, /fr/) and no unprefixed default-locale route, per the hub landing page design spec.
This commit is contained in:
+24
@@ -0,0 +1,24 @@
|
||||
# build output
|
||||
dist/
|
||||
# generated types
|
||||
.astro/
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
.env.production
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
# jetbrains setting folder
|
||||
.idea/
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"recommendations": ["astro-build.astro-vscode"],
|
||||
"unwantedRecommendations": []
|
||||
}
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"command": "./node_modules/.bin/astro dev",
|
||||
"name": "Development server",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
## Development
|
||||
|
||||
When starting the dev server, use background mode:
|
||||
|
||||
```
|
||||
astro dev --background
|
||||
```
|
||||
|
||||
Manage the background server with `astro dev stop`, `astro dev status`, and `astro dev logs`.
|
||||
|
||||
## Documentation
|
||||
|
||||
Full documentation: https://docs.astro.build
|
||||
|
||||
Consult these guides before working on related tasks:
|
||||
|
||||
- [Adding pages, dynamic routes, or middleware](https://docs.astro.build/en/guides/routing/)
|
||||
- [Working with Astro components](https://docs.astro.build/en/basics/astro-components/)
|
||||
- [Using React, Vue, Svelte, or other framework components](https://docs.astro.build/en/guides/framework-components/)
|
||||
- [Adding or managing content](https://docs.astro.build/en/guides/content-collections/)
|
||||
- [Adding styles or using Tailwind](https://docs.astro.build/en/guides/styling/)
|
||||
- [Supporting multiple languages](https://docs.astro.build/en/guides/internationalization/)
|
||||
@@ -6,102 +6,16 @@
|
||||
- No emojis or em-dashes.
|
||||
- Do not guess APIs, versions, flags, commit SHAs, or package names. Verify by reading code or docs before asserting.
|
||||
|
||||
<!-- code-review-graph MCP tools -->
|
||||
## MCP Tools: code-review-graph
|
||||
## Development
|
||||
- When starting the dev server, use background mode: `astro dev --background`
|
||||
- Manage the background server with `astro dev stop`, `astro dev status`, and `astro dev logs`.
|
||||
|
||||
**IMPORTANT: This project has a knowledge graph. ALWAYS use the
|
||||
code-review-graph MCP tools BEFORE using Grep/Glob/Read to explore
|
||||
the codebase.** The graph is faster, cheaper (fewer tokens), and gives
|
||||
you structural context (callers, dependents, test coverage) that file
|
||||
scanning cannot.
|
||||
## Documentation
|
||||
Full documentation: https://docs.astro.build
|
||||
|
||||
### When to use graph tools FIRST
|
||||
|
||||
- **Exploring code**: `semantic_search_nodes` or `query_graph` instead of Grep
|
||||
- **Understanding impact**: `get_impact_radius` instead of manually tracing imports
|
||||
- **Code review**: `detect_changes` + `get_review_context` instead of reading entire files
|
||||
- **Finding relationships**: `query_graph` with callers_of/callees_of/imports_of/tests_for
|
||||
- **Architecture questions**: `get_architecture_overview` + `list_communities`
|
||||
|
||||
Fall back to Grep/Glob/Read **only** when the graph doesn't cover what you need.
|
||||
|
||||
### Key Tools
|
||||
|
||||
| Tool | Use when |
|
||||
| ------ | ---------- |
|
||||
| `detect_changes` | Reviewing code changes — gives risk-scored analysis |
|
||||
| `get_review_context` | Need source snippets for review — token-efficient |
|
||||
| `get_impact_radius` | Understanding blast radius of a change |
|
||||
| `get_affected_flows` | Finding which execution paths are impacted |
|
||||
| `query_graph` | Tracing callers, callees, imports, tests, dependencies |
|
||||
| `semantic_search_nodes` | Finding functions/classes by name or keyword |
|
||||
| `get_architecture_overview` | Understanding high-level codebase structure |
|
||||
| `refactor_tool` | Planning renames, finding dead code |
|
||||
|
||||
### Workflow
|
||||
|
||||
1. The graph auto-updates on file changes (via hooks).
|
||||
2. Use `detect_changes` for code review.
|
||||
3. Use `get_affected_flows` to understand impact.
|
||||
4. Use `query_graph` pattern="tests_for" to check coverage.
|
||||
|
||||
## Local environment / running tests
|
||||
|
||||
- `.env` holds **production** credentials (o2switch host, real DB name/password). Never load it for local runs or tests.
|
||||
- `.env.local` holds the local dev DB credentials (`DB_HOST=127.0.0.1`, `DB_USER=convert_user`, `DB_NAME=file_converter`, `DB_PASSWORD=change_me`). This is what local testing should use.
|
||||
- `src/config.js` uses `dotenv/config`, which only loads `.env` and does not override variables already present in `process.env`. There's no vitest/dotenv wiring that picks up `.env.local` automatically.
|
||||
- To run tests locally without touching prod config, pass the `.env.local` values as inline env vars so they take precedence before `dotenv/config` runs, e.g.:
|
||||
```
|
||||
DB_HOST=127.0.0.1 DB_USER=convert_user DB_PASSWORD=change_me DB_NAME=file_converter STORAGE_DIR=./storage PORT=3000 npx vitest run
|
||||
```
|
||||
- A local MariaDB is expected to already be running on `127.0.0.1:3306` with the `file_converter` DB and `convert_user` credentials seeded.
|
||||
- Known pre-existing failures unrelated to any fix: `test/cleanup.test.js` ("deletes an expired pending job...") and `test/jobs/jobRepository.test.js` ("finds expired jobs and allows deleting them") — both fail on `main` independent of other changes (looks like a clock/timezone mismatch around `expiresAt` comparisons, not yet root-caused). Don't assume a change caused these; verify against `main` first if they show up again.
|
||||
|
||||
### Manual end-to-end testing (starting server.js/worker.js yourself)
|
||||
|
||||
- This dev machine typically already has its own long-running `node src/server.js` / `node src/worker.js` (and sometimes `npm run dev` for the frontend) processes started outside the session, from before any given conversation begins. Before manually verifying a change end-to-end, check for them first: `powershell -NoProfile -Command "Get-CimInstance Win32_Process -Filter \"Name='node.exe'\" | Select-Object ProcessId,CommandLine"`.
|
||||
- If a pre-existing worker is running, it loaded `src/converters/*.js` at its own start time and will keep running that in-memory code until restarted — it does not pick up edits made later in the session. If you start a *second*, freshly-spawned worker to test new converter code, both workers poll the same DB and race for pending jobs; if the stale one wins, the job fails with a misleading error (e.g. "No converter registered for X -> Y" for a pair that was, in fact, just added) even though the new code is correct. Don't conclude the new code is broken from a failure like this without first checking whether an older worker process grabbed the job.
|
||||
- Safe options once you notice this: only kill/clean up processes you started yourself in the session (identifiable by matching the exact command line you launched); never kill or restart the user's pre-existing `server.js`/`worker.js`/`npm run dev` instances without asking, since that's their running environment. If you want a real end-to-end confirmation, tell the user their existing server/worker needs restarting to pick up the change — don't restart it for them silently.
|
||||
- `curl -F "files=@/tmp/somefile"` fails with exit code 26 ("Failed to read local file") in this Git Bash environment when the path is under `/tmp`. Write the fixture under the project directory instead (e.g. `./scratch-test.md`) and reference it with a relative path in the `-F` flag; delete it afterward.
|
||||
|
||||
## Deployment (o2switch)
|
||||
|
||||
- o2switch is shared hosting: no compiler toolchain, no root access. Any dependency with a native/binary component must ship as a precompiled binary — it cannot be built from source on the server.
|
||||
- `sharp` (libvips) and `puppeteer` (bundled Chromium) are the current binary dependencies in `package.json`. Install them so npm fetches the prebuilt binary for the target platform/arch rather than triggering a source build.
|
||||
- Before adding any new dependency with native bindings, confirm it publishes prebuilt binaries for o2switch's platform/arch — otherwise it will fail to install or run there.
|
||||
- **The o2switch nodevenv/Passenger setup ("Setup Node.js App" in cPanel) only supports a single `package.json`/`node_modules` for the whole registered app — not one per subfolder.** Verified by directly debugging a failed `frontend/` build: `npm install --prefix frontend --include=dev` (root's own `build` script) and even a plain `npm install` run with `cd frontend` first (confirmed via `pwd` to genuinely be inside `frontend/`) both completed "successfully" (correct, unmodified `frontend/package-lock.json`, real `resolved` entries for every package) yet never created a `frontend/node_modules` directory on the server at all. Meanwhile `vite`/`@vitejs/plugin-react` (already present as root devDependencies) resolved fine during the build — only packages that exist *exclusively* in `frontend/package.json` (`react-router-dom`, `react-i18next`, `i18next`, `@phosphor-icons/react`) failed to resolve, with Vite/Rolldown erroring `Rolldown failed to resolve import "react-router-dom"`.
|
||||
- **Fix (applied):** every runtime package `frontend/src/**` imports must also be listed in the **root** `package.json`'s `dependencies` (not just `frontend/package.json`'s) — `react`/`react-dom` already were; `react-router-dom`, `react-i18next`, `i18next`, `@phosphor-icons/react` were added there too. Root's single `node_modules` is an ancestor directory of `frontend/src/`, so Node/Vite's normal upward `node_modules` resolution walk finds them there even with no `frontend/node_modules` on the server.
|
||||
- `frontend/package.json` still declares the same packages in its own `dependencies` — that's intentional, not stale duplication. It's what makes local dev (`npm run dev` inside `frontend/`, which gets a real, normal `frontend/node_modules` on a dev machine) work independently of this server-only constraint. When adding a new frontend runtime dependency, add it to **both** `package.json` files (frontend's own, for local dev; root's, for the o2switch build) and run `npm install` in both places to keep both lockfiles in sync.
|
||||
- `frontend/package.json`'s `devDependencies` (`vite`, `@vitejs/plugin-react`, `oxlint`, `@types/react*`) do **not** need mirroring to root — only the ones already there (`vite`, `@vitejs/plugin-react`) are actually required for the production build to run at all; `oxlint`/`@types/*` are dev-only tooling never invoked during `npm run build`.
|
||||
|
||||
## Database schema & migrations
|
||||
|
||||
- `prisma/schema.prisma` is the source of truth for the `conversion_jobs` schema; `prisma/migrations/` is its version history. `db/schema.sql` no longer exists.
|
||||
- Prisma CLI commands (`prisma migrate dev`, `prisma migrate deploy`, `prisma generate`) need `DATABASE_URL` in their own process environment, separate from the app. Compute it from the same `DB_HOST`/`DB_USER`/`DB_PASSWORD`/`DB_NAME` values used for local tests, via `node scripts/printDatabaseUrl.js` — never write `DATABASE_URL` into `.env` or `.env.local`.
|
||||
- **`prisma migrate dev` does NOT work against the local dev DB** — verified by actually running it (`DATABASE_URL=$(node scripts/printDatabaseUrl.js) npx prisma migrate dev --name test_shadow_db_probe`). It needs to create/drop a temporary shadow database to detect schema drift, and the local `convert_user` does not have `CREATE DATABASE`/`DROP DATABASE` privileges, so it fails immediately with:
|
||||
```
|
||||
Error: P3014
|
||||
Prisma Migrate could not create the shadow database. Please make sure the database user has permission to create databases.
|
||||
Original error: Error code: P1010
|
||||
User was denied access on the database `prisma_migrate_shadow_db_...`
|
||||
```
|
||||
No migration folder is created when it fails this way (it errors before writing anything), so nothing needs cleaning up afterward. Do not use `migrate dev` in this project until `convert_user`'s privileges change.
|
||||
- To create a new migration locally after editing `prisma/schema.prisma`, use the shadow-database-free fallback instead. Note this is **not** the same pattern Task 3 used for the `0_init` baseline — Task 3 used `--from-empty` (diffing against a blank schema), which is a different mode that also happens not to need a shadow database, but is not applicable here since the local DB is not empty. The verified fallback for an already-baselined DB is `--from-schema-datasource` (introspects the live DB's actual current structure — a real DB connection, but not a shadow database) diffed against `--to-schema-datamodel` (reads the target state straight from a schema file, no DB connection at all):
|
||||
```
|
||||
DB_HOST=127.0.0.1 DB_USER=convert_user DB_PASSWORD=change_me DB_NAME=file_converter STORAGE_DIR=./storage \
|
||||
DATABASE_URL=$(node scripts/printDatabaseUrl.js) npx prisma migrate diff \
|
||||
--from-schema-datasource prisma/schema.prisma --to-schema-datamodel prisma/schema.prisma --script > /tmp/migration.sql
|
||||
```
|
||||
This was verified twice against the local dev DB: with `prisma/schema.prisma` unchanged it printed `-- This is an empty migration.` (no shadow database requested, no error); with a throwaway copy of the schema (an extra `notes String? @db.Text` field on `ConversionJob`, passed as `--to-schema-datamodel <path-to-throwaway-copy>`) it printed a correct `ALTER TABLE conversion_jobs ADD COLUMN notes TEXT NULL;` — again with no shadow database involved. The earlier documented fallback here (`--from-migrations prisma/migrations`) was wrong: `--from-migrations` internally requires `--shadow-database-url` too, so it fails for the same P3014 reason `migrate dev` does; it was never actually tested before being written down.
|
||||
|
||||
Write the generated SQL to a path under the OS temp directory (e.g. `/tmp/migration.sql`), never to a file in the repo (an untracked `migration.sql` in the repo root is easy to accidentally commit). Review it, then manually create the next `prisma/migrations/<timestamp>_<description>/migration.sql` folder with that content, and mark it applied without executing it (since you'll apply it for real via `migrate deploy` or by hand):
|
||||
```
|
||||
DATABASE_URL=$(node scripts/printDatabaseUrl.js) npx prisma migrate resolve --applied <timestamp>_<description>
|
||||
```
|
||||
Delete the temp SQL file once it's been copied into the migration folder.
|
||||
- To apply pending migrations in production: with the real `DB_*` values loaded from `.env`, run `DATABASE_URL=$(node scripts/printDatabaseUrl.js) npx prisma migrate deploy` before starting the app.
|
||||
- **ONE-TIME step, required before the very first `migrate deploy` against any environment whose `conversion_jobs` table predates Prisma** (this includes o2switch production, which still has the table created by hand from the now-deleted `db/schema.sql`, but no `_prisma_migrations` tracking table): do **not** run `migrate deploy` first. Instead, baseline that environment exactly like Task 3 did locally:
|
||||
```
|
||||
DATABASE_URL=$(node scripts/printDatabaseUrl.js) npx prisma migrate resolve --applied 0_init
|
||||
```
|
||||
This tells Prisma that `0_init` is already applied (the table already exists) without trying to `CREATE TABLE` it again. Run this once, ever, per environment — after that, `migrate deploy` is the correct command for all subsequent deployments to that environment. Running `migrate deploy` first (without this baseline step) against such an environment will fail with a MySQL "table already exists" error and leave Prisma's migration history in a failed state requiring manual recovery.
|
||||
Consult these guides before working on related tasks:
|
||||
- [Adding pages, dynamic routes, or middleware](https://docs.astro.build/en/guides/routing/)
|
||||
- [Working with Astro components](https://docs.astro.build/en/basics/astro-components/)
|
||||
- [Adding or managing content](https://docs.astro.build/en/guides/content-collections/)
|
||||
- [Adding styles or using Tailwind](https://docs.astro.build/en/guides/styling/)
|
||||
- [Supporting multiple languages](https://docs.astro.build/en/guides/internationalization/)
|
||||
@@ -0,0 +1,43 @@
|
||||
# Astro Starter Kit: Minimal
|
||||
|
||||
```sh
|
||||
npm create astro@latest -- --template minimal
|
||||
```
|
||||
|
||||
> 🧑🚀 **Seasoned astronaut?** Delete this file. Have fun!
|
||||
|
||||
## 🚀 Project Structure
|
||||
|
||||
Inside of your Astro project, you'll see the following folders and files:
|
||||
|
||||
```text
|
||||
/
|
||||
├── public/
|
||||
├── src/
|
||||
│ └── pages/
|
||||
│ └── index.astro
|
||||
└── package.json
|
||||
```
|
||||
|
||||
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
|
||||
|
||||
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
|
||||
|
||||
Any static assets, like images, can be placed in the `public/` directory.
|
||||
|
||||
## 🧞 Commands
|
||||
|
||||
All commands are run from the root of the project, from a terminal:
|
||||
|
||||
| Command | Action |
|
||||
| :------------------------ | :----------------------------------------------- |
|
||||
| `npm install` | Installs dependencies |
|
||||
| `npm run dev` | Starts local dev server at `localhost:4321` |
|
||||
| `npm run build` | Build your production site to `./dist/` |
|
||||
| `npm run preview` | Preview your build locally, before deploying |
|
||||
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
|
||||
| `npm run astro -- --help` | Get help using the Astro CLI |
|
||||
|
||||
## 👀 Want to learn more?
|
||||
|
||||
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).
|
||||
@@ -0,0 +1,12 @@
|
||||
import { defineConfig } from 'astro/config';
|
||||
|
||||
export default defineConfig({
|
||||
site: 'https://www.ombrora.com',
|
||||
i18n: {
|
||||
locales: ['en', 'fr'],
|
||||
defaultLocale: 'en',
|
||||
routing: {
|
||||
prefixDefaultLocale: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
Generated
+4702
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "ombrora-landing",
|
||||
"type": "module",
|
||||
"version": "0.0.1",
|
||||
"engines": {
|
||||
"node": ">=22.12.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro",
|
||||
"test": "vitest run"
|
||||
},
|
||||
"dependencies": {
|
||||
"astro": "^7.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vitest": "^4.1.10"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 655 B |
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
|
||||
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
|
||||
<style>
|
||||
path { fill: #000; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
path { fill: #FFF; }
|
||||
}
|
||||
</style>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 749 B |
@@ -0,0 +1,12 @@
|
||||
---
|
||||
const locale = 'en';
|
||||
---
|
||||
<html lang={locale}>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Ombrora</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Ombrora — EN</h1>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
const locale = 'fr';
|
||||
---
|
||||
<html lang={locale}>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Ombrora</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Ombrora — FR</h1>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,15 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { readFileSync, existsSync } from 'node:fs';
|
||||
|
||||
describe('locale routing', () => {
|
||||
it('builds /en/ and /fr/ pages with correct lang attributes', () => {
|
||||
const en = readFileSync('dist/en/index.html', 'utf-8');
|
||||
const fr = readFileSync('dist/fr/index.html', 'utf-8');
|
||||
expect(en).toContain('lang="en"');
|
||||
expect(fr).toContain('lang="fr"');
|
||||
});
|
||||
|
||||
it('does not build an unprefixed root page', () => {
|
||||
expect(existsSync('dist/index.html')).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"include": [".astro/types.d.ts", "**/*"],
|
||||
"exclude": ["dist"]
|
||||
}
|
||||
Reference in New Issue
Block a user