Skip to content

Commit

Permalink
Add route handler, content type validator
Browse files Browse the repository at this point in the history
  • Loading branch information
jpalumickas committed Oct 29, 2024
1 parent 98edd2e commit b262737
Show file tree
Hide file tree
Showing 30 changed files with 531 additions and 198 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"uplo"
],
"cSpell.ignoreWords": [
"Hono",
"Nextra",
"blurhash",
"tsup"
Expand Down
3 changes: 2 additions & 1 deletion examples/cloudflare-worker/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Uplo Cloudflare Workers Example

Cloudflare Workers + Neon Postgres + Drizzle Adapter
Cloudflare Workers + Hono + Neon Postgres + Drizzle Adapter

## Requirements

Expand All @@ -16,4 +16,5 @@ AWS_BUCKET=""
AWS_ACCESS_KEY_ID=""
AWS_SECRET_ACCESS_KEY=""
DATABASE_URL=""
UPLO_SECRET_TOKEN=""
```
1 change: 1 addition & 0 deletions examples/cloudflare-worker/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"@uplo/service-s3": "workspace:*",
"@uplo/utils": "workspace:*",
"drizzle-orm": "^0.35.3",
"hono": "^4.6.8",
"pg": "^8.13.1",
"wrangler": "^3.83.0"
},
Expand Down
25 changes: 0 additions & 25 deletions examples/cloudflare-worker/src/index.test.ts

This file was deleted.

110 changes: 30 additions & 80 deletions examples/cloudflare-worker/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,92 +1,42 @@
import Uplo, { BlobInput, blobStringInput } from '@uplo/server';
import S3Service from '@uplo/service-s3';
import { DrizzleAdapter } from '@uplo/adapter-drizzle-pg';
import { initDB, schema } from './db';
import { checksumString } from '@uplo/utils';
// import GCSService from '@uplo/service-gcs'

export interface Env {
AWS_ENDPOINT: string;
AWS_BUCKET: string;
AWS_ACCESS_KEY_ID: string;
AWS_SECRET_ACCESS_KEY: string;
DATABASE_URL: string;
// Example binding to R2. Learn more at https://developers.cloudflare.com/workers/runtime-apis/r2/
// MY_BUCKET: R2Bucket;
//
// Example binding to a Service. Learn more at https://developers.cloudflare.com/workers/runtime-apis/service-bindings/
// MY_SERVICE: Fetcher;
}
import { Hono } from 'hono';
import { dbMiddleware } from './middleware/dbMiddleware.js';
import { uploMiddleware } from './middleware/uploMiddleware.js';
import type { HonoEnv } from './types/hono.js';
import { imageUrlToBlobInput } from './utils/imageUrlToBlobInput.js';
import { createUploRouteHandler } from '@uplo/server/route-handler';

const imageUrlToBlobInput = async (url: string): Promise<BlobInput> => {
const data = await fetch(url);
const arrayBuffer = await data.arrayBuffer();
const content = new Uint8Array(arrayBuffer);
// const base64 = btoa(String.fromCharCode());
const contentType = data.headers.get('content-type');
const app = new Hono<HonoEnv>();

if (!contentType) {
throw new Error('Cannot get content type from image');
}
app.use(dbMiddleware);
app.use(uploMiddleware);

return {
fileName: 'image.jpg',
size: content.length,
contentType,
checksum: await checksumString(content),
content: content,
};
};
app.get('/', (c) => c.text('Welcome to Uplo!'));

export default {
async fetch(
request: Request,
env: Env,
ctx: ExecutionContext
): Promise<Response> {
const { db } = await initDB({
databaseUrl: env.DATABASE_URL,
});
app.post('/attach-avatar', async (c) => {
const blobInput = await imageUrlToBlobInput(
'https://www.viewbug.com/media/mediafiles/2015/10/16/59560665_medium.jpg'
);
await c.get('uplo').attachments.user(1).avatar.attachFile(blobInput);

const s3Service = S3Service({
isPublic: false,
bucket: env.AWS_BUCKET,
accessKeyId: env.AWS_ACCESS_KEY_ID,
secretAccessKey: env.AWS_SECRET_ACCESS_KEY,
endpoint: env.AWS_ENDPOINT,
forcePathStyle: true,
requestHandler: {
requestInit(_httpRequest) {
return { cache: undefined };
},
},
});
return c.json({ success: true, message: 'Avatar attached' });
});

const uplo = Uplo({
adapter: DrizzleAdapter({
db,
schema,
}),
services: {
s3: s3Service,
},
attachments: {
user: {
avatar: true,
},
},
});
app.get('/avatar-url', async (c) => {
const uplo = c.get('uplo');

// const service = GCSService({})
const userAttachment = await uplo.attachments.user(1).avatar.findOne();

// const blobInput = await imageUrlToBlobInput(
// 'https://www.viewbug.com/media/mediafiles/2015/10/16/59560665_medium.jpg'
// );
// await uplo.attachments.user(1).avatar.attachFile(blobInput);
const avatarUrl = await userAttachment?.url();
return c.json({ avatarUrl });
});

const userAttachment = await uplo.attachments.user(1).avatar.findOne();
app.all('/uplo/*', async (c) => {
const uplo = c.get('uplo');
const handler = createUploRouteHandler({ uplo });

const avatarUrl = await userAttachment?.url();
return new Response(`User avatar url: ${avatarUrl}`);
},
};
return handler(c.req.raw);
});

export default app;
13 changes: 13 additions & 0 deletions examples/cloudflare-worker/src/middleware/dbMiddleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { createMiddleware } from 'hono/factory';
import { initDB } from '../db/initDB.js';
import { HonoEnv } from '../types/hono.js';

export const dbMiddleware = createMiddleware<HonoEnv>(async (c, next) => {
const { db } = await initDB({
databaseUrl: c.env.DATABASE_URL,
});

c.set('db', db);

await next();
});
10 changes: 10 additions & 0 deletions examples/cloudflare-worker/src/middleware/uploMiddleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { createMiddleware } from 'hono/factory';
import { HonoEnv } from '../types/hono.js';
import { createUplo } from '../services/uplo.js';

export const uploMiddleware = createMiddleware<HonoEnv>(async (c, next) => {
const uplo = createUplo(c);

c.set('uplo', uplo);
await next();
});
47 changes: 47 additions & 0 deletions examples/cloudflare-worker/src/services/uplo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import Uplo from '@uplo/server';
import S3Service from '@uplo/service-s3';
import { DrizzleAdapter } from '@uplo/adapter-drizzle-pg';
import type { HonoContext } from '../types/hono.js';
import * as schema from '../db/schema.js';

export const createUplo = (c: HonoContext) => {
const db = c.get('db');

const s3Service = S3Service({
isPublic: false,
bucket: c.env.AWS_BUCKET,
accessKeyId: c.env.AWS_ACCESS_KEY_ID,
secretAccessKey: c.env.AWS_SECRET_ACCESS_KEY,
endpoint: c.env.AWS_ENDPOINT,
requestHandler: {
requestInit(_httpRequest: Request) {
return { cache: undefined };
},
},
});

const uplo = Uplo({
config: {
privateKey: c.env.UPLO_SECRET_TOKEN,
},
adapter: DrizzleAdapter({
db,
schema,
}),
services: {
s3: s3Service,
},
attachments: {
user: {
avatar: {
validate: {
// contentType: ['image/png', 'image/jpeg'],
contentType: /image\/\w/,
},
},
},
},
});

return uplo;
};
26 changes: 26 additions & 0 deletions examples/cloudflare-worker/src/types/hono.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { Hono as H, Context } from 'hono';
import type { initDB } from '../db/initDB.js';
import type { createUplo } from '../services/uplo.js';

export type HonoBindings = {
DATABASE_URL: string;
UPLO_SECRET_TOKEN: string;

AWS_BUCKET: string;
AWS_ACCESS_KEY_ID: string;
AWS_SECRET_ACCESS_KEY: string;
AWS_ENDPOINT: string;
};

export type HonoVariables = {
db: Awaited<ReturnType<typeof initDB>>['db'];
uplo: Awaited<ReturnType<typeof createUplo>>;
};

export type HonoEnv = {
Bindings: HonoBindings;
Variables: HonoVariables;
};

export type Hono = H<HonoEnv>;
export type HonoContext = Context<HonoEnv>;
22 changes: 22 additions & 0 deletions examples/cloudflare-worker/src/utils/imageUrlToBlobInput.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { BlobInput } from '@uplo/server';
import { checksumString } from '@uplo/utils';

export const imageUrlToBlobInput = async (url: string): Promise<BlobInput> => {
const data = await fetch(url);
const arrayBuffer = await data.arrayBuffer();
const content = new Uint8Array(arrayBuffer);
// const base64 = btoa(String.fromCharCode());
const contentType = data.headers.get('content-type');

if (!contentType) {
throw new Error('Cannot get content type from image');
}

return {
fileName: 'image.jpg',
size: content.length,
contentType,
checksum: await checksumString(content),
content: content,
};
};
2 changes: 1 addition & 1 deletion examples/cloudflare-worker/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"strict": true,
"noEmit": true,
"isolatedModules": true,
"moduleResolution": "node",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"noUnusedLocals": true,
Expand Down
32 changes: 25 additions & 7 deletions packages/server/package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
{
"name": "@uplo/server",
"version": "0.20.0",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
"./route-handler": {
"import": {
"types": "./dist/route-handler/index.d.ts",
"default": "./dist/route-handler/index.js"
},
"require": {
"types": "./dist/route-handler/index.d.cts",
"default": "./dist/route-handler/index.cjs"
}
},
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"sideEffects": false,
Expand All @@ -19,16 +35,18 @@
"repository": "jpalumickas/uplo",
"homepage": "https://uplo.js.org",
"scripts": {
"build": "tsup src/index.ts --format cjs,esm --dts",
"build": "tsup",
"test": "vitest run",
"test:dev": "vitest dev"
},
"dependencies": {
"@uplo/types": "workspace:^",
"@uplo/utils": "workspace:^",
"camelcase": "^6.3.0",
"hono": "^4.6.8",
"jose": "^5.9.6",
"mime": "^3.0.0"
"mime": "^3.0.0",
"zod": "^3.23.8"
},
"devDependencies": {
"@types/mime": "^3.0.4",
Expand Down
Loading

0 comments on commit b262737

Please sign in to comment.