added apikey model

This commit is contained in:
daniel31x13 2023-11-20 12:48:41 -05:00
parent 9ad277c784
commit cf7b18e012
15 changed files with 242 additions and 56 deletions

View File

@ -5,6 +5,7 @@ import {
faPalette,
faBoxArchive,
faKey,
faLock,
} from "@fortawesome/free-solid-svg-icons";
import Link from "next/link";
import { useRouter } from "next/router";
@ -20,7 +21,7 @@ import {
} from "@fortawesome/free-brands-svg-icons";
export default function SettingsSidebar({ className }: { className?: string }) {
const LINKWARDEN_VERSION = "v2.2.1";
const LINKWARDEN_VERSION = "v2.3.0";
const { collections } = useCollectionStore();
@ -96,6 +97,23 @@ export default function SettingsSidebar({ className }: { className?: string }) {
</div>
</Link>
<Link href="/settings/api">
<div
className={`${
active === `/settings/api` ? "bg-sky-500" : "hover:bg-slate-500"
} duration-100 py-2 px-2 hover:bg-opacity-20 bg-opacity-20 cursor-pointer flex items-center gap-2 w-full rounded-md h-8`}
>
<FontAwesomeIcon
icon={faKey}
className="w-6 h-6 text-sky-500 dark:text-sky-500"
/>
<p className="text-black dark:text-white truncate w-full pr-7">
API Keys
</p>
</div>
</Link>
<Link href="/settings/password">
<div
className={`${
@ -105,7 +123,7 @@ export default function SettingsSidebar({ className }: { className?: string }) {
} duration-100 py-2 px-2 hover:bg-opacity-20 bg-opacity-20 cursor-pointer flex items-center gap-2 w-full rounded-md h-8`}
>
<FontAwesomeIcon
icon={faKey}
icon={faLock}
className="w-6 h-6 text-sky-500 dark:text-sky-500"
/>

View File

@ -49,8 +49,8 @@ export default function SettingsLayout({ children }: Props) {
<SettingsSidebar />
</div>
<div className="w-full flex flex-col min-h-screen p-5 lg:ml-64">
<div className="flex gap-3">
<div className="w-full min-h-screen p-5 lg:ml-64">
<div className="gap-2 inline-flex mr-3">
<div
onClick={toggleSidebar}
className="inline-flex lg:hidden gap-1 items-center select-none cursor-pointer p-2 text-gray-500 dark:text-gray-300 rounded-md duration-100 hover:bg-slate-200 dark:hover:bg-neutral-700"
@ -60,18 +60,12 @@ export default function SettingsLayout({ children }: Props) {
<Link
href="/dashboard"
className="inline-flex gap-1 items-center select-none cursor-pointer p-2 text-gray-500 dark:text-gray-300 rounded-md duration-100 hover:bg-slate-200 dark:hover:bg-neutral-700"
className="inline-flex w-fit gap-1 items-center select-none cursor-pointer p-2 text-gray-500 dark:text-gray-300 rounded-md duration-100 hover:bg-slate-200 dark:hover:bg-neutral-700"
>
<FontAwesomeIcon icon={faChevronLeft} className="w-5 h-5" />
</Link>
<p className="capitalize text-3xl font-thin">
{router.asPath.split("/").pop()} Settings
</p>
</div>
<hr className="my-3 border-1 border-sky-100 dark:border-neutral-700" />
{children}
{sidebar ? (

View File

@ -63,6 +63,7 @@
"@types/dompurify": "^3.0.4",
"@types/jsdom": "^21.1.3",
"autoprefixer": "^10.4.14",
"daisyui": "^4.4.2",
"postcss": "^8.4.26",
"prisma": "^5.1.0",
"tailwindcss": "^3.3.3"

View File

@ -151,6 +151,10 @@ export default function Account() {
return (
<SettingsLayout>
<p className="capitalize text-3xl font-thin inline">Account Settings</p>
<hr className="my-3 border-1 border-sky-100 dark:border-neutral-700" />
<div className="flex flex-col gap-10">
<div className="grid sm:grid-cols-2 gap-3 auto-rows-auto">
<div className="flex flex-col gap-3">

78
pages/settings/api.tsx Normal file
View File

@ -0,0 +1,78 @@
import Checkbox from "@/components/Checkbox";
import SubmitButton from "@/components/SubmitButton";
import SettingsLayout from "@/layouts/SettingsLayout";
import React, { useEffect, useState } from "react";
import useAccountStore from "@/store/account";
import { toast } from "react-hot-toast";
import { AccountSettings } from "@/types/global";
import TextInput from "@/components/TextInput";
export default function Api() {
const [submitLoader, setSubmitLoader] = useState(false);
const { account, updateAccount } = useAccountStore();
const [user, setUser] = useState<AccountSettings>(account);
const [archiveAsScreenshot, setArchiveAsScreenshot] =
useState<boolean>(false);
const [archiveAsPDF, setArchiveAsPDF] = useState<boolean>(false);
const [archiveAsWaybackMachine, setArchiveAsWaybackMachine] =
useState<boolean>(false);
useEffect(() => {
setUser({
...account,
archiveAsScreenshot,
archiveAsPDF,
archiveAsWaybackMachine,
});
}, [account, archiveAsScreenshot, archiveAsPDF, archiveAsWaybackMachine]);
function objectIsEmpty(obj: object) {
return Object.keys(obj).length === 0;
}
useEffect(() => {
if (!objectIsEmpty(account)) {
setArchiveAsScreenshot(account.archiveAsScreenshot);
setArchiveAsPDF(account.archiveAsPDF);
setArchiveAsWaybackMachine(account.archiveAsWaybackMachine);
}
}, [account]);
const submit = async () => {
// setSubmitLoader(true);
// const load = toast.loading("Applying...");
// const response = await updateAccount({
// ...user,
// });
// toast.dismiss(load);
// if (response.ok) {
// toast.success("Settings Applied!");
// } else toast.error(response.data as string);
// setSubmitLoader(false);
};
return (
<SettingsLayout>
<p className="capitalize text-3xl font-thin inline">API Keys</p>
<hr className="my-3 border-1 border-sky-100 dark:border-neutral-700" />
<div className="flex flex-col gap-3">
<div className="badge bg-yellow-300 text-black">
Status: Under Development
</div>
<p>This page will be for creating and managing your API keys.</p>
<p>
For now, you can <i>temporarily</i> use your{" "}
<code className="text-xs whitespace-nowrap bg-gray-500/40 rounded-md px-2 py-1">
next-auth.session-token
</code>{" "}
in your browser cookies as the API key for your integrations.
</p>
</div>
</SettingsLayout>
);
}

View File

@ -68,6 +68,10 @@ export default function Appearance() {
return (
<SettingsLayout>
<p className="capitalize text-3xl font-thin inline">Appearance</p>
<hr className="my-3 border-1 border-sky-100 dark:border-neutral-700" />
<div className="flex flex-col gap-10">
<div>
<p className="mb-3">Select Theme</p>

View File

@ -57,6 +57,10 @@ export default function Archive() {
return (
<SettingsLayout>
<p className="capitalize text-3xl font-thin inline">Archive Settings</p>
<hr className="my-3 border-1 border-sky-100 dark:border-neutral-700" />
<p>Formats to Archive webpages:</p>
<div className="p-3">
<Checkbox

View File

@ -11,9 +11,13 @@ export default function Billing() {
return (
<SettingsLayout>
<p className="capitalize text-3xl font-thin inline">Billing Settings</p>
<hr className="my-3 border-1 border-sky-100 dark:border-neutral-700" />
<div className="w-full mx-auto flex flex-col gap-3 justify-between">
<p className="text-md text-black dark:text-white">
To manage/cancel your subsciption, visit the{" "}
To manage/cancel your subscription, visit the{" "}
<a
href={process.env.NEXT_PUBLIC_STRIPE_BILLING_PORTAL_URL}
className="underline"

View File

@ -45,6 +45,10 @@ export default function Password() {
return (
<SettingsLayout>
<p className="capitalize text-3xl font-thin inline">Change Password</p>
<hr className="my-3 border-1 border-sky-100 dark:border-neutral-700" />
<p className="mb-3">
To change your password, please fill out the following. Your password
should be at least 8 characters.

View File

@ -0,0 +1,22 @@
-- CreateTable
CREATE TABLE "ApiKeys" (
"id" SERIAL NOT NULL,
"name" TEXT NOT NULL,
"userId" INTEGER NOT NULL,
"token" TEXT NOT NULL,
"expires" TIMESTAMP(3) NOT NULL,
"lastUsedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "ApiKeys_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "ApiKeys_token_key" ON "ApiKeys"("token");
-- CreateIndex
CREATE UNIQUE INDEX "ApiKeys_token_userId_key" ON "ApiKeys"("token", "userId");
-- AddForeignKey
ALTER TABLE "ApiKeys" ADD CONSTRAINT "ApiKeys_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@ -0,0 +1,34 @@
/*
Warnings:
- You are about to drop the `ApiKeys` table. If the table is not empty, all the data it contains will be lost.
*/
-- DropForeignKey
ALTER TABLE "ApiKeys" DROP CONSTRAINT "ApiKeys_userId_fkey";
-- DropTable
DROP TABLE "ApiKeys";
-- CreateTable
CREATE TABLE "ApiKey" (
"id" SERIAL NOT NULL,
"name" TEXT NOT NULL,
"userId" INTEGER NOT NULL,
"token" TEXT NOT NULL,
"expires" TIMESTAMP(3) NOT NULL,
"lastUsedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "ApiKey_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "ApiKey_token_key" ON "ApiKey"("token");
-- CreateIndex
CREATE UNIQUE INDEX "ApiKey_token_userId_key" ON "ApiKey"("token", "userId");
-- AddForeignKey
ALTER TABLE "ApiKey" ADD CONSTRAINT "ApiKey_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@ -20,7 +20,6 @@ model Account {
scope String?
id_token String?
session_state String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([provider, providerAccountId])
@ -29,45 +28,34 @@ model Account {
model User {
id Int @id @default(autoincrement())
name String
username String? @unique
email String? @unique
emailVerified DateTime?
image String?
accounts Account[]
password String?
collections Collection[]
tags Tag[]
pinnedLinks Link[]
collectionsJoined UsersAndCollections[]
whitelistedUsers WhitelistedUser[]
apiKeys ApiKey[]
subscriptions Subscription?
archiveAsScreenshot Boolean @default(true)
archiveAsPDF Boolean @default(true)
archiveAsWaybackMachine Boolean @default(false)
isPrivate Boolean @default(false)
displayLinkIcons Boolean @default(true)
blurredFavicons Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt @default(now())
}
model WhitelistedUser {
id Int @id @default(autoincrement())
username String @default("")
User User? @relation(fields: [userId], references: [id])
userId Int?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt @default(now())
}
@ -76,7 +64,6 @@ model VerificationToken {
identifier String
token String @unique
expires DateTime
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt @default(now())
@ -89,13 +76,12 @@ model Collection {
description String @default("")
color String @default("#0ea5e9")
isPublic Boolean @default(false)
owner User @relation(fields: [ownerId], references: [id])
ownerId Int
members UsersAndCollections[]
links Link[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt @default(now())
owner User @relation(fields: [ownerId], references: [id])
ownerId Int
members UsersAndCollections[]
links Link[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt @default(now())
@@unique([name, ownerId])
}
@ -103,16 +89,13 @@ model Collection {
model UsersAndCollections {
user User @relation(fields: [userId], references: [id])
userId Int
collection Collection @relation(fields: [collectionId], references: [id])
collectionId Int
canCreate Boolean
canUpdate Boolean
canDelete Boolean
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt @default(now())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt @default(now())
@@id([userId, collectionId])
}
@ -122,32 +105,25 @@ model Link {
name String
url String
description String @default("")
pinnedBy User[]
collection Collection @relation(fields: [collectionId], references: [id])
collectionId Int
tags Tag[]
textContent String?
screenshotPath String?
pdfPath String?
readabilityPath String?
lastPreserved DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt @default(now())
}
model Tag {
id Int @id @default(autoincrement())
name String
links Link[]
owner User @relation(fields: [ownerId], references: [id])
ownerId Int
id Int @id @default(autoincrement())
name String
links Link[]
owner User @relation(fields: [ownerId], references: [id])
ownerId Int
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt @default(now())
@ -160,10 +136,22 @@ model Subscription {
stripeSubscriptionId String @unique
currentPeriodStart DateTime
currentPeriodEnd DateTime
user User @relation(fields: [userId], references: [id])
userId Int @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt @default(now())
}
model ApiKey {
id Int @id @default(autoincrement())
name String
user User @relation(fields: [userId], references: [id])
userId Int
token String @unique
expires DateTime
lastUsedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt @default(now())
@@unique([token, userId])
}

View File

@ -142,7 +142,7 @@ body {
/* Theme */
@layer base {
body {
@apply dark:bg-neutral-900 bg-white dark:text-white;
@apply dark:bg-neutral-900 bg-white text-black dark:text-white;
}
}

View File

@ -2,6 +2,9 @@
module.exports = {
darkMode: "class",
// daisyui: {
// themes: ["light", "dark"],
// },
content: [
"./app/**/*.{js,ts,jsx,tsx}",
"./pages/**/*.{js,ts,jsx,tsx}",
@ -10,5 +13,5 @@ module.exports = {
// For the "layouts" directory
"./layouts/**/*.{js,ts,jsx,tsx}",
],
plugins: [],
plugins: [require("daisyui")],
};

View File

@ -2129,6 +2129,14 @@ crypto-js@^4.2.0:
resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631"
integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==
css-selector-tokenizer@^0.8:
version "0.8.0"
resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz#88267ef6238e64f2215ea2764b3e2cf498b845dd"
integrity sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==
dependencies:
cssesc "^3.0.0"
fastparse "^1.1.2"
cssesc@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
@ -2151,6 +2159,11 @@ csstype@^3.1.2:
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b"
integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==
culori@^3:
version "3.2.0"
resolved "https://registry.yarnpkg.com/culori/-/culori-3.2.0.tgz#df6561503f0cc20e8e1c029f086466666c0ac62f"
integrity sha512-HIEbTSP7vs1mPq/2P9In6QyFE0Tkpevh0k9a+FkjhD+cwsYm9WRSbn4uMdW9O0yXlNYC3ppxL3gWWPOcvEl57w==
cwise-compiler@^1.1.2:
version "1.1.3"
resolved "https://registry.yarnpkg.com/cwise-compiler/-/cwise-compiler-1.1.3.tgz#f4d667410e850d3a313a7d2db7b1e505bb034cc5"
@ -2158,6 +2171,16 @@ cwise-compiler@^1.1.2:
dependencies:
uniq "^1.0.0"
daisyui@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/daisyui/-/daisyui-4.4.2.tgz#669ee310be42894abe36d493635000d010fa4023"
integrity sha512-Ecg5loskj9dkaAnTSK5Xn5jb24TqDlQIg/NJ025jCkw2S/zw12btjvLgY2Sv5Ws1DFVoVBRs3XYXyojZG7zVnw==
dependencies:
css-selector-tokenizer "^0.8"
culori "^3"
picocolors "^1"
postcss-js "^4"
damerau-levenshtein@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
@ -2726,6 +2749,11 @@ fast-xml-parser@4.2.5:
dependencies:
strnum "^1.0.5"
fastparse@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9"
integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==
fastq@^1.6.0:
version "1.15.0"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a"
@ -4113,7 +4141,7 @@ performance-now@^2.1.0:
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==
picocolors@^1.0.0:
picocolors@^1, picocolors@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
@ -4159,7 +4187,7 @@ postcss-import@^15.1.0:
read-cache "^1.0.0"
resolve "^1.1.7"
postcss-js@^4.0.1:
postcss-js@^4, postcss-js@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.1.tgz#61598186f3703bab052f1c4f7d805f3991bee9d2"
integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==