53 lines
4.3 KiB
Markdown
53 lines
4.3 KiB
Markdown
# Secure Notes Vault API
|
|
|
|
## Design decisions
|
|
- Java programming language since Bluestaq is a Java shop
|
|
- Maven for building
|
|
- Spring Boot as a framework
|
|
- H2 for persistance - plan for Postgress
|
|
- JPA/Hibernate for SQL
|
|
- Flyway for DB schema control
|
|
- JWT for auth
|
|
|
|
## HTTP return codes
|
|
|
|
Application-defined HTTP status codes are generated in `AuthController` for successful registration and in `GlobalExceptionHandler` for API error responses. Spring Security and Spring MVC may still produce framework-level responses for requests that do not reach a controller, such as unauthenticated protected requests or unsupported methods.
|
|
|
|
| HTTP code | Status | Generated in | Trigger | Response body |
|
|
|---:|---|---|---|---|
|
|
| `201` | Created | `AuthController.register` | `POST /auth/register` creates a new user. | `UserResponse` with `id`, `username`, and `createdAt`. |
|
|
| `400` | Bad Request | `GlobalExceptionHandler.handleMalformedJson` | Request body cannot be parsed or converted from JSON. | `ErrorResponse` with `Malformed request body`. |
|
|
| `400` | Bad Request | `GlobalExceptionHandler.handleTypeMismatch` | Path variable or query parameter cannot be converted to the expected type. | `ErrorResponse` with `Invalid request parameter`. |
|
|
| `401` | Unauthorized | `GlobalExceptionHandler.handleAuthentication`; Spring Security filter chain | Authentication fails or an unauthenticated request targets a protected route. | `ErrorResponse` with `Invalid username or password` when handled by `GlobalExceptionHandler`; framework-generated body when rejected before controller handling. |
|
|
| `403` | Forbidden | `GlobalExceptionHandler.handleForbidden` | Authenticated caller is not allowed to perform the requested operation. | `ErrorResponse` with the exception message. |
|
|
| `404` | Not Found | `GlobalExceptionHandler.handleNotFound` | Requested domain resource does not exist or should be hidden from the caller. | `ErrorResponse` with the exception message. |
|
|
| `404` | Not Found | `GlobalExceptionHandler.handleNoResourceFound` | No controller route or static resource matches the request. | `ErrorResponse` with `Not found`. |
|
|
| `409` | Conflict | `GlobalExceptionHandler.handleConflict`; `AuthController.register` throws `ConflictException` | Duplicate username or other persistence/domain uniqueness conflict. | `ErrorResponse` with the exception message. |
|
|
| `422` | Unprocessable Entity | `GlobalExceptionHandler.handleValidation` | Bean Validation fails for a request body annotated with `@Valid`. | `ValidationErrorResponse` with `error` and per-field `fields`. |
|
|
|
|
## Database
|
|
|
|
- The schema is the basic requirements for a User, Note and NoteShare.
|
|
- H2 database is used for development.
|
|
- Flyway will allow us to migrate to Postgres for "production".
|
|
|
|
| Table | Column | Type | Required | Key / Constraint | Description |
|
|
|---|---|---:|:---:|---|---|
|
|
| `users` | `id` | `UUID` | Yes | Primary key | Unique user identifier. |
|
|
| `users` | `username` | `VARCHAR(100)` | Yes | Unique: `uq_users_username` | User login handle. |
|
|
| `users` | `password_hash` | `VARCHAR(255)` | Yes | | BCrypt password hash. |
|
|
| `users` | `created_at` | `TIMESTAMP WITH TIME ZONE` | Yes | | Account creation timestamp. |
|
|
| |
|
|
| `notes` | `id` | `UUID` | Yes | Primary key | Unique note identifier. |
|
|
| `notes` | `owner_id` | `UUID` | Yes | FK to `users(id)`; indexed by `idx_notes_owner_id` | User who owns the note. |
|
|
| `notes` | `title` | `VARCHAR(255)` | Yes | | Note title. |
|
|
| `notes` | `content` | `TEXT` | Yes | | Note body content. |
|
|
| `notes` | `created_at` | `TIMESTAMP WITH TIME ZONE` | Yes | | Note creation timestamp. |
|
|
| `notes` | `updated_at` | `TIMESTAMP WITH TIME ZONE` | Yes | | Last note update timestamp. |
|
|
| |
|
|
| `note_shares` | `id` | `UUID` | Yes | Primary key | Unique share identifier. |
|
|
| `note_shares` | `note_id` | `UUID` | Yes | FK to `notes(id)` with `ON DELETE CASCADE`; indexed by `idx_note_shares_note_id` | Note being shared. |
|
|
| `note_shares` | `shared_with_user_id` | `UUID` | Yes | FK to `users(id)`; indexed by `idx_note_shares_shared_with_user_id` | User receiving read-only access. |
|
|
| `note_shares` | `created_at` | `TIMESTAMP WITH TIME ZONE` | Yes | | Share creation timestamp. |
|
|
| `note_shares` | `note_id`, `shared_with_user_id` | `UUID`, `UUID` | Yes | Unique: `uq_note_shares_note_user` | Prevents duplicate shares for the same note and user. |
|