cleanup
This commit is contained in:
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"title": "syui.ai",
|
|
||||||
"handle": "syui.syui.ai",
|
|
||||||
"collection": "ai.syui.log.post",
|
|
||||||
"network": "syu.is",
|
|
||||||
"color": "#EF454A",
|
|
||||||
"siteUrl": "https://syui.ai"
|
|
||||||
}
|
|
||||||
12
network.json
12
network.json
@@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"bsky.social": {
|
|
||||||
"plc": "https://plc.directory",
|
|
||||||
"bsky": "https://public.api.bsky.app",
|
|
||||||
"web": "https://bsky.app"
|
|
||||||
},
|
|
||||||
"syu.is": {
|
|
||||||
"plc": "https://plc.syu.is",
|
|
||||||
"bsky": "https://bsky.syu.is",
|
|
||||||
"web": "https://syu.is"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
345
readme.md
345
readme.md
@@ -1,346 +1,7 @@
|
|||||||
# ailog
|
# ailog
|
||||||
|
|
||||||
ATProto-based blog platform built on at-browser.
|
```sh
|
||||||
|
$ vim public/config.json
|
||||||
## Concept
|
$ npm run build
|
||||||
|
|
||||||
**Data lives in ATProto, not on this server.**
|
|
||||||
|
|
||||||
This is not a traditional blog generator. It's a **viewer (client)** for ATProto records.
|
|
||||||
|
|
||||||
```
|
|
||||||
Traditional blog:
|
|
||||||
Server DB ← article data ← user
|
|
||||||
|
|
||||||
ATProto blog:
|
|
||||||
User's PDS ← article data (ai.syui.log.post)
|
|
||||||
↓
|
|
||||||
at-browser (this site) → displays records
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────┐
|
|
||||||
│ at-browser │
|
|
||||||
│ (ATProto record viewer/editor) │
|
|
||||||
├─────────────────────────────────────────┤
|
|
||||||
│ │
|
|
||||||
│ / → admin (config.json) │
|
|
||||||
│ /@alice → user page │
|
|
||||||
│ /@bob.bsky → user page │
|
|
||||||
│ │
|
|
||||||
└─────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
## Roles
|
|
||||||
|
|
||||||
| Role | Path | Data Source |
|
|
||||||
|------|------|-------------|
|
|
||||||
| **admin** | `/` (root) | local + remote |
|
|
||||||
| **user** | `/@handle` | remote only |
|
|
||||||
|
|
||||||
### Admin (Site Owner)
|
|
||||||
|
|
||||||
- Defined in `config.json`
|
|
||||||
- Has root (`/`) access
|
|
||||||
- Can reference **local files** (static assets, custom styles)
|
|
||||||
- Can reference **remote** (ATProto records)
|
|
||||||
|
|
||||||
### User (Any ATProto User)
|
|
||||||
|
|
||||||
- Accessed via `/@handle` path
|
|
||||||
- **Remote only** (ATProto records from their PDS)
|
|
||||||
- No registration required
|
|
||||||
- Anyone with an ATProto account can be displayed
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
### 1. at-browser (Core)
|
|
||||||
|
|
||||||
- Search by handle/DID
|
|
||||||
- Browse PDS collections
|
|
||||||
- Navigate ATProto records
|
|
||||||
|
|
||||||
### 2. ai.syui.log.post View
|
|
||||||
|
|
||||||
- Markdown rendering
|
|
||||||
- Syntax highlighting
|
|
||||||
- Blog-style display
|
|
||||||
|
|
||||||
### 3. OAuth
|
|
||||||
|
|
||||||
- Login with ATProto
|
|
||||||
- Post to ai.syui.log.post collection
|
|
||||||
|
|
||||||
## Use Cases
|
|
||||||
|
|
||||||
### Personal Blog
|
|
||||||
|
|
||||||
```json
|
|
||||||
// config.json
|
|
||||||
{
|
|
||||||
"did": "did:plc:xxxxx",
|
|
||||||
"handle": "syui.syui.ai"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- Deploy to `syui.ai`
|
|
||||||
- Root shows your profile + posts
|
|
||||||
- You are the admin (local + remote)
|
|
||||||
- Others can view via `/@handle`
|
|
||||||
|
|
||||||
### Blog Service
|
|
||||||
|
|
||||||
```json
|
|
||||||
// config.json
|
|
||||||
{
|
|
||||||
"admin": "service.example.com",
|
|
||||||
"handle": null
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- Deploy to `blog.example.com`
|
|
||||||
- Root shows landing/search
|
|
||||||
- All users via `/@handle` (remote only)
|
|
||||||
- Platform for any ATProto user
|
|
||||||
|
|
||||||
## Data Flow
|
|
||||||
|
|
||||||
```
|
|
||||||
┌──────────────┐ ┌──────────────┐
|
|
||||||
│ User's PDS │────→│ at-browser │
|
|
||||||
│ (ATProto) │←────│ (this site) │
|
|
||||||
└──────────────┘ └──────────────┘
|
|
||||||
↑ │
|
|
||||||
│ ↓
|
|
||||||
ai.syui.log.post ┌──────────┐
|
|
||||||
collection │ Display │
|
|
||||||
│ - Profile│
|
|
||||||
│ - Posts │
|
|
||||||
└──────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
## Local = Remote (Same Format)
|
|
||||||
|
|
||||||
**Critical design principle: local files use the exact same format as ATProto API responses.**
|
|
||||||
|
|
||||||
This allows the same code to handle both data sources.
|
|
||||||
|
|
||||||
### Remote (ATProto API)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl "https://syu.is/xrpc/com.atproto.repo.listRecords?repo=did:plc:xxx&collection=ai.syui.log.post"
|
|
||||||
```
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"records": [
|
|
||||||
{
|
|
||||||
"uri": "at://did:plc:xxx/ai.syui.log.post/3xxx",
|
|
||||||
"cid": "bafyrei...",
|
|
||||||
"value": {
|
|
||||||
"title": "Hello World",
|
|
||||||
"content": "# Hello\n\nThis is my post.",
|
|
||||||
"createdAt": "2025-01-01T00:00:00Z"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Local (Static File)
|
|
||||||
|
|
||||||
```
|
|
||||||
content/
|
|
||||||
└── did:plc:xxx/
|
|
||||||
├── describe.json # describeRepo (special)
|
|
||||||
├── app.bsky.actor.profile/
|
|
||||||
│ └── self.json # {collection}/{rkey}.json
|
|
||||||
└── ai.syui.log.post/
|
|
||||||
└── 3xxx.json # {collection}/{rkey}.json
|
|
||||||
```
|
|
||||||
|
|
||||||
```json
|
|
||||||
// content/did:plc:xxx/ai.syui.log.post/3xxx.json
|
|
||||||
{
|
|
||||||
"uri": "at://did:plc:xxx/ai.syui.log.post/3xxx",
|
|
||||||
"cid": "bafyrei...",
|
|
||||||
"value": {
|
|
||||||
"title": "Hello World",
|
|
||||||
"content": "# Hello\n\nThis is my post.",
|
|
||||||
"createdAt": "2025-01-01T00:00:00Z"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### ATProto API Reference
|
|
||||||
|
|
||||||
| API | Path | Description |
|
|
||||||
|-----|------|-------------|
|
|
||||||
| getRecord | `/xrpc/com.atproto.repo.getRecord` | Get single record |
|
|
||||||
| listRecords | `/xrpc/com.atproto.repo.listRecords` | List records in collection |
|
|
||||||
| describeRepo | `/xrpc/com.atproto.repo.describeRepo` | Get repo info + collections list |
|
|
||||||
|
|
||||||
See: [com.atproto.repo.describeRepo](https://docs.bsky.app/docs/api/com-atproto-repo-describe-repo)
|
|
||||||
|
|
||||||
### Resolution Strategy
|
|
||||||
|
|
||||||
```
|
|
||||||
at-browser
|
|
||||||
│
|
|
||||||
├── admin (config.json user)
|
|
||||||
│ ├── 1. Check local: /content/{did}/{collection}/{rkey}.json
|
|
||||||
│ └── 2. Fallback to remote: PDS API
|
|
||||||
│
|
|
||||||
└── user (/@handle)
|
|
||||||
└── remote only: PDS API
|
|
||||||
```
|
|
||||||
|
|
||||||
### Why Same Format?
|
|
||||||
|
|
||||||
- **One codebase**: No branching logic for local vs remote
|
|
||||||
- **Easy testing**: Copy API response to local file
|
|
||||||
- **Offline support**: Admin can work with local files
|
|
||||||
- **Migration**: Local → Remote (just POST to PDS)
|
|
||||||
|
|
||||||
## Config
|
|
||||||
|
|
||||||
### config.json
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"did": "did:plc:xxxxx",
|
|
||||||
"handle": "syui.syui.ai",
|
|
||||||
"pds": "syu.is",
|
|
||||||
"collection": "ai.syui.log.post"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Tech Stack
|
|
||||||
|
|
||||||
- **CLI**: Rust (ailog)
|
|
||||||
- **Frontend**: Vite + TypeScript
|
|
||||||
- **ATProto**: @atproto/api
|
|
||||||
- **OAuth**: @atproto/oauth-client-browser
|
|
||||||
- **Markdown**: marked + highlight.js
|
|
||||||
|
|
||||||
## CLI (ailog)
|
|
||||||
|
|
||||||
### Install
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo build --release
|
|
||||||
cp target/release/ailog ~/.local/bin/
|
|
||||||
```
|
|
||||||
|
|
||||||
### Commands
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Login to ATProto PDS
|
|
||||||
ailog login <handle> -p <password> [-s <server>]
|
|
||||||
|
|
||||||
# Post a record
|
|
||||||
ailog post <file.json> -c <collection> [-r <rkey>]
|
|
||||||
|
|
||||||
# Get records from collection
|
|
||||||
ailog get -c <collection> [-l <limit>]
|
|
||||||
|
|
||||||
# Delete a record
|
|
||||||
ailog delete -c <collection> -r <rkey>
|
|
||||||
|
|
||||||
# Sync PDS data to local content directory
|
|
||||||
ailog sync [-o <output>]
|
|
||||||
|
|
||||||
# Generate lexicon Rust code from ATProto lexicons
|
|
||||||
ailog gen [-i <input>] [-o <output>]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Example
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Login
|
|
||||||
ailog login syui.syui.ai -p "app-password" -s syu.is
|
|
||||||
|
|
||||||
# Post
|
|
||||||
echo '{"title":"Hello","content":"World","createdAt":"2025-01-01T00:00:00Z"}' > post.json
|
|
||||||
ailog post post.json -c ai.syui.log.post
|
|
||||||
|
|
||||||
# Sync to local
|
|
||||||
ailog sync -o content
|
|
||||||
```
|
|
||||||
|
|
||||||
### Project Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
src/
|
|
||||||
├── main.rs
|
|
||||||
├── commands/
|
|
||||||
│ ├── mod.rs
|
|
||||||
│ ├── auth.rs # login, refresh session
|
|
||||||
│ ├── token.rs # token management
|
|
||||||
│ ├── post.rs # post, get, delete, sync
|
|
||||||
│ └── gen.rs # lexicon code generation
|
|
||||||
└── lexicons/
|
|
||||||
└── mod.rs # auto-generated from ATProto lexicons
|
|
||||||
```
|
|
||||||
|
|
||||||
### Lexicon Generation
|
|
||||||
|
|
||||||
Generate Rust endpoint definitions from ATProto lexicon JSON files:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Clone atproto repo (if not exists)
|
|
||||||
git clone https://github.com/bluesky-social/atproto repos/atproto
|
|
||||||
|
|
||||||
# Generate lexicons
|
|
||||||
ailog gen -i ./repos/atproto/lexicons -o ./src/lexicons
|
|
||||||
|
|
||||||
# Rebuild
|
|
||||||
cargo build
|
|
||||||
```
|
|
||||||
|
|
||||||
## Collection Schema
|
|
||||||
|
|
||||||
### ai.syui.log.post
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"title": "Post Title",
|
|
||||||
"content": "Markdown content...",
|
|
||||||
"createdAt": "2025-01-01T00:00:00Z"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Assets
|
|
||||||
|
|
||||||
### PNG to SVG Conversion (Vector Trace)
|
|
||||||
|
|
||||||
Convert PNG images to true vector SVG using vtracer (Rust):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install vtracer
|
|
||||||
cargo install vtracer
|
|
||||||
|
|
||||||
# Convert PNG to SVG (color mode)
|
|
||||||
vtracer --input input.png --output output.svg --colormode color
|
|
||||||
|
|
||||||
# Convert PNG to SVG (black and white)
|
|
||||||
vtracer --input input.png --output output.svg
|
|
||||||
```
|
|
||||||
|
|
||||||
**Options:**
|
|
||||||
- `--colormode color` : Preserve colors (recommended for icons)
|
|
||||||
- `--colormode binary` : Black and white only
|
|
||||||
- `--filter_speckle 4` : Remove small artifacts
|
|
||||||
- `--corner_threshold 60` : Adjust corner detection
|
|
||||||
|
|
||||||
**Alternative tools:**
|
|
||||||
- potrace: `potrace input.pbm -s -o output.svg` (B&W only, requires PBM input)
|
|
||||||
- Inkscape CLI: `inkscape input.png --export-type=svg` (embeds image, no trace)
|
|
||||||
|
|
||||||
**Note:** Inkscape's CLI `--export-type=svg` only embeds the PNG, it does not trace. For true vectorization, use vtracer or potrace.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
MIT
|
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ struct DescribeRepoResponse {
|
|||||||
|
|
||||||
/// Sync PDS data to local content directory
|
/// Sync PDS data to local content directory
|
||||||
pub async fn sync_to_local(output: &str) -> Result<()> {
|
pub async fn sync_to_local(output: &str) -> Result<()> {
|
||||||
let config_content = fs::read_to_string("config.json")
|
let config_content = fs::read_to_string("public/config.json")
|
||||||
.context("config.json not found")?;
|
.context("config.json not found")?;
|
||||||
let config: Config = serde_json::from_str(&config_content)?;
|
let config: Config = serde_json::from_str(&config_content)?;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user