Working and documented
This commit is contained in:
@@ -84,10 +84,298 @@ To disable self-service account creation and allow only existing users or SSO si
|
|||||||
ALLOW_REGISTRATION=false
|
ALLOW_REGISTRATION=false
|
||||||
```
|
```
|
||||||
|
|
||||||
## API routes
|
## API
|
||||||
|
|
||||||
|
The app includes an Express API in [backend/src/app.ts](/home/corey/github/Arsenal_IQ/backend/src/app.ts). In local development, the frontend calls:
|
||||||
|
|
||||||
|
- Base URL: `http://localhost:5000/api`
|
||||||
|
- Health check: `http://localhost:5000/health`
|
||||||
|
|
||||||
|
### Authentication model
|
||||||
|
|
||||||
|
- Authenticated requests use `Authorization: Bearer <token>`
|
||||||
|
- The token is returned by local login/register or after a successful SSO callback
|
||||||
|
- The app is currently single-profile per user, even though profile endpoints still exist for compatibility
|
||||||
|
- Most inventory routes also accept `x-profile-id`, but in the current app this resolves to the user’s single arsenal/profile
|
||||||
|
|
||||||
|
### Environment flags
|
||||||
|
|
||||||
|
- `ALLOW_REGISTRATION=true|false`
|
||||||
|
- Controls whether `POST /api/auth/register` is available
|
||||||
|
- When `false`, the login UI hides self-service account creation
|
||||||
|
|
||||||
|
### Response shape notes
|
||||||
|
|
||||||
|
- Validation and business-rule errors generally return:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "error": "Human readable message" }
|
||||||
|
```
|
||||||
|
|
||||||
|
- Successful login/register responses return:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"token": "session-token",
|
||||||
|
"user": {
|
||||||
|
"id": "uuid",
|
||||||
|
"email": "owner@example.com",
|
||||||
|
"name": "Owner Name"
|
||||||
|
},
|
||||||
|
"profiles": [
|
||||||
|
{
|
||||||
|
"id": "uuid",
|
||||||
|
"name": "Owner"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"activeProfileId": "uuid"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Core routes
|
||||||
|
|
||||||
|
#### Service and discovery
|
||||||
|
|
||||||
- `GET /health`
|
- `GET /health`
|
||||||
|
- Returns service/database health
|
||||||
- `GET /api`
|
- `GET /api`
|
||||||
|
- Returns API metadata and `allowRegistration`
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "Arsenal IQ API",
|
||||||
|
"version": "3.0.0",
|
||||||
|
"allowRegistration": true,
|
||||||
|
"resources": [
|
||||||
|
"/api/auth/login",
|
||||||
|
"/api/auth/register",
|
||||||
|
"/api/dashboard",
|
||||||
|
"/api/firearms",
|
||||||
|
"/api/calibers",
|
||||||
|
"/api/ammo"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Authentication
|
||||||
|
|
||||||
|
- `GET /api/auth/providers`
|
||||||
|
- Public list of enabled SSO providers for the login page
|
||||||
|
- `POST /api/auth/register`
|
||||||
|
- Creates a local account when registration is enabled
|
||||||
|
- `POST /api/auth/login`
|
||||||
|
- Signs in with local email/password
|
||||||
|
- `POST /api/auth/logout`
|
||||||
|
- Invalidates the current session token
|
||||||
|
- `GET /api/auth/me`
|
||||||
|
- Returns the current authenticated user and active profile
|
||||||
|
- `GET /api/auth/sso/:providerKey/start`
|
||||||
|
- Starts the OIDC login flow and returns an authorization URL
|
||||||
|
- `GET /api/auth/sso/:providerKey/callback`
|
||||||
|
- Handles the provider callback, creates or links a user, then redirects back to the frontend with a token
|
||||||
|
|
||||||
|
Register request:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "Owner Name",
|
||||||
|
"email": "owner@example.com",
|
||||||
|
"password": "change-me"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Login request:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"email": "owner@example.com",
|
||||||
|
"password": "change-me"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
SSO behavior:
|
||||||
|
|
||||||
|
- If the SSO provider returns an email that matches an existing user, the SSO identity is linked to that user
|
||||||
|
- If the email does not exist yet, a new user is created automatically
|
||||||
|
- If the account was created via SSO only, local password login is rejected for that user
|
||||||
|
|
||||||
|
#### Profiles
|
||||||
|
|
||||||
|
- `GET /api/profiles`
|
||||||
|
- Returns the user’s profile list and active profile ID
|
||||||
|
- `POST /api/profiles`
|
||||||
|
- Currently disabled and returns `403`
|
||||||
|
- `POST /api/profiles/select`
|
||||||
|
- Returns the active profile ID
|
||||||
|
|
||||||
|
Note:
|
||||||
|
|
||||||
|
- The backend still exposes these endpoints, but the product now behaves as one user per arsenal/profile
|
||||||
|
|
||||||
|
#### Dashboard
|
||||||
|
|
||||||
- `GET /api/dashboard`
|
- `GET /api/dashboard`
|
||||||
|
- Returns the current user, active profile, firearms, calibers, ammo inventory, defaults, and summary metrics used by the React UI
|
||||||
|
|
||||||
|
Summary fields:
|
||||||
|
|
||||||
|
- `totalFirearms`
|
||||||
|
- `totalAmmoRounds`
|
||||||
|
- `firearmsInvestment`
|
||||||
|
- `ammoInvestment`
|
||||||
|
- `configuredCalibers`
|
||||||
|
|
||||||
|
#### Firearms
|
||||||
|
|
||||||
- `GET /api/firearms`
|
- `GET /api/firearms`
|
||||||
|
- Lists firearms for the current profile
|
||||||
|
- `POST /api/firearms`
|
||||||
|
- Creates a firearm
|
||||||
|
- `PUT /api/firearms/:id`
|
||||||
|
- Updates a firearm
|
||||||
|
- `DELETE /api/firearms/:id`
|
||||||
|
- Deletes a firearm
|
||||||
|
|
||||||
|
Firearm body:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"manufacturer": "Glock",
|
||||||
|
"model": "19",
|
||||||
|
"category": "Handgun",
|
||||||
|
"caliber": "9mm",
|
||||||
|
"serialNumber": "ABC123",
|
||||||
|
"purchasePrice": 550,
|
||||||
|
"acquiredOn": "2025-06-01",
|
||||||
|
"imageUrl": "",
|
||||||
|
"notes": "Carry pistol"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Valid firearm categories:
|
||||||
|
|
||||||
|
- `Handgun`
|
||||||
|
- `Rifle`
|
||||||
|
- `Shotgun`
|
||||||
|
- `PCC`
|
||||||
|
- `Other`
|
||||||
|
|
||||||
|
Firearm response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "uuid",
|
||||||
|
"manufacturer": "Glock",
|
||||||
|
"model": "19",
|
||||||
|
"category": "Handgun",
|
||||||
|
"caliber": "9mm",
|
||||||
|
"serialNumber": "ABC123",
|
||||||
|
"purchasePrice": 550,
|
||||||
|
"acquiredOn": "2025-06-01",
|
||||||
|
"imageUrl": null,
|
||||||
|
"notes": "Carry pistol"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Calibers
|
||||||
|
|
||||||
|
- `GET /api/calibers`
|
||||||
|
- Returns configured active calibers plus unused default caliber names
|
||||||
|
- `POST /api/calibers`
|
||||||
|
- Adds or re-enables a caliber for the current profile
|
||||||
|
- `PATCH /api/calibers/:id`
|
||||||
|
- Enables or disables a caliber
|
||||||
|
|
||||||
|
Create caliber request:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "300 BLK"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Toggle caliber request:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"isActive": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Caliber response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "uuid",
|
||||||
|
"name": "9mm",
|
||||||
|
"isDefault": true,
|
||||||
|
"isActive": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Ammo
|
||||||
|
|
||||||
- `GET /api/ammo`
|
- `GET /api/ammo`
|
||||||
|
- Returns active caliber inventory rows for the current profile
|
||||||
|
- `PATCH /api/ammo/:caliberId`
|
||||||
|
- Adjusts rounds on hand and optionally updates cost per round
|
||||||
|
|
||||||
|
Ammo patch request:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"rounds": 250,
|
||||||
|
"costPerRound": 0.24
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- Positive `rounds` adds to the current count
|
||||||
|
- Negative `rounds` removes from the current count
|
||||||
|
- The backend clamps the final total to `0`
|
||||||
|
- If `costPerRound` is omitted or `null`, the existing value is preserved
|
||||||
|
|
||||||
|
Ammo response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"caliberId": "uuid",
|
||||||
|
"caliber": "9mm",
|
||||||
|
"roundsOnHand": 750,
|
||||||
|
"costPerRound": 0.24,
|
||||||
|
"totalValue": 180
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Auth provider settings
|
||||||
|
|
||||||
|
- `GET /api/settings/auth-providers`
|
||||||
|
- Returns the full editable provider configuration for the authenticated settings page
|
||||||
|
- `PUT /api/settings/auth-providers/:providerKey`
|
||||||
|
- Updates an auth provider config such as Google, Entra, or another OIDC-compatible provider
|
||||||
|
|
||||||
|
Provider update request:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"displayName": "Google",
|
||||||
|
"protocol": "oidc",
|
||||||
|
"clientId": "your-client-id",
|
||||||
|
"clientSecret": "your-client-secret",
|
||||||
|
"authorizationEndpoint": "https://accounts.google.com/o/oauth2/v2/auth",
|
||||||
|
"tokenEndpoint": "https://oauth2.googleapis.com/token",
|
||||||
|
"userinfoEndpoint": "https://openidconnect.googleapis.com/v1/userinfo",
|
||||||
|
"issuer": "https://accounts.google.com",
|
||||||
|
"scopes": "openid profile email",
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Current limitations
|
||||||
|
|
||||||
|
- There is no password reset or account recovery flow yet
|
||||||
|
- There is no API versioning beyond the current route structure
|
||||||
|
- Profile endpoints remain present, but multiple profiles are intentionally disabled in the product
|
||||||
|
|||||||
Reference in New Issue
Block a user