Add read-only sharing, API tokens and OpenAPI spec
- Share a note as a live read-only page at /s/{token} with optional password and expiry
- Share page renders with the shared parser (TOC, footnotes, math, Mermaid, code highlighting)
and only serves images from the note's own .assets directory
- Shares follow renames made through the API or externally
- Manage shares and API tokens from the command palette and file tree
- Accept read/write API tokens as Bearer credentials; management endpoints require a session
- Serve a hand-written openapi.json, checked against the route table in tests
This commit is contained in:
1 parent
7b076346a4
commit
5ac01894e6
26 files changed
+3147
-52
No files matched your search
@@ -21,6 +21,26 @@ export interface SearchResult {
|
||||
snippet: string
|
||||
}
|
||||
|
||||
export interface Share {
|
||||
token: string
|
||||
path: string
|
||||
createdAt: number
|
||||
// 0 为永久
|
||||
expiresAt: number
|
||||
hasPassword: boolean
|
||||
// 笔记是否仍存在
|
||||
exists: boolean
|
||||
}
|
||||
|
||||
export interface ApiToken {
|
||||
id: string
|
||||
name: string
|
||||
scope: 'read' | 'write'
|
||||
createdAt: number
|
||||
// 0 为从未使用
|
||||
lastUsedAt: number
|
||||
}
|
||||
|
||||
export class ApiError extends Error {
|
||||
constructor(
|
||||
readonly status: number,
|
||||
@@ -106,6 +126,19 @@ export const api = {
|
||||
return (await json<{ link: string }>(request('POST', '/api/v1/assets?note=' + encodeURIComponent(note), { body: form }))).link
|
||||
},
|
||||
|
||||
shares: async (path?: string) =>
|
||||
(await json<{ shares: Share[] }>(request('GET', '/api/v1/shares' + (path ? '?path=' + encodeURIComponent(path) : '')))).shares,
|
||||
// expiresIn 为有效时长(秒),0 为永久
|
||||
createShare: (path: string, password: string, expiresIn: number) =>
|
||||
json<Share>(request('POST', '/api/v1/shares', { json: { path, password, expiresIn } })),
|
||||
deleteShare: (token: string) => request('DELETE', '/api/v1/shares/' + encodeURIComponent(token)),
|
||||
|
||||
tokens: async () => (await json<{ tokens: ApiToken[] }>(request('GET', '/api/v1/tokens'))).tokens,
|
||||
// 返回的 token 明文只有这一次
|
||||
createToken: (name: string, scope: ApiToken['scope']) =>
|
||||
json<{ token: string; info: ApiToken }>(request('POST', '/api/v1/tokens', { json: { name, scope } })),
|
||||
deleteToken: (id: string) => request('DELETE', '/api/v1/tokens/' + encodeURIComponent(id)),
|
||||
|
||||
trash: async () => (await json<{ items: TrashItem[] }>(request('GET', '/api/v1/trash'))).items,
|
||||
restore: (id: string) => json<TrashItem>(request('POST', `/api/v1/trash/${id}/restore`)),
|
||||
purge: (id: string) => request('DELETE', `/api/v1/trash/${id}`),
|
||||
|
||||
Reference in new issue
Block a user