Descriptive commit message

This commit is contained in:
Skip R. 2023-04-24 22:13:35 -07:00
parent 78ce62c377
commit 268a69c152
17 changed files with 569 additions and 6 deletions

2
.gitignore vendored
View file

@ -33,3 +33,5 @@ yarn-error.log*
# typescript # typescript
*.tsbuildinfo *.tsbuildinfo
next-env.d.ts next-env.d.ts
database.db*

3
.vsls.json Normal file
View file

@ -0,0 +1,3 @@
{
"gitignore": "none"
}

12
environment.d.ts vendored Normal file
View 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
View file

@ -8,6 +8,7 @@
"name": "gluestick", "name": "gluestick",
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"@prisma/client": "^4.13.0",
"@types/node": "18.16.0", "@types/node": "18.16.0",
"@types/react": "18.0.38", "@types/react": "18.0.38",
"@types/react-dom": "18.0.11", "@types/react-dom": "18.0.11",
@ -16,7 +17,12 @@
"next": "13.3.1", "next": "13.3.1",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "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": { "node_modules/@babel/runtime": {
@ -311,6 +317,38 @@
"url": "https://opencollective.com/unts" "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": { "node_modules/@rushstack/eslint-patch": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz", "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", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" "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": { "node_modules/@typescript-eslint/parser": {
"version": "5.59.1", "version": "5.59.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.1.tgz",
@ -2673,6 +2717,23 @@
"node": ">= 0.8.0" "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": { "node_modules/prop-types": {
"version": "15.8.1", "version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@ -3216,6 +3277,14 @@
"punycode": "^2.1.0" "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": { "node_modules/which": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@ -3474,6 +3543,25 @@
"tslib": "^2.4.0" "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": { "@rushstack/eslint-patch": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz", "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", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" "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": { "@typescript-eslint/parser": {
"version": "5.59.1", "version": "5.59.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.1.tgz", "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", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" "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": { "prop-types": {
"version": "15.8.1", "version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@ -5491,6 +5594,11 @@
"punycode": "^2.1.0" "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": { "which": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",

View file

@ -9,6 +9,7 @@
"lint": "next lint" "lint": "next lint"
}, },
"dependencies": { "dependencies": {
"@prisma/client": "^4.13.0",
"@types/node": "18.16.0", "@types/node": "18.16.0",
"@types/react": "18.0.38", "@types/react": "18.0.38",
"@types/react-dom": "18.0.11", "@types/react-dom": "18.0.11",
@ -17,6 +18,11 @@
"next": "13.3.1", "next": "13.3.1",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "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"
} }
} }

View 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");

View 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
View 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
}

View file

@ -1,3 +0,0 @@
export async function GET(request: Request) {
return new Response(":3");
}

191
src/app/icon.svg Normal file
View 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

View 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=/;`
}
});
}

View 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();

View 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"
}
});
}

View file

@ -6,3 +6,22 @@
width: 100vw; width: 100vw;
height: 100vh; 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;
}

View file

@ -3,7 +3,11 @@ import styles from "./page.module.css";
export default function Home() { export default function Home() {
return ( return (
<main className={styles.main}> <main className={styles.main}>
<p>:3</p> <p>
:3
<br />
<a href="/oauth/discord/login">login debug</a>
</p>
</main> </main>
); );
} }

48
src/app/register/page.tsx Normal file
View 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
View file

@ -0,0 +1,3 @@
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
export default prisma;