Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for other asset stores than S3 #59

Merged
merged 19 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
DATABASE_URL=postgresql://user:password@localhost:5432/dbname
REDIS_URL=redis://localhost/0
HOME_URL=https://example.com/ # optional; if present, the home page will redirect to this URL
SECRET_KEY=secret_key # generate a secret key with `openssl rand -base64 32`
LOG_LEVEL=info
Expand All @@ -12,10 +11,12 @@ LISTEN_PORT=3000
# is implemented.
ALLOW_PRIVATE_ADDRESS=false
REMOTE_ACTOR_FETCH_POSTS=10
DRIVE_DISK=
ASSET_URL_BASE=
FS_ASSET_PATH=
Comment on lines +14 to +16
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add documentation and example values for new storage configuration.

The new variables need documentation to guide users on proper configuration:

  • DRIVE_DISK: Specify valid options (e.g., 's3', 'local')
  • ASSET_URL_BASE: Indicate format and HTTPS recommendation
  • FS_ASSET_PATH: Provide path format and permission requirements

Apply this diff to add documentation:

-DRIVE_DISK=
-ASSET_URL_BASE=
-FS_ASSET_PATH=
+# Storage configuration
+DRIVE_DISK=local                    # Storage driver to use: 'local' for filesystem or 's3' for S3
+ASSET_URL_BASE=https://assets.example.com  # Base URL for accessing assets (HTTPS recommended)
+FS_ASSET_PATH=/path/to/assets      # Absolute path to store assets when using local filesystem
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
DRIVE_DISK=
ASSET_URL_BASE=
FS_ASSET_PATH=
# Storage configuration
DRIVE_DISK=local # Storage driver to use: 'local' for filesystem or 's3' for S3
ASSET_URL_BASE=https://assets.example.com # Base URL for accessing assets (HTTPS recommended)
FS_ASSET_PATH=/path/to/assets # Absolute path to store assets when using local filesystem

💡 Codebase verification

Based on the gathered information, I can now provide the final response:

Storage configuration requires additional security measures

The code review reveals several security considerations that need to be addressed:

  • FS_ASSET_PATH is used with FSDriver from the flydrive package with visibility: "public" setting, but there are no explicit permission checks or directory security validations
  • The ASSET_URL_BASE is used directly without URL sanitization, though it's only used as a base URL prefix
  • When DRIVE_DISK=fs, assets are served directly through serveStatic middleware without additional security controls

Recommendations:

  • Add validation for ASSET_URL_BASE to ensure it's a valid URL and ends with a trailing slash
  • Implement directory permission checks when initializing FSDriver to ensure FS_ASSET_PATH has correct permissions
  • Consider adding URL path sanitization in the serveStatic middleware
  • Document the required filesystem permissions for FS_ASSET_PATH in the deployment guide
🔗 Analysis chain

Verify security implications of new storage configuration.

The new storage configuration introduces potential security considerations:

  1. Ensure FS_ASSET_PATH is properly secured with appropriate permissions
  2. Verify that ASSET_URL_BASE is properly sanitized to prevent XSS

Let's check for any security-related code:


Let me gather more information about how these environment variables are used in the codebase:


Let me check one more aspect regarding file system security by looking at the FSDriver implementation:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for URL sanitization and path security measures
echo "Checking URL sanitization:"
rg -l "ASSET_URL_BASE" | xargs rg -A 5 "sanitize|validate|clean"

echo "Checking filesystem security:"
rg -l "FS_ASSET_PATH" | xargs rg -A 5 "chmod|permission|access"

Length of output: 1217


Script:

#!/bin/bash
# Check how these env variables are used in the code
echo "Checking ASSET_URL_BASE usage:"
rg -A 5 "ASSET_URL_BASE"

echo -e "\nChecking FS_ASSET_PATH usage:"
rg -A 5 "FS_ASSET_PATH"

echo -e "\nChecking DRIVE_DISK usage:"
rg -A 5 "DRIVE_DISK"

# Look for any URL or path validation/sanitization utilities
echo -e "\nChecking for URL/path validation utilities:"
rg -l "url|path" | xargs rg "validate|sanitize|normalize"

Length of output: 5126


Script:

#!/bin/bash
# Look for FSDriver implementation and security measures
echo "Checking FSDriver implementation:"
rg -A 10 "class FSDriver"

# Check for any security-related imports or utilities
echo -e "\nChecking for security-related imports:"
rg -l "FSDriver" | xargs rg "import.*(@node|path|fs|security)"

Length of output: 308

AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
S3_REGION=
S3_ENDPOINT_URL=
S3_BUCKET=
S3_URL_BASE=
S3_FORCE_PATH_STYLE=false
Binary file modified bun.lockb
Binary file not shown.
32 changes: 32 additions & 0 deletions compose-fs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
services:
hollo:
image: ghcr.io/dahlia/hollo:canary
dahlia marked this conversation as resolved.
Show resolved Hide resolved
ports:
- "3000:3000"
environment:
DATABASE_URL: "postgres://user:password@postgres:5432/database"
dahlia marked this conversation as resolved.
Show resolved Hide resolved
SECRET_KEY: "${SECRET_KEY}"
LOG_LEVEL: "${LOG_LEVEL}"
BEHIND_PROXY: "${BEHIND_PROXY}"
DRIVE_DISK: fs
ASSET_URL_BASE: http://localhost:3000/assets/
FS_ASSET_PATH: /var/lib/hollo
depends_on:
- postgres
volumes:
- assets_data:/var/lib/hollo
restart: unless-stopped
Comment on lines +4 to +18
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add health check and improve service configuration.

Consider the following improvements:

  1. Add health check to ensure container readiness
  2. Restrict port binding to localhost
  3. Add resource limits
     ports:
-      - "3000:3000"
+      - "127.0.0.1:3000:3000"
+    healthcheck:
+      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
+      interval: 30s
+      timeout: 10s
+      retries: 3
+    deploy:
+      resources:
+        limits:
+          memory: 1G
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ports:
- "3000:3000"
environment:
DATABASE_URL: "postgres://user:password@postgres:5432/database"
SECRET_KEY: "${SECRET_KEY}"
LOG_LEVEL: "${LOG_LEVEL}"
BEHIND_PROXY: "${BEHIND_PROXY}"
DRIVE_DISK: fs
ASSET_URL_BASE: http://localhost:3000/assets/
FS_ASSET_PATH: /var/lib/hollo
depends_on:
- postgres
volumes:
- assets_data:/var/lib/hollo
restart: unless-stopped
ports:
- "127.0.0.1:3000:3000"
environment:
DATABASE_URL: "postgres://user:password@postgres:5432/database"
SECRET_KEY: "${SECRET_KEY}"
LOG_LEVEL: "${LOG_LEVEL}"
BEHIND_PROXY: "${BEHIND_PROXY}"
DRIVE_DISK: fs
ASSET_URL_BASE: http://localhost:3000/assets/
FS_ASSET_PATH: /var/lib/hollo
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
deploy:
resources:
limits:
memory: 1G
depends_on:
- postgres
volumes:
- assets_data:/var/lib/hollo
restart: unless-stopped


postgres:
image: postgres:17
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: database
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
Comment on lines +20 to +28
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve Postgres service security and reliability.

Several improvements are recommended for the Postgres service:

  1. Externalize credentials
  2. Add health check
  3. Add resource limits
  4. Consider setting additional Postgres configurations
   postgres:
     image: postgres:17
     environment:
-      POSTGRES_USER: user
-      POSTGRES_PASSWORD: password
-      POSTGRES_DB: database
+      POSTGRES_USER: ${POSTGRES_USER}
+      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
+      POSTGRES_DB: ${POSTGRES_DB}
+      POSTGRES_INITDB_ARGS: "--auth-host=scram-sha-256"
+    healthcheck:
+      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
+      interval: 10s
+      timeout: 5s
+      retries: 5
+    deploy:
+      resources:
+        limits:
+          memory: 512M
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
postgres:
image: postgres:17
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: database
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
postgres:
image: postgres:17
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_INITDB_ARGS: "--auth-host=scram-sha-256"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
deploy:
resources:
limits:
memory: 512M
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped


volumes:
postgres_data:
assets_data:
3 changes: 2 additions & 1 deletion compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ services:
SECRET_KEY: "${SECRET_KEY}"
LOG_LEVEL: "${LOG_LEVEL}"
BEHIND_PROXY: "${BEHIND_PROXY}"
DRIVE_DISK: s3
ASSET_URL_BASE: http://localhost:9000/hollo/
Comment on lines +11 to +12
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Review the storage configuration.

There are several concerns with the current configuration:

  1. DRIVE_DISK is hardcoded to "s3" which contradicts the PR's objective of supporting multiple storage backends
  2. ASSET_URL_BASE points to localhost:9000 which might not work in non-local environments

Consider these changes:

-      DRIVE_DISK: s3
-      ASSET_URL_BASE: http://localhost:9000/hollo/
+      DRIVE_DISK: ${DRIVE_DISK:-s3}
+      ASSET_URL_BASE: ${ASSET_URL_BASE:-http://localhost:9000/hollo/}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
DRIVE_DISK: s3
ASSET_URL_BASE: http://localhost:9000/hollo/
DRIVE_DISK: ${DRIVE_DISK:-s3}
ASSET_URL_BASE: ${ASSET_URL_BASE:-http://localhost:9000/hollo/}

S3_REGION: us-east-1
S3_BUCKET: hollo
S3_URL_BASE: http://localhost:9000/hollo/
S3_ENDPOINT_URL: http://minio:9000
S3_FORCE_PATH_STYLE: "true"
AWS_ACCESS_KEY_ID: minioadmin
Expand Down
39 changes: 28 additions & 11 deletions docs/src/content/docs/install/env.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -78,35 +78,52 @@ Turned off by default.
a trusted environment and never in production.
</Aside>

### `S3_REGION` <Badge text="Optional" />
### `DRIVE_DISK`

The disk driver used by Hollo to store blobs such as avatars, custom emojis,
and other media.

Valid values are `fs` (local filesystem) and `s3` (S3-compatible object storage).

See https://flydrive.dev/docs/drive_manager for details about the drivers.
joschi marked this conversation as resolved.
Show resolved Hide resolved

### `ASSET_URL_BASE`

The public URL base of the asset storage, e.g.
`https://hollo.example.com/assets` (local filesystem) or
`https://hollo.s3.us-east-1.amazonaws.com` (Amazon S3).
joschi marked this conversation as resolved.
Show resolved Hide resolved

### `FS_ASSET_PATH`

The path in the local filesystem where blob assets are stored e.g. `/var/lib/hollo`.
The directory must be writable for Hollo.

joschi marked this conversation as resolved.
Show resolved Hide resolved
### S3-compatible object storage settings

#### `S3_REGION` <Badge text="Optional" />

The region of the S3-compatible object storage, e.g., `us-east-1`. On some
non-S3 services, this can be omitted. `auto` by default.

### `S3_BUCKET`
#### `S3_BUCKET`

The bucket name of the S3-compatible object storage, e.g., `hollo`.

### `S3_URL_BASE`

The public URL base of the S3-compatible object storage, e.g.,
`https://hollo.s3.us-east-1.amazonaws.com`.

### `S3_ENDPOINT_URL`
#### `S3_ENDPOINT_URL`

The endpoint URL for S3-compatible object storage, e.g.,
`https://s3.us-east-1.amazonaws.com`.

### `S3_FORCE_PATH_STYLE`
#### `S3_FORCE_PATH_STYLE`

Whether to force path-style URLs for S3-compatible object storage. `true` to
turn on, `false` to turn off. Useful for non-AWS S3-compatible services.
Turned off by default.

### `AWS_ACCESS_KEY_ID`
#### `AWS_ACCESS_KEY_ID`

The access key for S3-compatible object storage.

### `AWS_SECRET_ACCESS_KEY`
#### `AWS_SECRET_ACCESS_KEY`

The secret key for S3-compatible object storage
joschi marked this conversation as resolved.
Show resolved Hide resolved
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"dependencies": {
"@aws-sdk/client-s3": "^3.577.0",
"@aws-sdk/credential-providers": "^3.577.0",
"@aws-sdk/s3-request-presigner": "^3.577.0",
joschi marked this conversation as resolved.
Show resolved Hide resolved
"@fedify/fedify": "^1.2.2",
"@fedify/markdown-it-hashtag": "0.2.0",
"@fedify/markdown-it-mention": "^0.1.1",
Expand All @@ -25,10 +26,12 @@
"drizzle-orm": "^0.30.10",
"es-toolkit": "^1.25.2",
"fluent-ffmpeg": "^2.1.3",
"flydrive": "^1.1.0",
"hono": "^4.3.4",
"iso-639-1": "^3.1.2",
"markdown-it": "^14.1.0",
"markdown-it-replace-link": "^1.2.1",
"mime": "^4.0.4",
"open-graph-scraper": "^6.5.1",
"otpauth": "^9.3.4",
"postgres": "^3.4.5",
Expand Down
42 changes: 18 additions & 24 deletions src/api/v1/accounts.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { PutObjectCommand } from "@aws-sdk/client-s3";
import { Block, Undo, isActor, lookupObject } from "@fedify/fedify";
import * as vocab from "@fedify/fedify/vocab";
import { zValidator } from "@hono/zod-validator";
Expand All @@ -19,6 +18,7 @@ import {
sql,
} from "drizzle-orm";
import { Hono } from "hono";
import mime from "mime";
import { z } from "zod";
import { db } from "../../db";
import {
Expand All @@ -38,7 +38,7 @@ import {
unfollowAccount,
} from "../../federation/account";
import { type Variables, scopeRequired, tokenRequired } from "../../oauth";
import { S3_BUCKET, S3_URL_BASE, s3 } from "../../s3";
import { assetUrlBase, disk } from "../../s3";
import {
type Account,
type AccountOwner,
Expand Down Expand Up @@ -117,31 +117,25 @@ app.patch(
const form = c.req.valid("form");
let avatarUrl = undefined;
if (form.avatar instanceof File) {
await s3.send(
new PutObjectCommand({
Bucket: S3_BUCKET,
Key: `avatars/${account.id}`,
Body: new Uint8Array(await form.avatar.arrayBuffer()),
ContentType: form.avatar.type,
ACL: "public-read",
}),
);
avatarUrl = new URL(`avatars/${account.id}?${Date.now()}`, S3_URL_BASE)
.href;
const content = await form.avatar.arrayBuffer();
const path = `avatars/${account.id}.${mime.getExtension(form.avatar.type)}`;
await disk.put(path, new Uint8Array(content), {
contentType: form.avatar.type,
contentLength: content.byteLength,
visibility: "public",
});
avatarUrl = new URL(`${path}?${Date.now()}`, assetUrlBase).href;
joschi marked this conversation as resolved.
Show resolved Hide resolved
}
let coverUrl = undefined;
if (form.header instanceof File) {
await s3.send(
new PutObjectCommand({
Bucket: S3_BUCKET,
Key: `covers/${account.id}`,
Body: new Uint8Array(await form.header.arrayBuffer()),
ContentType: form.header.type,
ACL: "public-read",
}),
);
coverUrl = new URL(`covers/${account.id}?${Date.now()}`, S3_URL_BASE)
.href;
const content = await form.header.arrayBuffer();
const path = `covers/${account.id}.${mime.getExtension(form.header.type)}`;
await disk.put(path, new Uint8Array(content), {
contentType: form.header.type,
contentLength: content.byteLength,
visibility: "public",
});
coverUrl = new URL(`${path}?${Date.now()}`, assetUrlBase).href;
joschi marked this conversation as resolved.
Show resolved Hide resolved
}
const fedCtx = federation.createContext(c.req.raw, undefined);
const fmtOpts = {
Expand Down
22 changes: 10 additions & 12 deletions src/api/v1/media.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { PutObjectCommand } from "@aws-sdk/client-s3";
import { eq } from "drizzle-orm";
import { type Context, Hono } from "hono";
import mime from "mime";
import sharp from "sharp";
import { uuidv7 } from "uuidv7-js";
import { db } from "../../db";
import { serializeMedium } from "../../entities/medium";
import { makeVideoScreenshot, uploadThumbnail } from "../../media";
import { type Variables, scopeRequired, tokenRequired } from "../../oauth";
import { S3_BUCKET, S3_URL_BASE, s3 } from "../../s3";
import { assetUrlBase, disk } from "../../s3";
import { media } from "../../schema";

const app = new Hono<{ Variables: Variables }>();
Expand All @@ -31,16 +31,14 @@ export async function postMedia(c: Context<{ Variables: Variables }>) {
}
const image = sharp(imageBytes);
const fileMetadata = await image.metadata();
await s3.send(
new PutObjectCommand({
Bucket: S3_BUCKET,
Key: `media/${id}/original`,
Body: new Uint8Array(fileBuffer),
ContentType: file.type,
ACL: "public-read",
}),
);
const url = new URL(`media/${id}/original`, S3_URL_BASE).href;
const content = new Uint8Array(fileBuffer);
const path = `media/${id}/original.${mime.getExtension(file.type)}`;
joschi marked this conversation as resolved.
Show resolved Hide resolved
await disk.put(path, content, {
contentType: file.type,
contentLength: content.byteLength,
visibility: "public",
});
joschi marked this conversation as resolved.
Show resolved Hide resolved
const url = new URL(path, assetUrlBase).href;
const result = await db
.insert(media)
.values({
Expand Down
13 changes: 12 additions & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,28 @@ import "./logging";
import { join } from "node:path";
import { federation } from "@fedify/fedify/x/hono";
import { Hono } from "hono";
import { serveStatic } from "hono/bun";
import { behindProxy } from "x-forwarded-fetch";
import api from "./api";
import fedi from "./federation";
import image from "./image";
import oauth, { oauthAuthorizationServer } from "./oauth";
import pages from "./pages";
import { DRIVE_DISK, assetPath } from "./s3";

const app = new Hono();

app.use(federation(fedi, (_) => undefined));
if (DRIVE_DISK === "fs") {
app.use(
"/assets/*",
serveStatic({
root: assetPath,
rewriteRequestPath: (path) => path.substring("/assets".length),
}),
);
}
joschi marked this conversation as resolved.
Show resolved Hide resolved

app.use(federation(fedi, (_) => undefined));
app.route("/", pages);
app.route("/oauth", oauth);
app.get("/.well-known/oauth-authorization-server", oauthAuthorizationServer);
Expand Down
21 changes: 9 additions & 12 deletions src/media.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { mkdtemp } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { PutObjectCommand } from "@aws-sdk/client-s3";
import ffmpeg from "fluent-ffmpeg";
import type { Sharp } from "sharp";
import { S3_BUCKET, S3_URL_BASE, s3 } from "./s3";
import { disk } from "./s3";
import { assetUrlBase } from "./s3";

const DEFAULT_THUMBNAIL_AREA = 230_400;

Expand All @@ -27,17 +27,14 @@ export async function uploadThumbnail(
thumbnailArea,
);
const thumbnail = await original.resize(thumbnailSize).webp().toBuffer();
await s3.send(
new PutObjectCommand({
Bucket: S3_BUCKET,
Key: `media/${id}/thumbnail`,
Body: thumbnail.subarray(),
ContentType: "image/webp",
ACL: "public-read",
}),
);
const content = new Uint8Array(thumbnail);
await disk.put(`media/${id}/thumbnail.webp`, content, {
contentType: "image/webp",
contentLength: content.byteLength,
visibility: "public",
});
joschi marked this conversation as resolved.
Show resolved Hide resolved
return {
thumbnailUrl: new URL(`media/${id}/thumbnail`, S3_URL_BASE).href,
thumbnailUrl: new URL(`media/${id}/thumbnail.webp`, assetUrlBase).href,
thumbnailType: "image/webp",
thumbnailWidth: thumbnailSize.width,
thumbnailHeight: thumbnailSize.height,
Expand Down
22 changes: 10 additions & 12 deletions src/pages/emojis.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { PutObjectCommand } from "@aws-sdk/client-s3";
import { desc, inArray, isNotNull, ne } from "drizzle-orm";
import { Hono } from "hono";
import mime from "mime";
import { DashboardLayout } from "../components/DashboardLayout";
import db from "../db";
import { loginRequired } from "../login";
import { S3_BUCKET, S3_URL_BASE, s3 } from "../s3";
import { assetUrlBase, disk } from "../s3";
import { accounts, customEmojis, posts, reactions } from "../schema";

const emojis = new Hono();
Expand Down Expand Up @@ -166,16 +166,14 @@ emojis.post("/", async (c) => {
if (image == null || !(image instanceof File)) {
return c.text("No image provided", 400);
}
await s3.send(
new PutObjectCommand({
Bucket: S3_BUCKET,
Key: `emojis/${shortcode}`,
Body: new Uint8Array(await image.arrayBuffer()),
ContentType: image.type,
ACL: "public-read",
}),
);
const url = new URL(`emojis/${shortcode}`, S3_URL_BASE).href;
const content = new Uint8Array(await image.arrayBuffer());
const path = `emojis/${shortcode}.${mime.getExtension(image.type)}`;
joschi marked this conversation as resolved.
Show resolved Hide resolved
await disk.put(path, content, {
contentType: image.type,
contentLength: content.byteLength,
visibility: "public",
});
joschi marked this conversation as resolved.
Show resolved Hide resolved
const url = new URL(path, assetUrlBase).href;
joschi marked this conversation as resolved.
Show resolved Hide resolved
await db.insert(customEmojis).values({
category,
shortcode,
Expand Down
Loading