diff --git a/prisma/migrations/20240802082051_add_api_keys_to_schema/migration.sql b/prisma/migrations/20240802082051_add_api_keys_to_schema/migration.sql
new file mode 100644
index 0000000..d46c83e
--- /dev/null
+++ b/prisma/migrations/20240802082051_add_api_keys_to_schema/migration.sql
@@ -0,0 +1,16 @@
+-- AlterTable
+ALTER TABLE `User` ADD COLUMN `totp` VARCHAR(191) NULL;
+
+-- CreateTable
+CREATE TABLE `APIKey` (
+ `id` VARCHAR(191) NOT NULL,
+ `userId` INTEGER NOT NULL,
+ `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
+ `expiresAt` DATETIME(3) NOT NULL,
+ `permissions` INTEGER NOT NULL,
+
+ PRIMARY KEY (`id`)
+) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+-- AddForeignKey
+ALTER TABLE `APIKey` ADD CONSTRAINT `APIKey_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 19ef386..7775c27 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -12,6 +12,7 @@ model User {
username String @unique
email String @unique
password String
+ totp String?
role Role
createdAt DateTime @default(now())
lastSeen DateTime @default(now())
@@ -20,6 +21,7 @@ model User {
maxUploadMB Int @default(100)
uploads Upload[]
sessions Session[]
+ apiKeys APIKey[]
}
model Session {
@@ -58,6 +60,15 @@ model Upload {
uploaded DateTime @default(now())
}
+model APIKey {
+ id String @id
+ user User @relation(fields: [userId], references: [id])
+ userId Int
+ createdAt DateTime @default(now())
+ expiresAt DateTime
+ permissions Int
+}
+
enum Role {
ADMINISTRATOR
USER
diff --git a/src/lib/components/Header.svelte b/src/lib/components/Header.svelte
index 6e5bb15..6404869 100644
--- a/src/lib/components/Header.svelte
+++ b/src/lib/components/Header.svelte
@@ -20,7 +20,7 @@
Dashboard
Uploads
-
Links
+
Documentation
{#if $user?.role === 'ADMINISTRATOR'}
Admin
{/if}
diff --git a/src/lib/components/ListedAPIKey.svelte b/src/lib/components/ListedAPIKey.svelte
new file mode 100644
index 0000000..9d2b9fa
--- /dev/null
+++ b/src/lib/components/ListedAPIKey.svelte
@@ -0,0 +1,16 @@
+
+
+
+
+ {key.id}
+ |
+
+
+ |
+
diff --git a/src/lib/config.ts b/src/lib/config.ts
index ae168ec..27c20c2 100644
--- a/src/lib/config.ts
+++ b/src/lib/config.ts
@@ -23,3 +23,14 @@ export const DOMAINS = [
];
export const DEV_DOMAINS = ['cdn.dev.madhouselabs.net'];
+
+export const API_KEY_PERMISSIONS = {
+ CREATE_UPLOADS: 1 << 0,
+ READ_UPLOADS: 1 << 1,
+ UPDATE_UPLOADS: 1 << 2,
+ DELETE_UPLOADS: 1 << 3,
+
+ READ_ACCOUNT: 1 << 4,
+ UPDATE_ACCOUNT_ACCOUNT_SETTINGS: 1 << 5, // allows for updating username & email. bad idea? probably.
+ UPDATE_ACCOUNT_EMBED_SETTINGS: 1 << 6
+};
diff --git a/src/lib/server/database.ts b/src/lib/server/database.ts
index fdbdde6..063fa1e 100644
--- a/src/lib/server/database.ts
+++ b/src/lib/server/database.ts
@@ -138,3 +138,26 @@ export async function getSettings(id: number) {
}
});
}
+
+export async function getUserApiKeys(userId: number) {
+ if (!userId) return false;
+
+ return await prisma.aPIKey.findMany({
+ where: {
+ userId: userId
+ }
+ });
+}
+
+export async function createUserApiKey(userId: number, permissions: number, expiresAt: Date) {
+ if (!userId) return false;
+
+ return await prisma.aPIKey.create({
+ data: {
+ id: randomBytes(42).toString('base64url'),
+ userId,
+ permissions,
+ expiresAt
+ }
+ });
+}
diff --git a/src/routes/(app)/links/+page.svelte b/src/routes/(app)/documentation/+page.svelte
similarity index 100%
rename from src/routes/(app)/links/+page.svelte
rename to src/routes/(app)/documentation/+page.svelte
diff --git a/src/routes/(app)/settings/+page.server.ts b/src/routes/(app)/settings/+page.server.ts
new file mode 100644
index 0000000..f669c16
--- /dev/null
+++ b/src/routes/(app)/settings/+page.server.ts
@@ -0,0 +1,7 @@
+import { getUserApiKeys } from '$lib/server/database.js';
+
+export async function load({ locals }) {
+ return {
+ keys: (await getUserApiKeys(locals.user.id)) || []
+ };
+}
diff --git a/src/routes/(app)/settings/+page.svelte b/src/routes/(app)/settings/+page.svelte
index ba40b1e..7593b4a 100644
--- a/src/routes/(app)/settings/+page.svelte
+++ b/src/routes/(app)/settings/+page.svelte
@@ -1,6 +1,9 @@
@@ -26,7 +29,7 @@
-
-
+
+
2FA
A One-Time Password will be required each time you login.
@@ -184,7 +180,7 @@
-
+
-
+
diff --git a/src/routes/(app)/uploads/+page.server.ts b/src/routes/(app)/uploads/+page.server.ts
index 29d0e27..3883b88 100644
--- a/src/routes/(app)/uploads/+page.server.ts
+++ b/src/routes/(app)/uploads/+page.server.ts
@@ -3,13 +3,11 @@ import { error } from '@sveltejs/kit';
export async function load({ locals, url }) {
if (!locals.user) return error(403, { status: 403, message: 'Forbidden' });
-
if (+(url.searchParams.get('i') || 0) < 0) error(400, { status: 403, message: 'Invalid Index' });
const totalUploads = await prisma.upload.count({ where: { uploaderId: locals.user.id } });
if (!totalUploads) return { uploads: [], totalUploads: 0 };
-
if (+(url.searchParams.get('i') || 0) >= Math.ceil(totalUploads / 15))
error(400, { status: 403, message: 'Invalid Index' });
diff --git a/src/routes/api/v1/keys/+server.ts b/src/routes/api/v1/keys/+server.ts
new file mode 100644
index 0000000..94437c0
--- /dev/null
+++ b/src/routes/api/v1/keys/+server.ts
@@ -0,0 +1,17 @@
+import { API_KEY_PERMISSIONS } from '$lib/config.js';
+import { createUserApiKey, getUserApiKeys } from '$lib/server/database';
+import { json } from '@sveltejs/kit';
+
+export async function GET({ locals }) {
+ return json(await getUserApiKeys(locals.user.id));
+}
+
+export async function POST({ locals }) {
+ return json(
+ await createUserApiKey(
+ locals.user.id,
+ API_KEY_PERMISSIONS.CREATE_UPLOADS | API_KEY_PERMISSIONS.READ_ACCOUNT,
+ new Date('2099')
+ )
+ );
+}