Mr. Worldwide #93

Merged
quoije merged 10 commits from 2026-localization into prod 2026-03-18 20:44:34 -04:00
Showing only changes of commit e0886f2c8a - Show all commits
+71 -4
View File
@@ -1,9 +1,9 @@
## Oscars Pool 2026 ## Oscars Pool 2026
Little nodejs + mongodb app that I hack together with ChatGPT and Cursor to keep track of watched movies for an incoming Oscars Pool with some friends. Little nodejs + mongodb app that I hack together with ChatGPT and Cursor to keep track of watched movies for an incoming Oscars Pool with some friends.
**Note:** The application interface is in French. **Note:** The application interface now supports **English and French** via JSON locale bundles (default is English; the admin can force the site language from the Settings → Language tab).
**Disclaimer:** This was a fun, experimental project built with “AI slop” (Cursor), so it may have security issues. For example, `python_video_server` uses the default token‑in‑URL approach to serve video files, which can leave user tokens in logs or browser history. There are ways to improve this, but at this point I’m blinder than Ray Charles. **Disclaimer:** This was a fun, experimental project built with “AI slop” (Cursor, Claude and Copilot), so it may have security issues. For example, `python_video_server` uses the default token‑in‑URL approach to serve video files, which can leave user tokens in logs or browser history. There are ways to improve this, but at this point I’m blinder than Ray Charles.
![Oscars Pool 2026](img/preview.gif) ![Oscars Pool 2026](img/preview.gif)
@@ -38,6 +38,41 @@ Little nodejs + mongodb app that I hack together with ChatGPT and Cursor to keep
- **App version control**: manage app version display (footer) from admin panel (no GitHub dependency) - **App version control**: manage app version display (footer) from admin panel (no GitHub dependency)
- **Player UI settings**: control player page admin status display - **Player UI settings**: control player page admin status display
## Localization (i18n)
The app ships with a lightweight client-side localization system and two locales: **English** and **French**.
- Locale files live in:
- `public/locales/en.json`
- `public/locales/fr.json`
- The i18n runtime is implemented in `public/js/localization.js` and exposed as `window.i18n`.
- The admin can choose the **site language** (currently `en` or `fr`) from the **Admin → Settings → Language** tab. This value is stored in MongoDB and used by the client to pick the active locale.
- Markup is localized via `data-i18n` attributes (and friends):
- `data-i18n` → element text content
- `data-i18n-placeholder` → input placeholders
- `data-i18n-aria-label` → ARIA labels
- `data-i18n-title` → `title` attributes
- JavaScript code uses a small helper to translate keys with an English fallback, for example:
```js
// inside public/js/* files
function t(key, fallback, params) {
if (window.i18n && typeof window.i18n.t === 'function') {
const base = window.i18n.t(key, params || {});
return base && base !== key ? base : (fallback || key);
}
return fallback || key;
}
showResponse('success', t('admin.movies.updatedSuccessfully', 'Movie updated successfully.'));
```
To add new text:
1. Add an English entry in `public/locales/en.json` (this is the base source of truth).
2. Optionally add the corresponding French translation to `public/locales/fr.json` using the same key.
3. In HTML, wire it with `data-i18n="your.key"`, or in JS call `t('your.key', 'English fallback')`.
## Setup ## Setup
Create a `.env` file at the project root (same folder as `package.json`). Create a `.env` file at the project root (same folder as `package.json`).
@@ -66,6 +101,38 @@ Generate a good `JWT_SECRET`:
openssl rand -hex 32 openssl rand -hex 32
``` ```
## First-time admin setup
On a brand new database (no users with `role: 69`), the app exposes a simple first-time setup flow to create the initial admin account.
1. **Start the app**
- Make sure `.env` has at least `MONGO_URI` and `JWT_SECRET`.
- Run:
```bash
npm install
node index.js
```
2. **Open the home page**
- Visit `http://localhost:5050/` (or `http://localhost:${PORT}/` if you changed the port).
- If no admin user exists yet, you'll see an **admin setup card** instead of the regular login form.
- Fill in **name**, **email**, and **password**, then submit to create the first admin. The backend will:
- create a `User` document with `role: 69` (admin), and
- return a JWT so you're logged in immediately and redirected into the app.
3. **Access the admin panel**
- Once logged in as admin, open the **Admin / Control** page (or navigate to `/control.html`).
- From there you can:
- Set the **active Oscar year** (Settings → Active year).
- Configure the **Oscar date** for the countdown.
- Adjust **points configuration** (points per movie / per correct pick).
- Control **picks and version visibility**.
- Choose the **site language** (Settings → Language → `en` / `fr`).
- Manage movies, categories, winners, users, and the 100% completion modal.
After the first admin is created, the setup card disappears and the index page behaves as a normal login screen.
## Database (MongoDB) ## Database (MongoDB)
This app uses **MongoDB** via Mongoose. Provide a connection string in `MONGO_URI`. This app uses **MongoDB** via Mongoose. Provide a connection string in `MONGO_URI`.
@@ -85,7 +152,7 @@ This app uses **MongoDB** via Mongoose. Provide a connection string in `MONGO_UR
The app also reads these environment variables (all optional): The app also reads these environment variables (all optional):
```bash ```bash
PORT=5000 # server port (default: 5000) PORT=5050 # server port (default: 5050)
JWT_EXPIRES_IN=8h # JWT expiry (default: 8h) JWT_EXPIRES_IN=8h # JWT expiry (default: 8h)
VIDEO_FILES_DIR=/abs/path/to/video # where server video files live (default: ./public/video) VIDEO_FILES_DIR=/abs/path/to/video # where server video files live (default: ./public/video)
VIDEO_SESSION_MAX_AGE_SECONDS=28800 # video_auth cookie max-age (default: 8h) VIDEO_SESSION_MAX_AGE_SECONDS=28800 # video_auth cookie max-age (default: 8h)
@@ -93,7 +160,7 @@ MOVIES_CACHE_TTL_MS=30000 # movies route cache TTL (default: 30000ms)
``` ```
Notes: Notes:
- The server serves the UI from `public/` (open `http://localhost:5000` by default). - The server serves the UI from `public/` (open `http://localhost:5050` by default unless you override `PORT`).
- If you use `video_file` in a movie record, it must be a **relative path** under `VIDEO_FILES_DIR` (the backend blocks traversal/absolute paths). - If you use `video_file` in a movie record, it must be a **relative path** under `VIDEO_FILES_DIR` (the backend blocks traversal/absolute paths).
## Python video server (optional) ## Python video server (optional)