🌿 Collaborative wiki on ATProto lichen.wiki
atproto
14
fork

Configure Feed

Select the types of activity you want to include in your feed.

Update lexicons and blob format

juprodh ce18e8f9 ff90d4d9

+20 -19
+2
lexicons/wiki.lichen.membership.json
··· 12 12 "properties": { 13 13 "memberDid": { 14 14 "type": "string", 15 + "maxLength": 2048, 15 16 "description": "DID of the member being granted access." 16 17 }, 17 18 "wikiRef": { ··· 21 22 }, 22 23 "role": { 23 24 "type": "string", 25 + "maxLength": 32, 24 26 "knownValues": ["admin", "contributor", "viewer"], 25 27 "description": "The member's role in the wiki." 26 28 },
+2
lexicons/wiki.lichen.note.json
··· 12 12 "properties": { 13 13 "slug": { 14 14 "type": "string", 15 + "maxLength": 256, 15 16 "description": "Permanent human-readable identifier, used in URLs. Never changes once set." 16 17 }, 17 18 "title": { 18 19 "type": "string", 20 + "maxLength": 256, 19 21 "description": "Display name for the note. Can be updated freely." 20 22 }, 21 23 "wikiRef": {
+5 -16
lexicons/wiki.lichen.noteRevision.json
··· 22 22 }, 23 23 "diff": { 24 24 "type": "string", 25 + "maxLength": 1000000, 25 26 "description": "The diff content in the format specified by diffFormat." 26 27 }, 27 28 "diffFormat": { 28 29 "type": "string", 30 + "maxLength": 32, 29 31 "knownValues": ["diff-match-patch"], 30 32 "description": "Format of the diff string." 31 33 }, ··· 42 44 "blobs": { 43 45 "type": "array", 44 46 "items": { 45 - "type": "ref", 46 - "ref": "#blobRef" 47 + "type": "blob", 48 + "accept": ["image/jpeg", "image/png", "image/gif", "image/webp"], 49 + "maxSize": 10485760 47 50 }, 48 51 "description": "Blobs referenced by this revision's content." 49 52 } 50 - } 51 - } 52 - }, 53 - "blobRef": { 54 - "type": "object", 55 - "required": ["cid", "mimeType"], 56 - "properties": { 57 - "cid": { 58 - "type": "string", 59 - "description": "CID of the blob." 60 - }, 61 - "mimeType": { 62 - "type": "string", 63 - "description": "MIME type of the blob." 64 53 } 65 54 } 66 55 }
+1
lexicons/wiki.lichen.wiki.json
··· 17 17 }, 18 18 "visibility": { 19 19 "type": "string", 20 + "maxLength": 32, 20 21 "knownValues": ["public", "private"], 21 22 "description": "Access level." 22 23 },
+4 -3
public/editor/upload.ts
··· 1 1 import type { EditorView } from "codemirror"; 2 2 3 - const uploadedBlobs = new Map<string, string>(); // cid -> mimeType 3 + const uploadedBlobs = new Map<string, { mimeType: string; size: number }>(); // cid -> blob meta 4 4 5 5 export async function uploadImage( 6 6 file: File, ··· 24 24 url?: string; 25 25 cid?: string | null; 26 26 mimeType?: string; 27 + size?: number; 27 28 error?: string; 28 29 }; 29 30 ··· 45 46 }); 46 47 } 47 48 48 - if (json.cid && json.mimeType) { 49 - uploadedBlobs.set(json.cid, json.mimeType); 49 + if (json.cid && json.mimeType && json.size) { 50 + uploadedBlobs.set(json.cid, { mimeType: json.mimeType, size: json.size }); 50 51 syncBlobMetadata(); 51 52 } 52 53 } catch (err) {
+6
src/server/db/queries/revision.ts
··· 127 127 128 128 const db = getDb(); 129 129 130 + // Idempotent: skip if already ingested (optimistic write beat the firehose) 131 + const exists = db 132 + .query("SELECT 1 FROM revisions WHERE at_uri = ?") 133 + .get(revisionAtUri); 134 + if (exists) return; 135 + 130 136 const current = db 131 137 .query("SELECT content FROM current_note WHERE note_at_uri = ?") 132 138 .get(noteAtUri) as { content: string } | null;