Skip to content

Commit

Permalink
Convert all assistants avatar to jpeg server-side (#762)
Browse files Browse the repository at this point in the history
* Convert all assistants to jpeg server side, and rename endpoint appropriately

* Improve avatar validation/error display

* preserve aspect ratio on resize

* Update src/lib/components/chat/ChatMessages.svelte

Co-authored-by: Mishig <[email protected]>

---------

Co-authored-by: Mishig <[email protected]>
  • Loading branch information
nsarrazin and Mishig authored Feb 2, 2024
1 parent 0b2a549 commit 91ec91f
Show file tree
Hide file tree
Showing 11 changed files with 45 additions and 34 deletions.
17 changes: 14 additions & 3 deletions src/lib/components/AssistantSettings.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,14 @@
function onFilesChange(e: Event) {
const inputEl = e.target as HTMLInputElement;
if (inputEl.files?.length) {
if (inputEl.files?.length && inputEl.files[0].size > 0) {
if (!inputEl.files[0].type.includes("image")) {
inputEl.files = null;
files = null;
form = { error: true, errors: [{ field: "avatar", message: "Only images are allowed" }] };
return;
}
files = inputEl.files;
resetErrors();
deleteExistingAvatar = false;
Expand Down Expand Up @@ -90,6 +97,10 @@
// else we just remove it from the input
formData.delete("avatar");
}
} else {
if (files === null) {
formData.delete("avatar");
}
}

return async ({ result }) => {
Expand Down Expand Up @@ -135,7 +146,7 @@
/>
{:else if assistant?.avatar}
<img
src="{base}/settings/assistants/{assistant._id}/avatar?hash={assistant.avatar}"
src="{base}/settings/assistants/{assistant._id}/avatar.jpg?hash={assistant.avatar}"
alt="avatar"
class="crop mx-auto h-12 w-12 cursor-pointer rounded-full object-cover"
/>
Expand Down Expand Up @@ -169,8 +180,8 @@
<CarbonUpload class="mr-2 text-xs " /> Upload
</label>
</div>
<p class="text-xs text-red-500">{getError("avatar", form)}</p>
{/if}
<p class="text-xs text-red-500">{getError("avatar", form)}</p>
</div>

<label>
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/NavConversationItem.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
{/if}
{#if conv.avatarHash}
<img
src="{base}/settings/assistants/{conv.assistantId}/avatar?hash={conv.avatarHash}"
src="{base}/settings/assistants/{conv.assistantId}/avatar.jpg?hash={conv.avatarHash}"
alt="Assistant avatar"
class="mr-1.5 inline size-4 flex-none rounded-full object-cover"
/>
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/chat/AssistantIntroduction.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
>
{#if assistant.avatar}
<img
src={`${base}/settings/assistants/${assistant._id.toString()}/avatar?hash=${
src={`${base}/settings/assistants/${assistant._id.toString()}/avatar.jpg?hash=${
assistant.avatar
}`}
alt="avatar"
Expand Down
4 changes: 2 additions & 2 deletions src/lib/components/chat/ChatMessages.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@
>
{#if $page.data?.assistant.avatar}
<img
src="{base}/settings/assistants/{$page.data?.assistant._id.toString()}/avatar?hash=${$page
.data?.assistant.avatar}"
src="{base}/settings/assistants/{$page.data?.assistant._id.toString()}/avatar.jpg?hash=${$page
.data.assistant.avatar}"
alt="Avatar"
class="size-5 rounded-full object-cover"
/>
Expand Down
3 changes: 2 additions & 1 deletion src/routes/assistant/[assistantId]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@
{#if data.assistant.avatar}
<img
class="size-16 flex-none rounded-full object-cover sm:size-24"
src="{base}/settings/assistants/{data.assistant._id}/avatar?hash={data.assistant.avatar}"
src="{base}/settings/assistants/{data.assistant._id}/avatar.jpg?hash={data.assistant
.avatar}"
alt="avatar"
/>
{:else}
Expand Down
2 changes: 1 addition & 1 deletion src/routes/assistants/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
>
{#if assistant.avatar}
<img
src="{base}/settings/assistants/{assistant._id}/avatar"
src="{base}/settings/assistants/{assistant._id}/avatar.jpg"
alt="Avatar"
class="mb-2 aspect-square size-12 flex-none rounded-full object-cover sm:mb-6 sm:size-20"
/>
Expand Down
2 changes: 1 addition & 1 deletion src/routes/settings/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
>
{#if assistant.avatar}
<img
src="{base}/settings/assistants/{assistant._id.toString()}/avatar?hash={assistant.avatar}"
src="{base}/settings/assistants/{assistant._id.toString()}/avatar.jpg?hash={assistant.avatar}"
alt="Avatar"
class="h-6 w-6 rounded-full object-cover"
/>
Expand Down
2 changes: 1 addition & 1 deletion src/routes/settings/assistants/[assistantId]/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
{#if assistant?.avatar}
<!-- crop image if not square -->
<img
src={`${base}/settings/assistants/${assistant?._id}/avatar?hash=${assistant?.avatar}`}
src={`${base}/settings/assistants/${assistant?._id}/avatar.jpg?hash=${assistant?.avatar}`}
alt="Avatar"
class="size-16 flex-none rounded-full object-cover sm:size-24"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@ export const GET: RequestHandler = async ({ params }) => {

const fileId = collections.bucket.find({ filename: assistant._id.toString() });

let mime = "";

const content = await fileId.next().then(async (file) => {
mime = file?.metadata?.mime;

if (!file?._id) {
throw error(404, "Avatar not found");
}
Expand All @@ -40,7 +36,7 @@ export const GET: RequestHandler = async ({ params }) => {

return new Response(content, {
headers: {
"Content-Type": mime ?? "application/octet-stream",
"Content-Type": "image/jpeg",
},
});
};
17 changes: 11 additions & 6 deletions src/routes/settings/assistants/[assistantId]/edit/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import { fail, type Actions, redirect } from "@sveltejs/kit";
import { ObjectId } from "mongodb";

import { z } from "zod";
import sizeof from "image-size";
import { sha256 } from "$lib/utils/sha256";

import sharp from "sharp";

const newAsssistantSchema = z.object({
name: z.string().min(1),
modelId: z.string().min(1),
Expand Down Expand Up @@ -84,10 +85,14 @@ export const actions: Actions = {

let hash;
if (parse.data.avatar && parse.data.avatar !== "null" && parse.data.avatar.size > 0) {
const dims = sizeof(Buffer.from(await parse.data.avatar.arrayBuffer()));

if ((dims.height ?? 1000) > 512 || (dims.width ?? 1000) > 512) {
const errors = [{ field: "avatar", message: "Avatar too big" }];
let image;
try {
image = await sharp(await parse.data.avatar.arrayBuffer())
.resize(512, 512, { fit: "inside" })
.jpeg({ quality: 80 })
.toBuffer();
} catch (e) {
const errors = [{ field: "avatar", message: (e as Error).message }];
return fail(400, { error: true, errors });
}

Expand All @@ -100,7 +105,7 @@ export const actions: Actions = {
fileId = await fileCursor.next();
}

hash = await uploadAvatar(parse.data.avatar, assistant._id);
hash = await uploadAvatar(new File([image], "avatar.jpg"), assistant._id);
} else if (deleteAvatar) {
// delete the avatar
const fileCursor = collections.bucket.find({ filename: assistant._id.toString() });
Expand Down
22 changes: 10 additions & 12 deletions src/routes/settings/assistants/new/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { fail, type Actions, redirect } from "@sveltejs/kit";
import { ObjectId } from "mongodb";

import { z } from "zod";
import sizeof from "image-size";
import { sha256 } from "$lib/utils/sha256";
import sharp from "sharp";

const newAsssistantSchema = z.object({
name: z.string().min(1),
Expand Down Expand Up @@ -74,20 +74,18 @@ export const actions: Actions = {

let hash;
if (parse.data.avatar && parse.data.avatar.size > 0) {
const dims = sizeof(Buffer.from(await parse.data.avatar.arrayBuffer()));

if ((dims.height ?? 1000) > 512 || (dims.width ?? 1000) > 512) {
const errors = [
{
field: "avatar",
message:
"Avatar is too big. Please make sure the size of your avatar is no bigger than 512px by 512px.",
},
];
let image;
try {
image = await sharp(await parse.data.avatar.arrayBuffer())
.resize(512, 512, { fit: "inside" })
.jpeg({ quality: 80 })
.toBuffer();
} catch (e) {
const errors = [{ field: "avatar", message: (e as Error).message }];
return fail(400, { error: true, errors });
}

hash = await uploadAvatar(parse.data.avatar, newAssistantId);
hash = await uploadAvatar(new File([image], "avatar.jpg"), newAssistantId);
}

const { insertedId } = await collections.assistants.insertOne({
Expand Down

0 comments on commit 91ec91f

Please sign in to comment.