Add workspaces, conversation sessions, app-managed computers and pictures
- Rail: Add icon with stacked form modals (bot, workspace, computer), coordinator, recent bots with "See all bots", Workspaces section, and a workspace mode listing only its crew - Workspaces: page for context, bots (per-workspace instructions and memory), computers (default, link, unlink), models and delete; conversations scoped per bot per workspace - Sessions: conversations split into searchable, reopenable sessions with carry-over summaries; sessions and new-session header icons - Computers: created and provisioned over the docker socket, home dir kept at DATA_DIR/computers/<id> after delete; super admins only - Models: workspace settings win over a bot's own, then the defaults - Pictures for bots and workspaces; bots editable from the header - Coordinator tools: update_workspace, set_bot_workspace_instructions Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
import BunSQLite from "@moduletrace/bun-sqlite";
|
||||
import { AppData } from "@/src/data/app-data";
|
||||
import grabUserBot from "@/src/functions/backend/db/bots/grab-user-bot";
|
||||
import deleteMedia from "@/src/functions/backend/db/media/delete-media";
|
||||
import uploadAndRecordMedia from "@/src/functions/backend/db/users/media/upload-and-record-media";
|
||||
import grabUserWorkspace from "@/src/functions/backend/db/workspaces/grab-user-workspace";
|
||||
import type { ProfileImageTarget } from "@/src/schemas/profile-image-schema";
|
||||
import AppError from "@/src/utils/app-error";
|
||||
import bufferFromBase64 from "@/src/utils/buffer-from-base-64";
|
||||
|
||||
type Params = {
|
||||
user_id: number | string;
|
||||
target: ProfileImageTarget;
|
||||
id: number | string;
|
||||
/**
|
||||
* The new picture as a base64 data URL; null removes it.
|
||||
*/
|
||||
data_url: string | null;
|
||||
};
|
||||
|
||||
const Targets = {
|
||||
bot: { table: "bots", paradigm: "bot-image" },
|
||||
workspace: { table: "workspaces", paradigm: "workspace-image" },
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Sets (or removes) the picture of one of the user's bots or workspaces:
|
||||
* a private image, the old one deleted. Returns the new `image_media_id`.
|
||||
*/
|
||||
export default async function setProfileImage({ user_id, target, id, data_url }: Params) {
|
||||
const record =
|
||||
target == "bot"
|
||||
? await grabUserBot({ bot_id: id, user_id })
|
||||
: await grabUserWorkspace({ workspace_id: id, user_id });
|
||||
|
||||
if (!record?.id) {
|
||||
throw new AppError(target == "bot" ? `Bot not found.` : `Workspace not found.`);
|
||||
}
|
||||
|
||||
const { table, paradigm } = Targets[target];
|
||||
let image_media_id: number | null = null;
|
||||
|
||||
if (data_url) {
|
||||
if (!/^data:image\/(png|jpe?g|webp|gif)/i.test(data_url)) {
|
||||
throw new AppError(`Pick a PNG, JPG, WebP or GIF image.`);
|
||||
}
|
||||
|
||||
const { buffer, ext } = await bufferFromBase64(data_url);
|
||||
|
||||
if (buffer.length > AppData.ProfileImageMaxBytes) {
|
||||
throw new AppError(
|
||||
`That picture is too big. Pictures can be up to ${Math.round(AppData.ProfileImageMaxBytes / 1024 / 1024)} MB.`,
|
||||
);
|
||||
}
|
||||
|
||||
const res = await uploadAndRecordMedia({
|
||||
data: buffer,
|
||||
media_type: "image",
|
||||
media_mime_type: ext,
|
||||
media_paradigms: [paradigm],
|
||||
media: { title: `${record.name || target} picture` },
|
||||
is_private: true,
|
||||
user: { id: Number(user_id) },
|
||||
});
|
||||
|
||||
if (!res.singleRes?.id) {
|
||||
throw new AppError(`Couldn't save the picture. Try another image.`);
|
||||
}
|
||||
|
||||
image_media_id = Number(res.singleRes.id);
|
||||
}
|
||||
|
||||
await BunSQLite.sql({
|
||||
sql: `UPDATE ${table} SET image_media_id = ?, updated_at = ? WHERE id = ?`,
|
||||
values: [image_media_id, Date.now(), record.id],
|
||||
});
|
||||
|
||||
if (record.image_media_id) {
|
||||
await deleteMedia({ id: record.image_media_id, user_id: Number(user_id) }).catch(() => {});
|
||||
}
|
||||
|
||||
return { image_media_id };
|
||||
}
|
||||
Reference in New Issue
Block a user