@coffer-org/server 2.2.2 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/entity-schema.js +1 -0
- package/dist/thread-store.d.ts +8 -0
- package/dist/thread-store.js +23 -1
- package/dist/uploads.d.ts +10 -0
- package/dist/uploads.js +24 -1
- package/package.json +1 -1
package/dist/entity-schema.js
CHANGED
|
@@ -161,6 +161,7 @@ export const ThreadMessageSchema = new EntitySchema({
|
|
|
161
161
|
role: { type: 'text' },
|
|
162
162
|
sender: { type: 'text', nullable: true },
|
|
163
163
|
text: { type: 'text' },
|
|
164
|
+
attachments: { type: 'text', nullable: true },
|
|
164
165
|
ts: { type: 'integer' },
|
|
165
166
|
reply_to_id: { type: 'text', nullable: true },
|
|
166
167
|
},
|
package/dist/thread-store.d.ts
CHANGED
|
@@ -3,9 +3,16 @@ export interface StoredMsg {
|
|
|
3
3
|
role: 'user' | 'assistant' | 'reasoning';
|
|
4
4
|
sender: string | null;
|
|
5
5
|
text: string;
|
|
6
|
+
attachments?: StoredAttachment[];
|
|
6
7
|
ts: number;
|
|
7
8
|
replyToId: string | null;
|
|
8
9
|
}
|
|
10
|
+
export interface StoredAttachment {
|
|
11
|
+
name: string;
|
|
12
|
+
mime?: string;
|
|
13
|
+
size?: number;
|
|
14
|
+
label?: string;
|
|
15
|
+
}
|
|
9
16
|
export interface ThreadChat {
|
|
10
17
|
chatId: string;
|
|
11
18
|
lastTs: number;
|
|
@@ -19,6 +26,7 @@ export declare function putThreadMessage(m: {
|
|
|
19
26
|
msgId: string;
|
|
20
27
|
role: 'user' | 'assistant' | 'reasoning';
|
|
21
28
|
sender?: string | null;
|
|
29
|
+
attachments?: StoredAttachment[];
|
|
22
30
|
text: string;
|
|
23
31
|
ts: number;
|
|
24
32
|
replyToId: string | null;
|
package/dist/thread-store.js
CHANGED
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
import { getEm } from "./db.js";
|
|
2
2
|
function toStored(r) {
|
|
3
|
-
|
|
3
|
+
let attachments;
|
|
4
|
+
if (r.attachments) {
|
|
5
|
+
try {
|
|
6
|
+
const parsed = JSON.parse(r.attachments);
|
|
7
|
+
if (Array.isArray(parsed)) {
|
|
8
|
+
const valid = parsed.filter((v) => typeof v === 'object' && v !== null && typeof v.name === 'string');
|
|
9
|
+
if (valid.length)
|
|
10
|
+
attachments = valid;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
msgId: r.msg_id,
|
|
18
|
+
role: r.role,
|
|
19
|
+
sender: r.sender,
|
|
20
|
+
text: r.text,
|
|
21
|
+
...(attachments ? { attachments } : {}),
|
|
22
|
+
ts: r.ts,
|
|
23
|
+
replyToId: r.reply_to_id,
|
|
24
|
+
};
|
|
4
25
|
}
|
|
5
26
|
export async function getThreadMessage(connector, chatId, msgId) {
|
|
6
27
|
const em = getEm().fork();
|
|
@@ -15,6 +36,7 @@ export async function putThreadMessage(m) {
|
|
|
15
36
|
msg_id: m.msgId,
|
|
16
37
|
role: m.role,
|
|
17
38
|
sender: m.sender ?? null,
|
|
39
|
+
attachments: m.attachments?.length ? JSON.stringify(m.attachments) : null,
|
|
18
40
|
text: m.text,
|
|
19
41
|
ts: m.ts,
|
|
20
42
|
reply_to_id: m.replyToId,
|
package/dist/uploads.d.ts
CHANGED
|
@@ -2,4 +2,14 @@ export declare function uploadsDir(): string;
|
|
|
2
2
|
export declare function seedAsset(absPath: string): {
|
|
3
3
|
name: string;
|
|
4
4
|
};
|
|
5
|
+
export interface StoredUpload {
|
|
6
|
+
name: string;
|
|
7
|
+
mime?: string;
|
|
8
|
+
size: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function saveUploadBytes(bytes: Uint8Array, opts?: {
|
|
11
|
+
originalName?: string;
|
|
12
|
+
mime?: string;
|
|
13
|
+
maxBytes?: number;
|
|
14
|
+
}): Promise<StoredUpload>;
|
|
5
15
|
export declare function seedAssetPath(metaUrl: string): (rel: string) => string;
|
package/dist/uploads.js
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { copyFileSync, mkdirSync } from 'node:fs';
|
|
2
|
-
import {
|
|
2
|
+
import { writeFile } from 'node:fs/promises';
|
|
3
|
+
import { basename, extname, join } from 'node:path';
|
|
4
|
+
import { randomUUID } from 'node:crypto';
|
|
3
5
|
import { fileURLToPath } from 'node:url';
|
|
6
|
+
const MIME_EXT = {
|
|
7
|
+
'image/jpeg': '.jpg',
|
|
8
|
+
'image/png': '.png',
|
|
9
|
+
'image/gif': '.gif',
|
|
10
|
+
'image/webp': '.webp',
|
|
11
|
+
'application/pdf': '.pdf',
|
|
12
|
+
'text/plain': '.txt',
|
|
13
|
+
'text/csv': '.csv',
|
|
14
|
+
'application/json': '.json',
|
|
15
|
+
};
|
|
4
16
|
export function uploadsDir() {
|
|
5
17
|
const dir = process.env.UPLOADS ?? join(process.cwd(), 'data', 'uploads');
|
|
6
18
|
mkdirSync(dir, { recursive: true });
|
|
@@ -11,6 +23,17 @@ export function seedAsset(absPath) {
|
|
|
11
23
|
copyFileSync(absPath, join(uploadsDir(), name));
|
|
12
24
|
return { name };
|
|
13
25
|
}
|
|
26
|
+
export async function saveUploadBytes(bytes, opts = {}) {
|
|
27
|
+
const maxBytes = opts.maxBytes ?? 25 * 1024 * 1024;
|
|
28
|
+
if (bytes.byteLength > maxBytes)
|
|
29
|
+
throw new Error(`upload exceeds ${maxBytes} bytes`);
|
|
30
|
+
const originalExt = opts.originalName ? extname(basename(opts.originalName)).toLowerCase() : '';
|
|
31
|
+
const ext = originalExt && /^[.][a-z0-9]{1,10}$/.test(originalExt) ? originalExt : MIME_EXT[opts.mime ?? ''] ?? '';
|
|
32
|
+
const name = `${randomUUID()}${ext}`;
|
|
33
|
+
await writeFile(join(uploadsDir(), name), bytes);
|
|
34
|
+
const mime = opts.mime === 'application/octet-stream' ? undefined : opts.mime;
|
|
35
|
+
return { name, ...(mime ? { mime } : {}), size: bytes.byteLength };
|
|
36
|
+
}
|
|
14
37
|
export function seedAssetPath(metaUrl) {
|
|
15
38
|
return (rel) => fileURLToPath(new URL(`../../seed/assets/${rel}`, metaUrl));
|
|
16
39
|
}
|