|
|
|
|
@ -1,10 +1,21 @@
|
|
|
|
|
import { db } from '$lib/db'
|
|
|
|
|
import { shortener } from '$lib/db/schema'
|
|
|
|
|
import { file, shortener, user } from '$lib/db/schema'
|
|
|
|
|
import type { RequestHandler } from './$types'
|
|
|
|
|
import util from 'util'
|
|
|
|
|
import { generateId } from 'lucia'
|
|
|
|
|
import { nanoid } from 'nanoid'
|
|
|
|
|
import { and, eq, isNull } from 'drizzle-orm'
|
|
|
|
|
import { env } from '$env/dynamic/private'
|
|
|
|
|
import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3'
|
|
|
|
|
|
|
|
|
|
const client = new S3Client({
|
|
|
|
|
region: 'auto',
|
|
|
|
|
endpoint: `https://fly.storage.tigris.dev`,
|
|
|
|
|
credentials: {
|
|
|
|
|
accessKeyId: env.PRIVATE_AWS_ACCESS_KEY_ID,
|
|
|
|
|
secretAccessKey: env.PRIVATE_AWS_SECRET_ACCESS_KEY,
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
type S3WebhookStructure = {
|
|
|
|
|
Records: {
|
|
|
|
|
@ -51,6 +62,18 @@ type S3WebhookStructure = {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const POST: RequestHandler = async (event) => {
|
|
|
|
|
const authHeader = event.request.headers.get('Authorization')
|
|
|
|
|
console.log('auth header:', authHeader)
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
!authHeader ||
|
|
|
|
|
(env.PRIVATE_AWS_WEBHOOK_TOKEN &&
|
|
|
|
|
authHeader !== `Bearer ${env.PRIVATE_AWS_WEBHOOK_TOKEN}`)
|
|
|
|
|
) {
|
|
|
|
|
console.log('Unauthorized')
|
|
|
|
|
return new Response('', { status: 401 })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const body = (await event.request.json()) as S3WebhookStructure
|
|
|
|
|
|
|
|
|
|
const eventHandlers = body.Records.map(async (event) => {
|
|
|
|
|
@ -58,9 +81,16 @@ export const POST: RequestHandler = async (event) => {
|
|
|
|
|
util.inspect(event, false, null, true /* enable colors */),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if (event.eventName === 'ObjectCreated:Put') {
|
|
|
|
|
const key = event.s3.object.key
|
|
|
|
|
const [userId, projectId, filename] = key.split('/')
|
|
|
|
|
const key = event.s3.object.key
|
|
|
|
|
const [userId, projectId, filename] = key.split('/')
|
|
|
|
|
|
|
|
|
|
const objectOwner = await db.query.user.findFirst({
|
|
|
|
|
where: (user, { eq }) => eq(user.id, userId),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (!objectOwner) return
|
|
|
|
|
|
|
|
|
|
if (event.eventName === 's3:ObjectCreated:Put') {
|
|
|
|
|
const existingShortener = await db.query.shortener.findFirst({
|
|
|
|
|
where: (shortener, { eq, and, isNull }) =>
|
|
|
|
|
and(
|
|
|
|
|
@ -73,34 +103,70 @@ export const POST: RequestHandler = async (event) => {
|
|
|
|
|
),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (existingShortener) {
|
|
|
|
|
return
|
|
|
|
|
if (!existingShortener) {
|
|
|
|
|
await db.insert(shortener).values({
|
|
|
|
|
id: generateId(8),
|
|
|
|
|
link: '',
|
|
|
|
|
code: nanoid(8),
|
|
|
|
|
projectId: projectId !== 'personal' ? projectId : undefined,
|
|
|
|
|
userId: userId,
|
|
|
|
|
is_file_upload: true,
|
|
|
|
|
file_path: key,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await db.insert(shortener).values({
|
|
|
|
|
id: generateId(8),
|
|
|
|
|
link: '',
|
|
|
|
|
code: nanoid(8),
|
|
|
|
|
projectId: projectId !== 'personal' ? projectId : undefined,
|
|
|
|
|
userId: userId,
|
|
|
|
|
is_file_upload: true,
|
|
|
|
|
file_path: key,
|
|
|
|
|
const object = await client.send(
|
|
|
|
|
new GetObjectCommand({
|
|
|
|
|
Bucket: env.PRIVATE_AWS_BUCKET_NAME,
|
|
|
|
|
Key: key,
|
|
|
|
|
}),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const existingFile = await db.query.file.findFirst({
|
|
|
|
|
where: (file, { eq }) => eq(file.key, key),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (existingFile) {
|
|
|
|
|
await db
|
|
|
|
|
.update(file)
|
|
|
|
|
.set({
|
|
|
|
|
size: object.ContentLength,
|
|
|
|
|
eTag: object.ETag,
|
|
|
|
|
updatedAt: Date.now(),
|
|
|
|
|
})
|
|
|
|
|
.where(eq(file.id, existingFile.id))
|
|
|
|
|
|
|
|
|
|
await db
|
|
|
|
|
.update(user)
|
|
|
|
|
.set({
|
|
|
|
|
fileStorageUsageInByte:
|
|
|
|
|
objectOwner.fileStorageUsageInByte +
|
|
|
|
|
(object.ContentLength! - existingFile.size),
|
|
|
|
|
})
|
|
|
|
|
.where(eq(user.id, userId))
|
|
|
|
|
} else {
|
|
|
|
|
await db.insert(file).values({
|
|
|
|
|
id: generateId(8),
|
|
|
|
|
userId: userId,
|
|
|
|
|
projectId: projectId !== 'personal' ? projectId : undefined,
|
|
|
|
|
key: key,
|
|
|
|
|
name: filename,
|
|
|
|
|
size: object.ContentLength!,
|
|
|
|
|
eTag: object.ETag!,
|
|
|
|
|
createdAt: Date.now(),
|
|
|
|
|
updatedAt: Date.now(),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
await db
|
|
|
|
|
.update(user)
|
|
|
|
|
.set({
|
|
|
|
|
fileStorageUsageInByte:
|
|
|
|
|
objectOwner.fileStorageUsageInByte +
|
|
|
|
|
(object.ContentLength || 0),
|
|
|
|
|
})
|
|
|
|
|
.where(eq(user.id, userId))
|
|
|
|
|
}
|
|
|
|
|
} else if (event.eventName === 's3:ObjectRemoved:Delete') {
|
|
|
|
|
const key = event.s3.object.key
|
|
|
|
|
const [userId, projectId, filename] = key.split('/')
|
|
|
|
|
await db
|
|
|
|
|
.delete(shortener)
|
|
|
|
|
.where(
|
|
|
|
|
and(
|
|
|
|
|
eq(shortener.userId, userId),
|
|
|
|
|
projectId !== 'personal'
|
|
|
|
|
? eq(shortener.projectId, projectId)
|
|
|
|
|
: isNull(shortener.projectId),
|
|
|
|
|
eq(shortener.is_file_upload, true),
|
|
|
|
|
eq(shortener.file_path, key),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|