From 0d068b5ac65255bc10df58a9067fa546b652e113 Mon Sep 17 00:00:00 2001 From: maziggy Date: Sun, 8 Feb 2026 09:01:55 +0100 Subject: [PATCH] Updated CONTRIBUTING.md --- CONTRIBUTING.md | 94 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 395f764fa..25f20c3c1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,6 +9,8 @@ Thank you for your interest in contributing to Bambuddy! This document provides - [Development Setup](#development-setup) - [Making Changes](#making-changes) - [Code Style](#code-style) +- [Internationalization (i18n)](#internationalization-i18n) +- [Authentication & Permissions](#authentication--permissions) - [Testing](#testing) - [CI Pipeline](#ci-pipeline) - [Submitting Changes](#submitting-changes) @@ -152,6 +154,98 @@ Pre-commit hooks run automatically on `git commit` and include Ruff linting/form pre-commit run --all-files ``` +## Internationalization (i18n) + +The frontend uses [react-i18next](https://react.i18next.com/) for all user-facing text. **Never hardcode user-visible strings** — always use translation keys. + +### Locale Files + +Translations live in `frontend/src/i18n/locales/`: + +| File | Language | +|------|----------| +| `en.ts` | English (primary) | +| `de.ts` | German | +| `ja.ts` | Japanese | + +### Adding New Strings + +1. Add the key to the appropriate section in **all three** locale files +2. Use the `useTranslation` hook in your component: + +```tsx +import { useTranslation } from 'react-i18next'; + +function MyComponent() { + const { t } = useTranslation(); + return {t('section.myNewKey')}; +} +``` + +3. Keys are organized by feature (e.g., `spoolman.`, `nav.`, `common.`) + +### Important Notes + +- All three locale files must use the **same key structure** — same nesting, same key paths +- Always add keys to all three locales to maintain parity +- Run frontend tests after changes — locale parity is validated +- If you find structural inconsistencies between locales, fix them — different key paths cause silent fallback to English + +## Authentication & Permissions + +Bambuddy has an optional authentication system. When auth is enabled, API endpoints are protected by granular permissions. + +### How It Works + +Authentication is **opt-in** — when disabled, all endpoints are open. The system uses `RequirePermissionIfAuthEnabled` which: + +- Checks if auth is enabled in settings +- If disabled: allows the request through (no-op) +- If enabled: validates JWT token/API key and checks the user has the required permission + +### Adding Auth to New Endpoints + +Use the `RequirePermissionIfAuthEnabled` dependency in your route: + +```python +from backend.app.core.auth import RequirePermissionIfAuthEnabled +from backend.app.core.permissions import Permission + +@router.get("/my-resource") +async def get_my_resource( + _: User | None = RequirePermissionIfAuthEnabled(Permission.RESOURCE_READ), +): + ... +``` + +### Permission Convention + +Permissions follow the `resource:action` pattern (e.g., `filaments:read`, `printers:control`). Standard actions: + +| Action | Usage | +|--------|-------| +| `read` | View/list resources | +| `create` | Create new resources | +| `update` | Modify existing resources | +| `delete` | Remove resources | + +Some resources have additional actions (e.g., `printers:control` for start/stop, `printers:files` for file transfer). + +### Adding New Permissions + +1. Add the permission to the `Permission` enum in `backend/app/core/permissions.py` +2. Add it to the appropriate category in `PERMISSION_CATEGORIES` +3. Add it to the relevant default groups (`Administrators` gets all, `Operators` and `Viewers` as appropriate) +4. Use it in your route with `RequirePermissionIfAuthEnabled` + +### Default Groups + +| Group | Access Level | +|-------|-------------| +| **Administrators** | All permissions | +| **Operators** | Full control of printers, own items in archives/queue, read-only settings | +| **Viewers** | Read-only access to all resources | + ## Testing The easiest way to run tests is with the provided scripts in the project root: