Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 51 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,22 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Development Commands

- **Dev server**: `npm run dev` (starts Astro dev server on localhost:4321)
- **Build**: `npm run build` (fetches GitHub stars, then generates static site in `dist/`)
- **Dev server (with DB)**: `npm run dev:remote` (starts dev server with remote database connectivity)
- **Build**: `npm run build` (fetches GitHub stars, then generates static site in `dist/` with remote DB)
- **Preview**: `npm run preview` (serves built site for testing)
- **Tests**: `npm run test:e2e` (runs Playwright E2E tests)
- **Single test**: `npx playwright test tests/specific-test.spec.ts`
- **Format**: `npx prettier --write .`
- **Fetch GitHub stars**: `npm run fetch-stars` (updates cached star data from GitHub REST API)
- **Database push**: `npm run db:push` (pushes local schema changes to remote database)
- I'll be running the dev server in the background
- To view GitHub dependabot autodetected vulnerabilities, use 'gh api repos/Jinksi/ericjinks.com/dependabot/alerts'

## Secrets and Credentials

- The GH_TOKEN is in the `.env` file
- Authentication credentials (ADMIN_USERNAME, ADMIN_PASSWORD, ADMIN_SECRET) are in `.env`
- Database credentials (ASTRO_DB_REMOTE_URL, ASTRO_DB_APP_TOKEN) are in `.env` for Turso integration
- Uses Astro 5.x `astro:env` system for type-safe environment variable access

## Architecture Overview
Expand All @@ -26,6 +29,7 @@ This is a personal blog/portfolio website built with **Astro** as the primary fr

### Tech Stack
- **Framework**: Astro 5.x (static site generation with hybrid SSR)
- **Database**: Astro DB (LibSQL/SQLite with local development and remote production)
- **UI Libraries**: React 18, Svelte 5, TypeScript
- **Content**: Astro Content Collections with Zod schema validation
- **Styling**: SCSS with CSS custom properties, dark/light theme support
Expand All @@ -45,6 +49,10 @@ This is a personal blog/portfolio website built with **Astro** as the primary fr
- `react/` subdirectory for React components requiring client-side interactivity
- `ml/` subdirectory for TensorFlow.js machine learning demos

- **`src/actions/`**: Astro Actions (server-side functions)
- `index.ts` - Type-safe server actions for database operations
- Exports `getCounter` and `incrementCounter` actions for admin functionality

- **`src/sketches/`**: Canvas-based generative art using canvas-sketch library
- Each sketch has corresponding `.ts`/`.tsx` file and MDX content file
- Uses Two.js, canvas-sketch, and custom Vector utilities
Expand All @@ -62,9 +70,13 @@ This is a personal blog/portfolio website built with **Astro** as the primary fr
- Fetched via REST API using `scripts/fetch-github-stars.js`
- **Authentication**:
- `/login/` - Admin login page with form-based authentication
- `/admin/` - Protected admin dashboard (requires authentication)
- `/admin/` - Protected admin dashboard with interactive counter functionality
- `/api/logout/` - Logout endpoint supporting both GET and POST requests

- **`db/`**: Database configuration and seeding
- `config.ts` - Astro DB schema definition with Counter table
- `seed.ts` - Database seeding script for initial data

### Content Management

Content is managed through Astro Content Collections with strict TypeScript schemas:
Expand Down Expand Up @@ -124,6 +136,43 @@ Single-user admin authentication with the following components:
- Creates cycling blocks for automated attacks while allowing legitimate use
- Prevents brute force attacks and conserves Netlify function invocations

### Database System

Astro DB integration for persistent data storage:

- **Database Engine**: LibSQL (SQLite-compatible) with local development and remote production
- **Schema Definition** (`db/config.ts`):
- `Counter` table with `id` (text, primary key), `name` (text, unique), `count` (number, default 0)
- Uses Astro's `defineDb` and `defineTable` for type-safe schema definitions

- **Data Seeding** (`db/seed.ts`):
- Initialises database with `adminCounter` starting at 0
- Uses `crypto.randomUUID()` for unique ID generation

- **Server Actions** (`src/actions/index.ts`):
- Type-safe server-side functions using Astro Actions
- `getCounter`: Retrieves counter by name with error handling
- `incrementCounter`: Atomically increments counter value and returns new count
- Uses Drizzle ORM queries with `eq()` for safe SQL operations

- **Development Setup**:
- Local SQLite database for development (`npm run dev`)
- Remote database connectivity for production builds (`npm run dev:remote`, `npm run build`)
- Schema changes pushed to remote with `npm run db:push`
- Automatic table creation and seeding on first run

- **Remote Database (Turso)**:
- **Required Environment Variables**:
- `ASTRO_DB_REMOTE_URL`: Connection URL to LibSQL database (e.g., `libsql://your-db.turso.io`)
- `ASTRO_DB_APP_TOKEN`: Authentication token for remote database access
- Setup via [Turso](https://turso.tech/) for production LibSQL hosting
- Compatible with Astro DB's LibSQL adapter for seamless local/remote development

- **Client Integration**:
- Admin dashboard uses `Astro.callAction()` for server-side data fetching
- Client-side JavaScript calls actions for interactive updates
- Seamless hydration between server and client state

### Testing Strategy

Playwright E2E tests configured to:
Expand Down
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,24 @@ The site includes a secure admin area:
### Environment Variables Required

```bash
# Authentication
ADMIN_USERNAME=your-admin-username
ADMIN_PASSWORD=your-super-secret-password
ADMIN_SECRET=your-32-char-secret-key

# Database (Turso)
ASTRO_DB_REMOTE_URL=libsql://your-database.turso.io
ASTRO_DB_APP_TOKEN=your-turso-auth-token
```

Generate secure secret: `node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"`
**Setup Commands:**

```bash
# Generate secure authentication secret
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

# Turso database setup (requires Turso CLI)
turso db create your-database-name # Create database
turso db show your-database-name # Get database URL
turso db tokens create your-database-name # Generate auth token
```
14 changes: 8 additions & 6 deletions astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pagefind from 'astro-pagefind'
import { defineConfig, envField } from 'astro/config'

import db from '@astrojs/db'
import mdx from '@astrojs/mdx'
import netlify from '@astrojs/netlify'
import react from '@astrojs/react'
Expand All @@ -24,6 +25,7 @@ export default defineConfig({
applyBaseStyles: false,
}),
pagefind(),
db(),
],
trailingSlash: 'always',
markdown: {
Expand All @@ -49,17 +51,17 @@ export default defineConfig({
schema: {
ADMIN_USERNAME: envField.string({
context: 'server',
access: 'secret'
access: 'secret',
}),
ADMIN_PASSWORD: envField.string({
context: 'server',
access: 'secret'
access: 'secret',
}),
ADMIN_SECRET: envField.string({
context: 'server',
access: 'secret'
})
access: 'secret',
}),
},
validateSecrets: true
}
validateSecrets: true,
},
})
13 changes: 13 additions & 0 deletions db/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { column, defineDb, defineTable } from 'astro:db'

const Counter = defineTable({
columns: {
id: column.text({ primaryKey: true }),
name: column.text({ unique: true }),
count: column.number({ default: 0 }),
},
})

export default defineDb({
tables: { Counter },
})
8 changes: 8 additions & 0 deletions db/seed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Counter, db } from 'astro:db'

// https://astro.build/db/seed
export default async function seed() {
await db
.insert(Counter)
.values([{ id: crypto.randomUUID(), name: 'adminCounter', count: 0 }])
}
Loading