Skip to content

Commit

Permalink
More improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
jpalumickas committed Sep 16, 2024
1 parent f4f13d1 commit 3e1db4f
Show file tree
Hide file tree
Showing 13 changed files with 107 additions and 37 deletions.
19 changes: 19 additions & 0 deletions examples/cloudflare-worker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Uplo Cloudflare Workers Example

Cloudflare Workers + Neon Postgres + Drizzle Adapter

## Requirements

* [Neon database](https://neon.tech)

## Usage

Create `.dev.vars` file:

```text
AWS_ENDPOINT=""
AWS_BUCKET=""
AWS_ACCESS_KEY_ID=""
AWS_SECRET_ACCESS_KEY=""
DATABASE_URL=""
```
2 changes: 2 additions & 0 deletions examples/cloudflare-worker/src/db/initDB.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ export const initDB = async ({ databaseUrl }: { databaseUrl: string }) => {

return { db };
};

export type DB = Awaited<ReturnType<typeof initDB>>['db'];
32 changes: 29 additions & 3 deletions examples/cloudflare-worker/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { checksumString } from '@uplo/utils';
import Uplo from '@uplo/server';
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 {
Expand All @@ -18,6 +18,26 @@ export interface Env {
// MY_SERVICE: Fetcher;
}

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,
};
};

export default {
async fetch(
request: Request,
Expand Down Expand Up @@ -52,10 +72,16 @@ export default {

// const service = GCSService({})

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 userAttachment = await uplo.attachments.user(1).avatar.findOne();

console.log(userAttachment);

return new Response(`Hello World 2! ${await checksumString('test')}`);
const avatarUrl = userAttachment?.url();
return new Response(`User avatar url: ${avatarUrl}`);
},
};
3 changes: 3 additions & 0 deletions examples/node/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Uplo Node Example

Node + Prisma + Postgres
3 changes: 1 addition & 2 deletions packages/node/src/blobInputs/blobFileInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import { ReadStream, createReadStream, lstatSync } from 'node:fs';
import { basename } from 'node:path';
import crypto from 'node:crypto';
import mime from 'mime/lite.js';
import { BlobInput } from '../../../server/src/blobInputs/types.js';
import { UploError } from '../../../server/src/errors.js';
import { BlobInput, UploError } from '@uplo/server';

export interface BlobFileInput {
path: string;
Expand Down
5 changes: 3 additions & 2 deletions packages/node/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from '@uplo/server'
export * from './blobInputs'
export * from '@uplo/server';
export { default } from '@uplo/server';
export * from './blobInputs';
13 changes: 10 additions & 3 deletions packages/utils/src/browser/checksumString.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
export const checksumString = async (content: string) => {
const msgUint8 = new TextEncoder().encode(content); // encode as (utf-8) Uint8Array
export const checksumString = async (content: string | Uint8Array) => {
let msgUint8: Uint8Array;

if (typeof content === 'string') {
msgUint8 = new TextEncoder().encode(content); // encode as (utf-8) Uint8Array
} else {
msgUint8 = content;
}

const hashBuffer = await crypto.subtle.digest('MD5', msgUint8); // hash the message
// const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array
// // const hashHex = hashArray
// .map((b) => b.toString(16).padStart(2, '0'))
// .join('');

const digest = btoa(String.fromCharCode(...new Uint8Array(hashBuffer)));
const digest = btoa(String.fromCharCode(...new Uint8Array(hashBuffer)));

return digest;
};
12 changes: 6 additions & 6 deletions packages/utils/src/browser/generateKey.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
function dec2hex (dec: any) {
return dec.toString(16).padStart(2, "0")
function dec2hex(dec: any) {
return dec.toString(16).padStart(2, '0');
}

export const generateKey = async (size: number = 32) => {
const arr = new Uint8Array((size || 40) / 2)
globalThis.crypto.getRandomValues(arr)
return Array.from(arr, dec2hex).join('')
}
const arr = new Uint8Array((size || 40) / 2);
globalThis.crypto.getRandomValues(arr);
return Array.from(arr, dec2hex).join('');
};
10 changes: 7 additions & 3 deletions packages/utils/src/checksumString.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import crypto from 'node:crypto';

export const checksumString = async (content: string) => {
const buffer = Buffer.from(content, 'utf-8')
export const checksumString = async (content: string | Uint8Array) => {
if (typeof content !== 'string') {
throw new Error('Expected content to be a string');
}

const buffer = Buffer.from(content, 'utf-8');
return crypto.createHash('md5').update(buffer).digest('base64');
}
};
8 changes: 7 additions & 1 deletion packages/utils/src/contentDisposition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ import { contentDisposition as generate } from '@tinyhttp/content-disposition';

export type ContentDispositionType = 'attachment' | 'inline';

export const contentDisposition = ({ fileName, type = 'inline' }: { fileName: string, type?: ContentDispositionType }) => {
export const contentDisposition = ({
fileName,
type = 'inline',
}: {
fileName: string;
type?: ContentDispositionType;
}) => {
return generate(fileName, { type });
};
12 changes: 6 additions & 6 deletions packages/utils/src/generateKey.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { randomBytes } from 'node:crypto';

/**
* Returns random key. This is used to generate Blob key.
*
* @param size - Length of the key
* @returns Generated key
*
*/
* Returns random key. This is used to generate Blob key.
*
* @param size - Length of the key
* @returns Generated key
*
*/
export const generateKey = async (size: number = 32) => {
return randomBytes(size).toString('hex');
};
15 changes: 9 additions & 6 deletions packages/utils/src/getDeepValue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,25 @@ type GetIndexedField<T, K> = K extends keyof T
: number extends keyof T
? T[number]
: undefined
: undefined
: undefined;

type FieldWithPossiblyUndefined<T, Key> =
| GetFieldType<Exclude<T, undefined>, Key>
| Extract<T, undefined>
| Extract<T, undefined>;

type IndexedFieldWithPossiblyUndefined<T, Key> =
| GetIndexedField<Exclude<T, undefined>, Key>
| Extract<T, undefined>
| Extract<T, undefined>;

export type GetFieldType<T, P> = P extends `${infer Left}.${infer Right}`
? Left extends keyof T
? FieldWithPossiblyUndefined<T[Left], Right>
: Left extends `${infer FieldKey}[${infer IndexKey}]`
? FieldKey extends keyof T
? FieldWithPossiblyUndefined<IndexedFieldWithPossiblyUndefined<T[FieldKey], IndexKey>, Right>
? FieldWithPossiblyUndefined<
IndexedFieldWithPossiblyUndefined<T[FieldKey], IndexKey>,
Right
>
: undefined
: undefined
: P extends keyof T
Expand All @@ -32,12 +35,12 @@ export type GetFieldType<T, P> = P extends `${infer Left}.${infer Right}`
? FieldKey extends keyof T
? IndexedFieldWithPossiblyUndefined<T[FieldKey], IndexKey>
: undefined
: undefined
: undefined;

export function getDeepValue<
TData,
TPath extends string,
TDefault = GetFieldType<TData, TPath>
TDefault = GetFieldType<TData, TPath>,
>(
data: TData,
path: TPath,
Expand Down
10 changes: 5 additions & 5 deletions packages/utils/src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ export const isEdge =
// @ts-ignore
(typeof EdgeRuntime !== 'undefined' &&
// @ts-ignore
['edge-runtime', 'vercel'].includes(EdgeRuntime))
['edge-runtime', 'vercel'].includes(EdgeRuntime));

export const isNode =
typeof process !== 'undefined' &&
process.versions != null &&
process.versions.node != null
process.versions.node != null;

export const isDeno =
// @ts-ignore
typeof Deno !== "undefined" &&
typeof Deno !== 'undefined' &&
// @ts-ignore
typeof Deno.version !== "undefined" &&
typeof Deno.version !== 'undefined' &&
// @ts-ignore
typeof Deno.version.deno !== "undefined";
typeof Deno.version.deno !== 'undefined';

0 comments on commit 3e1db4f

Please sign in to comment.