82 lines
3.4 KiB
Markdown
82 lines
3.4 KiB
Markdown
# Diary
|
|
|
|
A password-protected diary of reviews (books, movies, albums) and notes, built with Nuxt 4 + Nuxt UI on top of two
|
|
self-hosted Baserow tables.
|
|
|
|
```bash
|
|
pnpm install
|
|
cp .env.example .env
|
|
pnpm dev
|
|
```
|
|
|
|
## Configuration
|
|
|
|
| Variable | Purpose |
|
|
|---|---|
|
|
| `NUXT_BASEROW_URL` | Baserow instance, no trailing slash |
|
|
| `NUXT_BASEROW_TOKEN` | Database token with read and create access to both tables |
|
|
| `NUXT_BASEROW_REVIEWS_TABLE_ID` | Reviews table |
|
|
| `NUXT_BASEROW_NOTES_TABLE_ID` | Notes table |
|
|
| `NUXT_DIARY_PASSWORD` | Password that unlocks the diary |
|
|
| `NUXT_SESSION_SECRET` | Signs the login cookie, at least 32 characters (`openssl rand -hex 32`) |
|
|
|
|
In `app/app.config.ts`, `showCovers: true` shows a generated title tile next to each review.
|
|
|
|
## What it does
|
|
|
|
- `/` is the only page: reviews and notes on one timeline, newest first, grouped by year.
|
|
- Only entries with text are shown. Long text is collapsed behind "Read more".
|
|
- Search matches every word against the kind (`review`, `note`), the medium (`book`, `movie`, `album`),
|
|
the date (`aug 2025`, `2025-08`), the title, the author and the text. Matches in the text are highlighted.
|
|
- "Log" opens a form with a Note / Review switch.
|
|
- Until the password is entered, only a password prompt is shown and every API route except
|
|
`/api/session` and `/api/login` returns 401. A correct password sets a signed, http-only cookie valid for 30 days.
|
|
Changing `NUXT_SESSION_SECRET` logs out every browser.
|
|
|
|
## Code layout
|
|
|
|
```
|
|
app/
|
|
app.vue password gate around the page
|
|
pages/index.vue the diary page
|
|
components/
|
|
DiaryEntry.vue one timeline entry (date, title, text, read more)
|
|
LogModal.vue Note / Review switch around the two forms
|
|
NoteForm.vue, ReviewForm.vue
|
|
PasswordModal.vue
|
|
CoverTile.vue generated cover, used when showCovers is on
|
|
composables/
|
|
useDiary.ts loads reviews + notes, searches, sorts and groups them by year
|
|
useSaveEntry.ts shared submit logic of the forms
|
|
useLog.ts open/close state of the Log modal
|
|
utils/ dates, icons, search highlighting, error messages
|
|
types/entry.ts a timeline entry (review or note)
|
|
server/
|
|
middleware/auth.ts locks /api/* until unlocked
|
|
api/ session, login, reviews and notes (GET list, POST create)
|
|
utils/
|
|
baserow.ts Baserow client: listRows, createRow, listFields
|
|
review.ts, note.ts Baserow row -> app type
|
|
auth.ts session cookie and password check
|
|
validation.ts readValidBody(event, zodSchema)
|
|
shared/
|
|
types/ Review, Note
|
|
utils/medium.ts the review media: label and creator label
|
|
utils/schemas.ts zod schemas used by both forms and API
|
|
```
|
|
|
|
## Baserow tables
|
|
|
|
**Reviews:** `title`, `date` (ISO date), `author_or_director`, `review` (long text) and `medium`
|
|
(single select: `Book` / `Movie` / `Album`).
|
|
|
|
**Notes:** `Datum` (date) and `Notes` (long text).
|
|
|
|
Rows without a date are skipped. Text is plain text: line breaks are kept and it is never rendered as HTML.
|
|
|
|
### Adding a medium
|
|
|
|
1. Add the option to the `medium` single select in Baserow.
|
|
2. Add it to `Medium` in `shared/types/review.ts`, to `MEDIUMS` in `shared/utils/medium.ts` (its `label` must match
|
|
the Baserow option) and to `MEDIUM_ICONS` in `app/utils/icons.ts`.
|