feat(ui): Ghibli/Miyazaki reskin + Obsidian docs vault + project audit

UI: warm daylight design system (Tailwind v4 @theme palette, gh-* component
classes, watercolor grain, Zen Maru Gothic + Klee One fonts), animated SSR-safe
GhibliBackground (drifting clouds, meadow hills, soot sprites), and a full reskin
of navbar, connect button, dapp page, loan cards, settings modal, and readme.
Fixes the bg-white-on-dark loan-card inconsistency. Web3/business logic untouched.

Docs: converted docs/ into an Obsidian vault (frontmatter, [[wikilinks]],
callouts, Home MOC, folders Architecture/Operations/Audits) and added a
full-project audit note (Project Audit 2026-06). Redacted a real leaked Schedy
key value from the security audit example (rotate it at Schedy).

Also commits the previously-untracked server layer: app/api (cron + tasks routes)
and lib (redis, ssrf-guard, task-store).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Siavash Sameni
2026-06-14 08:13:53 +04:00
parent cf76322008
commit 6ae581ab2e
25 changed files with 4245 additions and 369 deletions

View File

@@ -0,0 +1,211 @@
---
title: API Reference
tags: [mortgagefi, api, reference]
type: reference
status: stable
updated: 2026-06-14
---
# API Reference
## nftcache API
Base URL: `http://localhost:8090` (or `/nftcache` when proxied)
### Authentication
> [!info]
> If `NFTCACHE_API_KEY` is configured, include it in the `X-API-Key` header for all requests.
---
### GET /nfts
Returns token IDs owned by a specific wallet for a given NFT contract.
**Query Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `network` | string | Yes | Network key: `eth`, `arb`, `base` |
| `nft_contract` | string | Yes | Contract address or config slug (e.g., `cbbtc`) |
| `user_wallet` | string | Yes | Owner wallet address |
**Response (200):**
```json
{
"token_ids": ["1", "5", "42"],
"source": "cache"
}
```
**Sources:**
- `cache` — Returned from BadgerDB cache
- `refreshed` — Fetched live from RPC and cached
**Errors:**
- `400` — Missing required parameters
- `401` — Invalid or missing API key
- `502` — RPC fetch error (rate limit, node unavailable)
---
### POST /nfts/refresh
Force a full refresh of the contract ownership cache.
**Query Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `network` | string | Yes | Network key |
| `nft_contract` | string | Yes | Contract address or slug |
**Response:**
- `204 No Content` — Success
- `401` — Unauthorized
- `502` — RPC error
---
### POST /nfts/invalidate
Delete a contract's cache entry.
**Query Parameters:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `network` | string | Yes | Network key |
| `nft_contract` | string | Yes | Contract address |
**Response:**
- `204 No Content` — Success
- `401` — Unauthorized
---
## Schedy API
Base URL: `http://localhost:8080` (or `/schedy` when proxied)
### Authentication
> [!info]
> If `SCHEDY_API_KEY` is configured, include it in the `X-API-Key` header for all requests.
---
### POST /tasks
Create a new scheduled task.
**Request Body:**
```json
{
"url": "https://ntfy.sh/mytopic",
"headers": {
"Content-Type": "text/plain",
"X-Email": "user@example.com"
},
"payload": "Your position is approaching liquidation",
"execute_at": "2026-05-10T12:00:00Z",
"retries": 3,
"retry_interval": 2000
}
```
**Fields:**
| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `url` | string | Yes | — | Webhook target URL |
| `headers` | object | No | `{}` | HTTP headers to send |
| `payload` | any | No | `null` | Request body (string or JSON) |
| `execute_at` | string | Yes | — | ISO 8601 / RFC3339 timestamp |
| `retries` | int | No | `0` | Max retry attempts on failure |
| `retry_interval` | int | No | `2000` | Milliseconds between retries |
**Response (201):**
```json
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"url": "https://ntfy.sh/mytopic",
"execute_at": "2026-05-10T12:00:00Z",
"headers": { "Content-Type": "text/plain" },
"payload": "Your position is approaching liquidation",
"retries": 3,
"retry_interval": 2000
}
```
**Errors:**
- `400` — Invalid body or timestamp
- `400` — Timestamp is in the past
- `401` — Missing API key
- `403` — Invalid API key
---
### GET /tasks
List all scheduled tasks.
**Response (200):**
```json
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"url": "https://ntfy.sh/mytopic",
"execute_at": "2026-05-10T12:00:00Z",
"headers": {},
"payload": "...",
"retries": 3,
"retry_interval": 2000
}
]
```
---
### DELETE /tasks/{id}
Delete a scheduled task by ID.
**Response:**
- `204 No Content` — Success
- `404` — Task not found
- `401` / `403` — Auth error
---
## ntfy API (Proxied)
Base URL: `http://localhost/ntfy` (when behind nginx)
### POST /{topic}
Publish a message to a topic.
**Request Headers:**
| Header | Description |
|--------|-------------|
| `Content-Type: text/plain` | Message body is plain text |
| `X-Email: user@example.com` | Forward message via email |
| `X-Priority: 5` | Message priority (1-5) |
**Example:**
```bash
curl -X POST -H 'Content-Type: text/plain' -H 'X-Email: user@example.com' \
http://localhost/ntfy/mytopic \
-d 'Your position is approaching liquidation'
```
**Response:**
- `200 OK` — Message published
### Web UI
Access `http://localhost/ntfy/` to use the ntfy web interface for subscribing to topics.
## Related
- [[Home]]
- [[Architecture]]
- [[Deployment]]