Descriptive commit message
This commit is contained in:
parent
78ce62c377
commit
268a69c152
17 changed files with 569 additions and 6 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -33,3 +33,5 @@ yarn-error.log*
|
|||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
database.db*
|
||||
|
|
3
.vsls.json
Normal file
3
.vsls.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"gitignore": "none"
|
||||
}
|
12
environment.d.ts
vendored
Normal file
12
environment.d.ts
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
declare global {
|
||||
namespace NodeJS {
|
||||
interface ProcessEnv {
|
||||
DISCORD_CLIENT_ID: string;
|
||||
DISCORD_CLIENT_SECRET: string;
|
||||
DISCORD_ALLOWED_GUILDS: string;
|
||||
BASE_DOMAIN: string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
110
package-lock.json
generated
110
package-lock.json
generated
|
@ -8,6 +8,7 @@
|
|||
"name": "gluestick",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@prisma/client": "^4.13.0",
|
||||
"@types/node": "18.16.0",
|
||||
"@types/react": "18.0.38",
|
||||
"@types/react-dom": "18.0.11",
|
||||
|
@ -16,7 +17,12 @@
|
|||
"next": "13.3.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"typescript": "5.0.4"
|
||||
"typescript": "5.0.4",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/uuid": "^9.0.1",
|
||||
"prisma": "^4.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
|
@ -311,6 +317,38 @@
|
|||
"url": "https://opencollective.com/unts"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/client": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.13.0.tgz",
|
||||
"integrity": "sha512-YaiiICcRB2hatxsbnfB66uWXjcRw3jsZdlAVxmx0cFcTc/Ad/sKdHCcWSnqyDX47vAewkjRFwiLwrOUjswVvmA==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@prisma/engines-version": "4.13.0-50.1e7af066ee9cb95cf3a403c78d9aab3e6b04f37a"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prisma": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"prisma": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/engines": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.13.0.tgz",
|
||||
"integrity": "sha512-HrniowHRZXHuGT9XRgoXEaP2gJLXM5RMoItaY2PkjvuZ+iHc0Zjbm/302MB8YsPdWozAPHHn+jpFEcEn71OgPw==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true
|
||||
},
|
||||
"node_modules/@prisma/engines-version": {
|
||||
"version": "4.13.0-50.1e7af066ee9cb95cf3a403c78d9aab3e6b04f37a",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.13.0-50.1e7af066ee9cb95cf3a403c78d9aab3e6b04f37a.tgz",
|
||||
"integrity": "sha512-fsQlbkhPJf08JOzKoyoD9atdUijuGBekwoOPZC3YOygXEml1MTtgXVpnUNchQlRSY82OQ6pSGQ9PxUe4arcSLQ=="
|
||||
},
|
||||
"node_modules/@rushstack/eslint-patch": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz",
|
||||
|
@ -362,6 +400,12 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
|
||||
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ=="
|
||||
},
|
||||
"node_modules/@types/uuid": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.1.tgz",
|
||||
"integrity": "sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "5.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.1.tgz",
|
||||
|
@ -2673,6 +2717,23 @@
|
|||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prisma": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.13.0.tgz",
|
||||
"integrity": "sha512-L9mqjnSmvWIRCYJ9mQkwCtj4+JDYYTdhoyo8hlsHNDXaZLh/b4hR0IoKIBbTKxZuyHQzLopb/+0Rvb69uGV7uA==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@prisma/engines": "4.13.0"
|
||||
},
|
||||
"bin": {
|
||||
"prisma": "build/index.js",
|
||||
"prisma2": "build/index.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/prop-types": {
|
||||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
|
@ -3216,6 +3277,14 @@
|
|||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
|
||||
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
@ -3474,6 +3543,25 @@
|
|||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"@prisma/client": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.13.0.tgz",
|
||||
"integrity": "sha512-YaiiICcRB2hatxsbnfB66uWXjcRw3jsZdlAVxmx0cFcTc/Ad/sKdHCcWSnqyDX47vAewkjRFwiLwrOUjswVvmA==",
|
||||
"requires": {
|
||||
"@prisma/engines-version": "4.13.0-50.1e7af066ee9cb95cf3a403c78d9aab3e6b04f37a"
|
||||
}
|
||||
},
|
||||
"@prisma/engines": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.13.0.tgz",
|
||||
"integrity": "sha512-HrniowHRZXHuGT9XRgoXEaP2gJLXM5RMoItaY2PkjvuZ+iHc0Zjbm/302MB8YsPdWozAPHHn+jpFEcEn71OgPw==",
|
||||
"devOptional": true
|
||||
},
|
||||
"@prisma/engines-version": {
|
||||
"version": "4.13.0-50.1e7af066ee9cb95cf3a403c78d9aab3e6b04f37a",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.13.0-50.1e7af066ee9cb95cf3a403c78d9aab3e6b04f37a.tgz",
|
||||
"integrity": "sha512-fsQlbkhPJf08JOzKoyoD9atdUijuGBekwoOPZC3YOygXEml1MTtgXVpnUNchQlRSY82OQ6pSGQ9PxUe4arcSLQ=="
|
||||
},
|
||||
"@rushstack/eslint-patch": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz",
|
||||
|
@ -3525,6 +3613,12 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
|
||||
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ=="
|
||||
},
|
||||
"@types/uuid": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.1.tgz",
|
||||
"integrity": "sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==",
|
||||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/parser": {
|
||||
"version": "5.59.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.1.tgz",
|
||||
|
@ -5128,6 +5222,15 @@
|
|||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="
|
||||
},
|
||||
"prisma": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.13.0.tgz",
|
||||
"integrity": "sha512-L9mqjnSmvWIRCYJ9mQkwCtj4+JDYYTdhoyo8hlsHNDXaZLh/b4hR0IoKIBbTKxZuyHQzLopb/+0Rvb69uGV7uA==",
|
||||
"devOptional": true,
|
||||
"requires": {
|
||||
"@prisma/engines": "4.13.0"
|
||||
}
|
||||
},
|
||||
"prop-types": {
|
||||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
|
@ -5491,6 +5594,11 @@
|
|||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"uuid": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
|
||||
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg=="
|
||||
},
|
||||
"which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/client": "^4.13.0",
|
||||
"@types/node": "18.16.0",
|
||||
"@types/react": "18.0.38",
|
||||
"@types/react-dom": "18.0.11",
|
||||
|
@ -17,6 +18,11 @@
|
|||
"next": "13.3.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"typescript": "5.0.4"
|
||||
"typescript": "5.0.4",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/uuid": "^9.0.1",
|
||||
"prisma": "^4.13.0"
|
||||
}
|
||||
}
|
||||
|
|
19
prisma/migrations/20230425043204_init/migration.sql
Normal file
19
prisma/migrations/20230425043204_init/migration.sql
Normal file
|
@ -0,0 +1,19 @@
|
|||
-- CreateTable
|
||||
CREATE TABLE "AuthTicket" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"username" TEXT,
|
||||
"ticket" TEXT NOT NULL
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "DiscordAuth" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"authTicketId" INTEGER NOT NULL,
|
||||
"accessToken" TEXT NOT NULL,
|
||||
"refreshToken" TEXT NOT NULL,
|
||||
"refreshAt" DATETIME NOT NULL,
|
||||
CONSTRAINT "DiscordAuth_authTicketId_fkey" FOREIGN KEY ("authTicketId") REFERENCES "AuthTicket" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "DiscordAuth_authTicketId_key" ON "DiscordAuth"("authTicketId");
|
3
prisma/migrations/migration_lock.toml
Normal file
3
prisma/migrations/migration_lock.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (i.e. Git)
|
||||
provider = "sqlite"
|
26
prisma/schema.prisma
Normal file
26
prisma/schema.prisma
Normal file
|
@ -0,0 +1,26 @@
|
|||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "sqlite"
|
||||
url = "file:./database.db"
|
||||
}
|
||||
|
||||
model AuthTicket {
|
||||
id Int @id @default(autoincrement())
|
||||
username String?
|
||||
ticket String
|
||||
discordAuth DiscordAuth?
|
||||
}
|
||||
|
||||
model DiscordAuth {
|
||||
id String @id
|
||||
|
||||
authTicket AuthTicket @relation(fields: [authTicketId], references: [id])
|
||||
authTicketId Int @unique
|
||||
|
||||
accessToken String
|
||||
refreshToken String
|
||||
refreshAt DateTime
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
export async function GET(request: Request) {
|
||||
return new Response(":3");
|
||||
}
|
191
src/app/icon.svg
Normal file
191
src/app/icon.svg
Normal file
|
@ -0,0 +1,191 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="31.999998"
|
||||
height="31.999998"
|
||||
viewBox="0 0 8.4666652 8.4666652"
|
||||
version="1.1"
|
||||
id="svg327"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs324" />
|
||||
<g
|
||||
id="layer1">
|
||||
<g
|
||||
id="g1582"
|
||||
style="clip-rule:evenodd;display:none;fill-rule:evenodd;stroke-linecap:round;stroke-miterlimit:1.5"
|
||||
transform="scale(0.26458333)">
|
||||
<clipPath
|
||||
id="_clip1">
|
||||
<rect
|
||||
x="0"
|
||||
y="0"
|
||||
width="32"
|
||||
height="32"
|
||||
id="rect1513" />
|
||||
|
||||
</clipPath>
|
||||
|
||||
<path
|
||||
id="path2704"
|
||||
style="clip-rule:evenodd;display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:4;stroke-linecap:round;stroke-miterlimit:1.5;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
|
||||
d="M 10,6 C 8,6 6,8.000001 6,9 v 14 1 3 h 3 l 1,-1 h 12 l 1,1 h 3 V 24 23 9 C 26,8.000001 24,6 22,6 h -6 z"
|
||||
transform="translate(0,-1)" />
|
||||
|
||||
<rect
|
||||
style="clip-rule:evenodd;display:inline;fill:#777777;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:1.5;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"
|
||||
id="rect4044"
|
||||
width="24"
|
||||
height="2"
|
||||
x="4"
|
||||
y="28"
|
||||
ry="0"
|
||||
rx="0" />
|
||||
<path
|
||||
d="M 26,9 C 26,8 24,6 22,6 H 16 C 16,6 12,6 10,6 8,6 6,8 6,9 v 15 h 20 z"
|
||||
style="clip-rule:evenodd;display:inline;fill:#777777;fill-rule:evenodd;stroke-linecap:round;stroke-miterlimit:1.5"
|
||||
id="path1524"
|
||||
transform="translate(0,-1)" />
|
||||
<path
|
||||
d="M 20,22 19.989,21 H 12 v 1 H 6 v 4 h 3 l 1,-1 h 12 l 1,1 h 3 v -4 z"
|
||||
style="clip-rule:evenodd;fill:#1a1a1a;fill-rule:evenodd;stroke-linecap:round;stroke-miterlimit:1.5"
|
||||
id="path1578" />
|
||||
<circle
|
||||
cx="22"
|
||||
cy="24"
|
||||
r="1"
|
||||
style="clip-rule:evenodd;fill:#ffffff;fill-rule:evenodd;stroke-linecap:round;stroke-miterlimit:1.5"
|
||||
id="circle1530"
|
||||
transform="translate(0,-17)" />
|
||||
<circle
|
||||
cx="22"
|
||||
cy="24"
|
||||
r="1"
|
||||
style="clip-rule:evenodd;fill:#ffffff;fill-rule:evenodd;stroke-linecap:round;stroke-miterlimit:1.5"
|
||||
id="circle1534"
|
||||
transform="translate(-12,-17)" />
|
||||
<rect
|
||||
x="12"
|
||||
y="10"
|
||||
width="6"
|
||||
height="1"
|
||||
style="clip-rule:evenodd;fill:#e9a620;fill-rule:evenodd;stroke-linecap:round;stroke-miterlimit:1.5"
|
||||
id="rect1554"
|
||||
transform="matrix(0.666667,0,0,1,5,-4)" />
|
||||
<rect
|
||||
x="19"
|
||||
y="10"
|
||||
width="1"
|
||||
height="1"
|
||||
style="clip-rule:evenodd;fill:#e9a620;fill-rule:evenodd;stroke-linecap:round;stroke-miterlimit:1.5"
|
||||
id="rect1558"
|
||||
transform="translate(-1,-4)" />
|
||||
<path
|
||||
d="m 7,19 h 4 v 1 H 7 v 1 h 4 v 1 H 7 v 1 H 6 v -5 h 1 z"
|
||||
style="clip-rule:evenodd;fill:#888888;fill-rule:evenodd;stroke-linecap:round;stroke-miterlimit:1.5"
|
||||
id="path1538"
|
||||
transform="translate(0,-2)" />
|
||||
<path
|
||||
d="m 26,18 v 5 h -1 v -1 h -4 v -1 h 4 v -1 h -4 v -1 h 4 v -1 z"
|
||||
style="clip-rule:evenodd;fill:#888888;fill-rule:evenodd;stroke-linecap:round;stroke-miterlimit:1.5"
|
||||
id="path1542"
|
||||
transform="translate(0,-2)" />
|
||||
<path
|
||||
d="m 18,11 c 0.55,0 0.997,0.445 1,0.994 V 12 23 H 13 V 12 11.994 C 13.003,11.445 13.45,11 14,11 Z"
|
||||
style="clip-rule:evenodd;fill:#666666;fill-rule:evenodd;stroke-linecap:round;stroke-miterlimit:1.5"
|
||||
id="path1546"
|
||||
transform="translate(0,-2)" />
|
||||
<path
|
||||
d="m 21,11 h 3 c 0.552,0 1,0.448 1,1 v 4 c 0,0.55 -0.445,0.997 -0.994,1 H 24 21 c -0.552,0 -1,-0.448 -1,-1 v -4 c 0,-0.552 0.448,-1 1,-1 z"
|
||||
id="path1550"
|
||||
style="clip-rule:evenodd;fill-rule:evenodd;stroke-linecap:round;stroke-miterlimit:1.5"
|
||||
transform="translate(0,-2)" />
|
||||
<path
|
||||
d="m 12,16 c 0,0.552 -0.448,1 -1,1 H 8 C 7.448,17 7,16.552 7,16 v -4 c 0,-0.552 0.448,-1 1,-1 h 3 c 0.55,0 0.997,0.445 1,0.994 V 12 Z"
|
||||
id="path1562"
|
||||
style="clip-rule:evenodd;fill-rule:evenodd;stroke-linecap:round;stroke-miterlimit:1.5"
|
||||
transform="translate(0,-2)" />
|
||||
<path
|
||||
d="M 14.994,18 C 14.445,17.997 14,17.55 14,17 v -4 c 0,-0.552 0.448,-1 1,-1 h 2 c 0.552,0 1,0.448 1,1 v 4 c 0,0.55 -0.445,0.997 -0.994,1 H 17 15 Z"
|
||||
id="path1566"
|
||||
style="clip-rule:evenodd;fill-rule:evenodd;stroke-linecap:round;stroke-miterlimit:1.5"
|
||||
transform="translate(0,-2)" />
|
||||
<path
|
||||
d="m 12,24 h -2 v 2.5 c 0,0.133 0.053,0.26 0.146,0.354 C 10.24,26.947 10.367,27 10.5,27 h 1 c 0.133,0 0.26,-0.053 0.354,-0.146 C 11.947,26.76 12,26.633 12,26.5 Z"
|
||||
style="clip-rule:evenodd;fill:#262626;fill-rule:evenodd;stroke-linecap:round;stroke-miterlimit:1.5"
|
||||
id="path1570"
|
||||
transform="translate(-0.0263628,1)" />
|
||||
<path
|
||||
d="m 22,24 h -2 v 2.5 c 0,0.133 0.053,0.26 0.146,0.354 C 20.24,26.947 20.367,27 20.5,27 h 1 c 0.133,0 0.26,-0.053 0.354,-0.146 C 21.947,26.76 22,26.633 22,26.5 Z"
|
||||
style="clip-rule:evenodd;display:none;fill:#262626;fill-rule:evenodd;stroke-linecap:round;stroke-miterlimit:1.5"
|
||||
id="path1574"
|
||||
transform="translate(-0.0171758,1)" />
|
||||
</g>
|
||||
<path
|
||||
id="path3192"
|
||||
style="fill:#afafe9;fill-opacity:1;stroke:none;stroke-width:1.05833;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
d="M 6.5463697,1.4789793 C 6.0670904,1.4803459 5.6637357,1.628841 5.0270828,1.7197915 H 4.8947911 l 0.396875,1.5874998 c 0,0 0.3968755,0.2645835 1.0583332,0.3968749 0.6614576,0.1322916 0.926042,0.5291669 1.3229165,0.79375 0.2118459,0.1412306 0.5368253,0.1315796 0.7937499,0.091984 V 2.5197426 c -0.040284,-0.00394 -0.083861,-0.0062 -0.1322917,-0.0062 -0.5454518,0 -0.1322925,-0.6614585 -1.0583332,-0.9260416 C 6.9977877,1.5079989 6.7642239,1.4783582 6.5463697,1.4789793 Z" />
|
||||
<rect
|
||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.899228;stroke-linecap:square;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="rect2807"
|
||||
width="1.7466048"
|
||||
height="6.481462"
|
||||
x="4.5804152"
|
||||
y="-2.4549208"
|
||||
ry="0.46296161"
|
||||
transform="rotate(44.094445)" />
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:none;stroke-width:0.0433012;stroke-linecap:square;paint-order:stroke fill markers"
|
||||
id="rect1071"
|
||||
width="1.5874997"
|
||||
height="4.7624993"
|
||||
x="4.6599679"
|
||||
y="-1.8600227"
|
||||
ry="0"
|
||||
transform="rotate(44.094445)" />
|
||||
<rect
|
||||
style="fill:#afafe9;stroke:none;stroke-width:0.0433012;stroke-linecap:square;paint-order:stroke fill markers"
|
||||
id="rect749"
|
||||
width="1.5874997"
|
||||
height="1.5874999"
|
||||
x="4.6599679"
|
||||
y="-2.3891892"
|
||||
ry="0.52916664"
|
||||
transform="rotate(44.094445)" />
|
||||
<rect
|
||||
style="fill:#ff7f2a;stroke:none;stroke-width:0.0433012;stroke-linecap:square;paint-order:stroke fill markers"
|
||||
id="rect753"
|
||||
width="1.5874997"
|
||||
height="1.0583332"
|
||||
x="4.6599679"
|
||||
y="2.9024765"
|
||||
ry="0.52916658"
|
||||
transform="rotate(44.094445)" />
|
||||
<path
|
||||
style="fill:#ff6600;stroke:none;stroke-width:0.0433012;stroke-linecap:square;paint-order:stroke fill markers"
|
||||
d="M 4.8606341,3.9615115 3.7205016,2.8568606 2.9840677,3.6169489 3.1859169,4.5493186 4.1242001,4.7215998 Z"
|
||||
id="path917" />
|
||||
<path
|
||||
style="fill:#00ffff;stroke:none;stroke-width:0.0433012;stroke-linecap:square;paint-order:stroke fill markers"
|
||||
d="M 3.1859169,4.5493186 4.1242001,4.7215998 3.5718746,5.2916661 2.6335914,5.1193848 2.4317422,4.1870152 2.9840677,3.6169489 Z"
|
||||
id="path973" />
|
||||
<rect
|
||||
style="fill:#afafe9;fill-opacity:1;stroke:none;stroke-width:0.0433012;stroke-linecap:square;paint-order:stroke fill markers"
|
||||
id="rect1680"
|
||||
width="1.5874997"
|
||||
height="0.52916658"
|
||||
x="4.6599679"
|
||||
y="-1.3308557"
|
||||
transform="rotate(44.094445)" />
|
||||
<rect
|
||||
style="fill:#ff7f2a;fill-opacity:1;stroke:none;stroke-width:0.0433012;stroke-linecap:square;paint-order:stroke fill markers"
|
||||
id="rect2805"
|
||||
width="1.5874997"
|
||||
height="0.52916658"
|
||||
x="4.6599679"
|
||||
y="2.9024765"
|
||||
transform="rotate(44.094445)" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 7.9 KiB |
25
src/app/oauth/discord/login/route.ts
Normal file
25
src/app/oauth/discord/login/route.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { discordRedirectUri } from "../oauth";
|
||||
|
||||
export async function GET(request: Request) {
|
||||
let url = `https://discord.com/oauth2/authorize`;
|
||||
|
||||
let randomAssString = Math.random().toString(36).substring(2, 15);
|
||||
|
||||
let params = new URLSearchParams();
|
||||
params.set("response_type", "code");
|
||||
params.set("client_id", process.env.DISCORD_CLIENT_ID);
|
||||
params.set("scope", "guilds identify email");
|
||||
params.set("state", randomAssString);
|
||||
params.set("redirect_uri", discordRedirectUri());
|
||||
params.set("prompt", "consent");
|
||||
|
||||
url += "?" + params.toString();
|
||||
|
||||
return new Response(null, {
|
||||
status: 302,
|
||||
headers: {
|
||||
Location: url,
|
||||
"Set-Cookie": `state=${randomAssString}; Path=/;`
|
||||
}
|
||||
});
|
||||
}
|
25
src/app/oauth/discord/oauth.ts
Normal file
25
src/app/oauth/discord/oauth.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { v4 } from "uuid";
|
||||
|
||||
export type DiscordAccessTokenResponse = {
|
||||
access_token: string;
|
||||
token_type: string;
|
||||
expires_in: number;
|
||||
refresh_token: string;
|
||||
scope: string;
|
||||
};
|
||||
|
||||
export function discordRedirectUri() {
|
||||
return `${process.env.BASE_DOMAIN}oauth/discord/redirect`;
|
||||
}
|
||||
|
||||
export async function getDiscordID(token: string) {
|
||||
const req = await fetch("https://discord.com/api/users/@me", {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
const res: { id: string } = await req.json();
|
||||
return res.id;
|
||||
}
|
||||
|
||||
export const makeTicket = (): string => v4();
|
72
src/app/oauth/discord/redirect/route.ts
Normal file
72
src/app/oauth/discord/redirect/route.ts
Normal file
|
@ -0,0 +1,72 @@
|
|||
import { URLSearchParams } from "url";
|
||||
import {
|
||||
discordRedirectUri,
|
||||
DiscordAccessTokenResponse,
|
||||
makeTicket,
|
||||
getDiscordID
|
||||
} from "../oauth";
|
||||
import { cookies } from "next/dist/client/components/headers";
|
||||
import prisma from "@/prisma";
|
||||
|
||||
export async function GET(request: Request) {
|
||||
let url = new URL(request.url);
|
||||
let code = url.searchParams.get("code");
|
||||
let state = url.searchParams.get("state");
|
||||
|
||||
if (code === null || state === null)
|
||||
return new Response("missing code/state", { status: 400 });
|
||||
console.log(`code: ${code}, state: ${state}`);
|
||||
|
||||
const cookieStore = cookies();
|
||||
let cookieState = cookieStore.get("state");
|
||||
// prevent forgery
|
||||
console.log(`state: ${state}, cookieState: ${cookieState?.value}`);
|
||||
if (cookieState?.value !== state)
|
||||
return new Response("state is invalid", { status: 400 });
|
||||
|
||||
let form = new URLSearchParams();
|
||||
form.append("client_id", process.env.DISCORD_CLIENT_ID);
|
||||
form.append("client_secret", process.env.DISCORD_CLIENT_SECRET);
|
||||
form.append("grant_type", "authorization_code");
|
||||
form.append("code", code);
|
||||
form.append("redirect_uri", discordRedirectUri());
|
||||
|
||||
let tokenResponse = await fetch("https://discord.com/api/oauth2/token", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded"
|
||||
},
|
||||
body: form.toString()
|
||||
});
|
||||
if (!tokenResponse.ok) throw "baby";
|
||||
let tokenBody: DiscordAccessTokenResponse = await tokenResponse.json();
|
||||
const id = await getDiscordID(tokenBody.access_token);
|
||||
|
||||
const user = await prisma.authTicket.create({
|
||||
data: {
|
||||
username: null,
|
||||
ticket: makeTicket(),
|
||||
discordAuth: {
|
||||
connectOrCreate: {
|
||||
where: {
|
||||
id
|
||||
},
|
||||
create: {
|
||||
id,
|
||||
accessToken: tokenBody.access_token,
|
||||
refreshToken: tokenBody.refresh_token,
|
||||
refreshAt: new Date(Date.now() + tokenBody.expires_in * 1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return new Response(null, {
|
||||
status: 302,
|
||||
headers: {
|
||||
"Set-Cookie": `ticket=${user.ticket}; Path=/;`,
|
||||
Location: "/register"
|
||||
}
|
||||
});
|
||||
}
|
|
@ -6,3 +6,22 @@
|
|||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.form div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.form div label {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.form div input {
|
||||
width: 15rem;
|
||||
}
|
|
@ -3,7 +3,11 @@ import styles from "./page.module.css";
|
|||
export default function Home() {
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
<p>:3</p>
|
||||
<p>
|
||||
:3
|
||||
<br />
|
||||
<a href="/oauth/discord/login">login debug</a>
|
||||
</p>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
|
48
src/app/register/page.tsx
Normal file
48
src/app/register/page.tsx
Normal file
|
@ -0,0 +1,48 @@
|
|||
import { cookies } from "next/dist/client/components/headers";
|
||||
import styles from "../page.module.css";
|
||||
|
||||
export default function Page() {
|
||||
const cookieStore = cookies();
|
||||
const ticket = cookieStore.get("ticket");
|
||||
if (ticket === null) {
|
||||
return <div>Ticket is null?</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
<form action="/api/register" method="post" className={styles.form}>
|
||||
<div>
|
||||
<label htmlFor="username">Username</label>
|
||||
<input type="text" id="username" placeholder="julian" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="display-name">Display name</label>
|
||||
<input type="text" id="display-name" placeholder="NotNite" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="email">Email</label>
|
||||
<input type="text" id="email" placeholder="hi@notnite.com" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="password">Password</label>
|
||||
<input type="password" id="password" placeholder="deeznuts47" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="avatar">Avatar</label>
|
||||
<input
|
||||
type="file"
|
||||
id="avatar"
|
||||
name="avatar"
|
||||
accept="image/png, image/jpeg"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
</main>
|
||||
);
|
||||
}
|
3
src/prisma.ts
Normal file
3
src/prisma.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
import { PrismaClient } from "@prisma/client";
|
||||
const prisma = new PrismaClient();
|
||||
export default prisma;
|
Loading…
Reference in a new issue