|
| 1 | +# Project Overview |
| 2 | +- Rails 7.1 monolith for the Raspberry Pi Code Editor API (REST + GraphQL), served at `editor-api.raspberrypi.org`. |
| 3 | +- Primary runtime via Docker; API listens on port 3009. |
| 4 | + |
| 5 | +## Architecture |
| 6 | +- **REST** under `app/controllers/api/**` with Jbuilder views in `app/views/api/**`; **GraphQL** at `/graphql` (schema in `app/graphql/**`). |
| 7 | +- **Auth**: Browser/session via OmniAuth (OIDC to Hydra); API token via `Authorization: Bearer` with `Identifiable#identify_user` → `User.from_token` → `HydraPublicApiClient`. |
| 8 | +- **Authorization**: cancancan in `app/models/ability.rb`. Use `load_and_authorize_resource` in controllers; GraphQL uses `Types::ProjectType.authorized?` and `current_ability` in context. |
| 9 | +- **Domain**: `Project` (+ `Component`) with Active Storage attachments. Domain operations in `lib/concepts/**` (e.g. `Project::Create`, `Project::CreateRemix`). Prefer calling these from controllers/mutations. |
| 10 | +- **Jobs**: GoodJob (`bundle exec good_job start --max-threads=8`). Admin UI at `/admin/good_job`. |
| 11 | +- **Integrations**: Profile API (`lib/profile_api_client.rb`), UserInfo API, GitHub GraphQL (`lib/github_api.rb`), GitHub webhooks via `GithubWebhooksController`. |
| 12 | +- **Storage/CORS**: Active Storage uses S3 in non-dev. CORS via `config/initializers/cors.rb` and `lib/origin_parser.rb`. `CorpMiddleware` sets CORP for Active Storage routes. |
| 13 | + |
| 14 | +## Key Conventions |
| 15 | +- GraphQL context: `current_user`, `current_ability`, `remix_origin`. Object IDs use GlobalID. Locale fallback via `ProjectLoader`: `[requested, 'en', nil]`. |
| 16 | +- REST pagination returns HTTP `Link` header (see `Api::ProjectsController#pagination_link_header`). |
| 17 | +- Project rules: identifiers unique per locale; default component name/extension immutable on update; students cannot update `instructions` on school projects; creating a project in a school auto-builds `SchoolProject`. |
| 18 | +- Remix: `Project::CreateRemix` clones media/components, sets `remix_origin`, clears `lesson_id`. |
| 19 | +- Errors: domain ops return `OperationResponse` with `:error`; controllers return 4xx heads; GraphQL raises `GraphQL::ExecutionError`. Exceptions reported to Sentry. |
| 20 | +- snake_case for variable numbers (exceptions: `sha256`, `X-Hub-Signature-256`). |
| 21 | + |
| 22 | +## Quickstart |
| 23 | +```bash |
| 24 | +cp .env.example .env |
| 25 | +docker compose build |
| 26 | +docker compose run --rm api rails db:setup |
| 27 | +docker compose up |
| 28 | +``` |
| 29 | + |
| 30 | +## Development |
| 31 | +- Use `docker compose` for all commands; project mounts into `editor-api:builder` with tmpfs for `tmp/`. |
| 32 | + |
| 33 | +## Testing |
| 34 | +- Full suite: `docker compose run --rm api rspec` |
| 35 | +- Single spec: `docker compose run --rm api rspec spec/path/to/spec.rb` |
| 36 | +- Lint: `docker compose run --rm api bundle exec rubocop` |
| 37 | +- CI: CircleCI with Ruby 3.2, Postgres 12, Redis. |
| 38 | + |
| 39 | +## Where to Look First |
| 40 | +- Routes: `config/routes.rb`. Auth: `config/initializers/omniauth.rb`, `app/helpers/authentication_helper.rb`, `app/controllers/concerns/identifiable.rb`. |
| 41 | +- Permissions: `app/models/ability.rb`. Domain ops: `lib/concepts/**`. Models: `app/models/**`. GraphQL: `app/graphql/**`. |
| 42 | + |
| 43 | +## Security |
| 44 | +- Never commit secrets (`.env`, `config/master.key`, API tokens, webhook secrets). |
| 45 | +- `.env.example` contains placeholder values only. |
0 commit comments