labels for inputs, add api keys
This commit is contained in:
parent
e97b603d94
commit
303f1a232f
11 changed files with 115 additions and 20 deletions
|
@ -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;
|
|
@ -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
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<div class="flex my-auto space-x-3">
|
||||
<HeaderLink href="/dashboard">Dashboard</HeaderLink>
|
||||
<HeaderLink href="/uploads">Uploads</HeaderLink>
|
||||
<HeaderLink href="/links">Links</HeaderLink>
|
||||
<HeaderLink href="/documentation">Documentation</HeaderLink>
|
||||
{#if $user?.role === 'ADMINISTRATOR'}
|
||||
<HeaderLink href="/admin">Admin</HeaderLink>
|
||||
{/if}
|
||||
|
|
16
src/lib/components/ListedAPIKey.svelte
Normal file
16
src/lib/components/ListedAPIKey.svelte
Normal file
|
@ -0,0 +1,16 @@
|
|||
<script>
|
||||
import { X } from 'lucide-svelte';
|
||||
|
||||
export let key;
|
||||
</script>
|
||||
|
||||
<tr>
|
||||
<th>
|
||||
<tt class="text-md">{key.id}</tt>
|
||||
</th>
|
||||
<th class="flex">
|
||||
<button class="m-auto hover:text-red">
|
||||
<X></X>
|
||||
</button>
|
||||
</th>
|
||||
</tr>
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
7
src/routes/(app)/settings/+page.server.ts
Normal file
7
src/routes/(app)/settings/+page.server.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { getUserApiKeys } from '$lib/server/database.js';
|
||||
|
||||
export async function load({ locals }) {
|
||||
return {
|
||||
keys: (await getUserApiKeys(locals.user.id)) || []
|
||||
};
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
<script>
|
||||
import { Info, X, Plus } from 'lucide-svelte';
|
||||
import Button from '$lib/components/Inputs/Button.svelte';
|
||||
import ListedApiKey from '$lib/components/ListedAPIKey.svelte';
|
||||
|
||||
export let data;
|
||||
</script>
|
||||
|
||||
<div class="grid grid-cols-1 gap-2 mx-auto xl:grid-cols-3 w-fit">
|
||||
|
@ -26,7 +29,7 @@
|
|||
</div>
|
||||
<div class="mb-1">
|
||||
<div class="flex flex-col gap-1">
|
||||
<label class="text-lg font-bold" for="username">Email</label>
|
||||
<label class="text-lg font-bold" for="email">Email</label>
|
||||
<input
|
||||
class="px-2 h-8 rounded-lg ring-1 transition-all bg-mantle ring-surface2 focus-visible:outline-none focus-visible:outline-overlay0"
|
||||
type="email"
|
||||
|
@ -60,16 +63,9 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>
|
||||
<tt class="text-md">d8895a0c-daa5-4b6e-b66f-2494c039fe9e </tt>
|
||||
</th>
|
||||
<th class="flex">
|
||||
<button class="m-auto hover:text-red">
|
||||
<X></X>
|
||||
</button>
|
||||
</th>
|
||||
</tr>
|
||||
{#each data.keys as key}
|
||||
<ListedApiKey {key} />
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -165,8 +161,8 @@
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<label class="text-lg font-bold" for="description">2FA</label>
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<p class="text-lg font-bold">2FA</p>
|
||||
<p class="text-sm text-surface2">
|
||||
A One-Time Password will be required each time you login.
|
||||
</p>
|
||||
|
@ -184,7 +180,7 @@
|
|||
<span class="w-full border-b-2 border-surface0" />
|
||||
<div class="flex flex-col gap-3">
|
||||
<div class="">
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<div>
|
||||
<input type="checkbox" name="newPostsPublic" id="newPostsPublic" />
|
||||
<label class="" for="newPostsPublic">New Posts Public</label>
|
||||
|
@ -195,10 +191,10 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="">
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<div>
|
||||
<input type="checkbox" name="newPostsPublic" id="newPostsPublic" />
|
||||
<label class="" for="newPostsPublic">Encrypt Uploads</label>
|
||||
<input type="checkbox" name="encryptUploads" id="encryptUploads" />
|
||||
<label class="" for="encryptUploads">Encrypt Uploads</label>
|
||||
</div>
|
||||
<p class="text-sm text-surface2">Enable encryption for new uploads. (Client-side)</p>
|
||||
</div>
|
||||
|
|
|
@ -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' });
|
||||
|
||||
|
|
17
src/routes/api/v1/keys/+server.ts
Normal file
17
src/routes/api/v1/keys/+server.ts
Normal file
|
@ -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')
|
||||
)
|
||||
);
|
||||
}
|
Loading…
Reference in a new issue