217 lines
6.3 KiB
JavaScript
217 lines
6.3 KiB
JavaScript
export default {
|
|
setup(ctx) {
|
|
ctx.db.exec(`
|
|
CREATE TABLE IF NOT EXISTS messages (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
client_id TEXT NOT NULL,
|
|
sender TEXT NOT NULL,
|
|
direction TEXT NOT NULL,
|
|
text TEXT NOT NULL,
|
|
timestamp INTEGER NOT NULL
|
|
);
|
|
`);
|
|
try {
|
|
ctx.db.exec(
|
|
`CREATE INDEX IF NOT EXISTS msg_by_client ON messages(client_id, timestamp)`
|
|
);
|
|
} catch (_) {}
|
|
|
|
const cols = ctx.db
|
|
.prepare(`PRAGMA table_info(messages)`)
|
|
.all()
|
|
.map((r) => r.name);
|
|
if (!cols.includes("attachment_name")) {
|
|
try {
|
|
ctx.db.exec(`ALTER TABLE messages ADD COLUMN attachment_name TEXT`);
|
|
} catch (_) {}
|
|
}
|
|
if (!cols.includes("attachment_mime")) {
|
|
try {
|
|
ctx.db.exec(`ALTER TABLE messages ADD COLUMN attachment_mime TEXT`);
|
|
} catch (_) {}
|
|
}
|
|
if (!cols.includes("attachment_data")) {
|
|
try {
|
|
ctx.db.exec(`ALTER TABLE messages ADD COLUMN attachment_data BLOB`);
|
|
} catch (_) {}
|
|
}
|
|
},
|
|
|
|
onEvent(ctx, clientId, event, payload) {
|
|
if (event === "chat_message") {
|
|
const ts = Date.now();
|
|
const info = ctx.db
|
|
.prepare(
|
|
`INSERT INTO messages(client_id, sender, direction, text, timestamp) VALUES (?, ?, ?, ?, ?)`
|
|
)
|
|
.run(
|
|
clientId,
|
|
payload.from || "Unknown",
|
|
"from_target",
|
|
payload.text || "",
|
|
ts
|
|
);
|
|
ctx.broadcast("new_message", {
|
|
id: Number(info.lastInsertRowid),
|
|
clientId,
|
|
sender: payload.from || "Unknown",
|
|
direction: "from_target",
|
|
text: payload.text || "",
|
|
timestamp: ts,
|
|
});
|
|
}
|
|
if (event === "chat_attachment") {
|
|
const ts = Date.now();
|
|
const name = payload.name || "file";
|
|
const mime = payload.mime || "application/octet-stream";
|
|
let buf = null;
|
|
try {
|
|
buf = Buffer.from(payload.dataB64 || "", "base64");
|
|
} catch (_) {
|
|
buf = null;
|
|
}
|
|
if (!buf || buf.length === 0) return;
|
|
const info = ctx.db
|
|
.prepare(
|
|
`INSERT INTO messages(client_id, sender, direction, text, timestamp, attachment_name, attachment_mime, attachment_data) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
|
|
)
|
|
.run(
|
|
clientId,
|
|
payload.from || "Unknown",
|
|
"from_target",
|
|
"",
|
|
ts,
|
|
name,
|
|
mime,
|
|
buf
|
|
);
|
|
ctx.broadcast("new_message", {
|
|
id: Number(info.lastInsertRowid),
|
|
clientId,
|
|
sender: payload.from || "Unknown",
|
|
direction: "from_target",
|
|
text: "",
|
|
timestamp: ts,
|
|
attachment: { name, mime, size: buf.length },
|
|
});
|
|
}
|
|
if (event === "chat_opened") {
|
|
ctx.broadcast("chat_status", { clientId, status: "opened" });
|
|
}
|
|
if (event === "chat_closed") {
|
|
ctx.broadcast("chat_status", { clientId, status: "closed" });
|
|
}
|
|
},
|
|
|
|
rpc: {
|
|
get_history(ctx, params) {
|
|
return ctx.db
|
|
.prepare(
|
|
`SELECT id, client_id, sender, direction, text, timestamp,
|
|
attachment_name, attachment_mime,
|
|
CASE WHEN attachment_data IS NULL THEN 0 ELSE length(attachment_data) END AS attachment_size
|
|
FROM messages WHERE client_id = ? ORDER BY timestamp ASC LIMIT 500`
|
|
)
|
|
.all(params.clientId)
|
|
.map((r) => ({
|
|
id: r.id,
|
|
clientId: r.client_id,
|
|
sender: r.sender,
|
|
direction: r.direction,
|
|
text: r.text,
|
|
timestamp: r.timestamp,
|
|
attachment: r.attachment_name
|
|
? {
|
|
name: r.attachment_name,
|
|
mime: r.attachment_mime,
|
|
size: r.attachment_size,
|
|
}
|
|
: null,
|
|
}));
|
|
},
|
|
|
|
get_attachment(ctx, params) {
|
|
const row = ctx.db
|
|
.prepare(
|
|
`SELECT attachment_name, attachment_mime, attachment_data FROM messages WHERE id = ?`
|
|
)
|
|
.get(params.id);
|
|
if (!row || !row.attachment_data) {
|
|
return { ok: false, error: "not found" };
|
|
}
|
|
const buf = Buffer.isBuffer(row.attachment_data)
|
|
? row.attachment_data
|
|
: Buffer.from(row.attachment_data);
|
|
return {
|
|
ok: true,
|
|
name: row.attachment_name,
|
|
mime: row.attachment_mime,
|
|
dataB64: buf.toString("base64"),
|
|
};
|
|
},
|
|
|
|
store_message(ctx, params) {
|
|
const ts = Date.now();
|
|
const info = ctx.db
|
|
.prepare(
|
|
`INSERT INTO messages(client_id, sender, direction, text, timestamp) VALUES (?, ?, ?, ?, ?)`
|
|
)
|
|
.run(params.clientId, params.sender, "to_target", params.text, ts);
|
|
ctx.broadcast("new_message", {
|
|
id: Number(info.lastInsertRowid),
|
|
clientId: params.clientId,
|
|
sender: params.sender,
|
|
direction: "to_target",
|
|
text: params.text,
|
|
timestamp: ts,
|
|
});
|
|
return { ok: true, id: Number(info.lastInsertRowid), timestamp: ts };
|
|
},
|
|
|
|
store_attachment(ctx, params) {
|
|
const ts = Date.now();
|
|
const name = params.name || "file";
|
|
const mime = params.mime || "application/octet-stream";
|
|
let buf = null;
|
|
try {
|
|
buf = Buffer.from(params.dataB64 || "", "base64");
|
|
} catch (_) {}
|
|
if (!buf || buf.length === 0) {
|
|
return { ok: false, error: "empty data" };
|
|
}
|
|
const info = ctx.db
|
|
.prepare(
|
|
`INSERT INTO messages(client_id, sender, direction, text, timestamp, attachment_name, attachment_mime, attachment_data) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
|
|
)
|
|
.run(
|
|
params.clientId,
|
|
params.sender,
|
|
"to_target",
|
|
"",
|
|
ts,
|
|
name,
|
|
mime,
|
|
buf
|
|
);
|
|
ctx.broadcast("new_message", {
|
|
id: Number(info.lastInsertRowid),
|
|
clientId: params.clientId,
|
|
sender: params.sender,
|
|
direction: "to_target",
|
|
text: "",
|
|
timestamp: ts,
|
|
attachment: { name, mime, size: buf.length },
|
|
});
|
|
return { ok: true, id: Number(info.lastInsertRowid), timestamp: ts };
|
|
},
|
|
|
|
clear_history(ctx, params) {
|
|
ctx.db
|
|
.prepare(`DELETE FROM messages WHERE client_id = ?`)
|
|
.run(params.clientId);
|
|
ctx.broadcast("history_cleared", { clientId: params.clientId });
|
|
return { ok: true };
|
|
},
|
|
},
|
|
};
|