feat: Upload new tracks
This commit is contained in:
parent
e13aee63b0
commit
388b0605dd
9 changed files with 89 additions and 12 deletions
|
@ -15,6 +15,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-auto": "^2.0.0",
|
"@sveltejs/adapter-auto": "^2.0.0",
|
||||||
"@sveltejs/kit": "^1.27.4",
|
"@sveltejs/kit": "^1.27.4",
|
||||||
|
"@types/uuid": "^9.0.7",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||||
"@typescript-eslint/parser": "^6.0.0",
|
"@typescript-eslint/parser": "^6.0.0",
|
||||||
"eslint": "^8.28.0",
|
"eslint": "^8.28.0",
|
||||||
|
@ -36,6 +37,7 @@
|
||||||
"@lucia-auth/adapter-prisma": "^3.0.2",
|
"@lucia-auth/adapter-prisma": "^3.0.2",
|
||||||
"@picocss/pico": "^1.5.10",
|
"@picocss/pico": "^1.5.10",
|
||||||
"@prisma/client": "5.6.0",
|
"@prisma/client": "5.6.0",
|
||||||
"lucia": "^2.7.4"
|
"lucia": "^2.7.4",
|
||||||
|
"uuid": "^9.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,9 @@ dependencies:
|
||||||
lucia:
|
lucia:
|
||||||
specifier: ^2.7.4
|
specifier: ^2.7.4
|
||||||
version: 2.7.4
|
version: 2.7.4
|
||||||
|
uuid:
|
||||||
|
specifier: ^9.0.1
|
||||||
|
version: 9.0.1
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@sveltejs/adapter-auto':
|
'@sveltejs/adapter-auto':
|
||||||
|
@ -28,6 +31,9 @@ devDependencies:
|
||||||
'@sveltejs/kit':
|
'@sveltejs/kit':
|
||||||
specifier: ^1.27.4
|
specifier: ^1.27.4
|
||||||
version: 1.27.6(svelte@4.2.5)(vite@4.5.0)
|
version: 1.27.6(svelte@4.2.5)(vite@4.5.0)
|
||||||
|
'@types/uuid':
|
||||||
|
specifier: ^9.0.7
|
||||||
|
version: 9.0.7
|
||||||
'@typescript-eslint/eslint-plugin':
|
'@typescript-eslint/eslint-plugin':
|
||||||
specifier: ^6.0.0
|
specifier: ^6.0.0
|
||||||
version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.54.0)(typescript@5.2.2)
|
version: 6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.54.0)(typescript@5.2.2)
|
||||||
|
@ -1577,6 +1583,10 @@ packages:
|
||||||
resolution: {integrity: sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==}
|
resolution: {integrity: sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@types/uuid@9.0.7:
|
||||||
|
resolution: {integrity: sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.54.0)(typescript@5.2.2):
|
/@typescript-eslint/eslint-plugin@6.11.0(@typescript-eslint/parser@6.11.0)(eslint@8.54.0)(typescript@5.2.2):
|
||||||
resolution: {integrity: sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==}
|
resolution: {integrity: sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==}
|
||||||
engines: {node: ^16.0.0 || >=18.0.0}
|
engines: {node: ^16.0.0 || >=18.0.0}
|
||||||
|
@ -3207,6 +3217,11 @@ packages:
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/uuid@9.0.1:
|
||||||
|
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
|
||||||
|
hasBin: true
|
||||||
|
dev: false
|
||||||
|
|
||||||
/vite-node@0.34.6(@types/node@20.9.2):
|
/vite-node@0.34.6(@types/node@20.9.2):
|
||||||
resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==}
|
resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==}
|
||||||
engines: {node: '>=v14.18.0'}
|
engines: {node: '>=v14.18.0'}
|
||||||
|
|
|
@ -41,6 +41,7 @@ model Key {
|
||||||
model Track {
|
model Track {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
title String
|
title String
|
||||||
|
objectKey String
|
||||||
versions TrackVersion[]
|
versions TrackVersion[]
|
||||||
producer User @relation(fields: [producerId], references: [id], onDelete: Cascade)
|
producer User @relation(fields: [producerId], references: [id], onDelete: Cascade)
|
||||||
producerId String
|
producerId String
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import type { Handle } from '@sveltejs/kit';
|
import type { Handle } from '@sveltejs/kit';
|
||||||
|
|
||||||
import { S3_STORAGE_URL } from '$env/static/private';
|
import { S3_STORAGE_URL, S3_STORAGE_BUCKET } from '$env/static/private';
|
||||||
|
|
||||||
import { auth } from '$lib/server/lucia';
|
import { auth } from '$lib/server/lucia';
|
||||||
import { ObjectStorageS3 } from '$lib/server/storage/s3';
|
import { ObjectStorageS3 } from '$lib/server/storage/s3';
|
||||||
import { DatabasePrisma } from '$lib/server/db/prisma';
|
import { DatabasePrisma } from '$lib/server/db/prisma';
|
||||||
|
|
||||||
const s3Client = new ObjectStorageS3(S3_STORAGE_URL);
|
const s3Client = new ObjectStorageS3(S3_STORAGE_URL, S3_STORAGE_BUCKET);
|
||||||
const prismaClient = new DatabasePrisma();
|
const prismaClient = new DatabasePrisma();
|
||||||
|
|
||||||
export const handle: Handle = async ({ event, resolve }) => {
|
export const handle: Handle = async ({ event, resolve }) => {
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
|
import type { Track } from '@prisma/client';
|
||||||
|
|
||||||
export interface Database {
|
export interface Database {
|
||||||
listTracksWithProducer: (
|
listTracksWithProducer: (producerId: string) => Promise<
|
||||||
producerId: string
|
{
|
||||||
) => Promise<{ title: string; producer: { username: string } }[]>;
|
title: string;
|
||||||
|
producer: { username: string };
|
||||||
|
}[]
|
||||||
|
>;
|
||||||
|
|
||||||
|
insertTrack: (producerId: string, title: string, objectKey: string) => Promise<Track>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,4 +25,16 @@ export class DatabasePrisma implements Database {
|
||||||
|
|
||||||
return tracks;
|
return tracks;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
insertTrack = async (producerId: string, title: string, objectKey: string) => {
|
||||||
|
const track = await this.client.track.create({
|
||||||
|
data: {
|
||||||
|
title,
|
||||||
|
objectKey,
|
||||||
|
producerId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return track;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
export interface ObjectStorage {
|
export interface ObjectStorage {
|
||||||
putObject: (obj: Buffer) => Promise<Error | null>;
|
putObject: (key: string, obj: Uint8Array) => Promise<Error | null>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,17 +3,20 @@ import { PutObjectCommand, S3Client, S3ServiceException } from '@aws-sdk/client-
|
||||||
|
|
||||||
class ObjectStorageS3 implements ObjectStorage {
|
class ObjectStorageS3 implements ObjectStorage {
|
||||||
client: S3Client;
|
client: S3Client;
|
||||||
|
bucket: string;
|
||||||
|
|
||||||
constructor(url: string) {
|
constructor(url: string, bucket: string) {
|
||||||
|
this.bucket = bucket;
|
||||||
this.client = new S3Client({
|
this.client = new S3Client({
|
||||||
endpoint: url
|
endpoint: url,
|
||||||
|
region: 'us-east-1'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
putObject = async (obj: Buffer) => {
|
putObject = async (key: string, obj: Uint8Array) => {
|
||||||
const command = new PutObjectCommand({
|
const command = new PutObjectCommand({
|
||||||
Bucket: 'test-bucket',
|
Bucket: this.bucket,
|
||||||
Key: 'hello-s3.txt',
|
Key: key,
|
||||||
Body: obj
|
Body: obj
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
37
src/routes/upload/+page.server.ts
Normal file
37
src/routes/upload/+page.server.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import type { Actions, PageServerLoad } from './$types';
|
||||||
|
|
||||||
|
import { fail, redirect } from '@sveltejs/kit';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
export const load: PageServerLoad = async ({ locals: { authReq } }) => {
|
||||||
|
const session = await authReq.validate();
|
||||||
|
if (!session) throw redirect(302, '/login');
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const actions: Actions = {
|
||||||
|
default: async ({ request, locals: { authReq, database, objectStorage } }) => {
|
||||||
|
const session = await authReq.validate();
|
||||||
|
if (!session) throw redirect(302, '/login');
|
||||||
|
|
||||||
|
const producerId = session.user.userId;
|
||||||
|
|
||||||
|
const formData = await request.formData();
|
||||||
|
const title = formData.get('title') as string;
|
||||||
|
const file = formData.get('file') as File;
|
||||||
|
|
||||||
|
const objectKey = uuidv4();
|
||||||
|
|
||||||
|
const err = await objectStorage.putObject(objectKey, new Uint8Array(await file.arrayBuffer()));
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
return fail(500, {
|
||||||
|
message: 'Failed to upload file.'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await database.insertTrack(producerId, title, objectKey);
|
||||||
|
|
||||||
|
throw redirect(302, '/');
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in a new issue