mirror of
https://github.com/hpware/news-analyze.git
synced 2025-06-23 15:51:01 +08:00
feat: implement user authentication with GitHub OAuth, create database schema, and enhance navigation
This commit is contained in:
parent
d773473eb0
commit
98ffbec764
@ -1,4 +1,9 @@
|
|||||||
S3_ACCESS_KEY=""
|
S3_ACCESS_KEY=""
|
||||||
S3_SECRET_KEY=""
|
S3_SECRET_KEY=""
|
||||||
S3_BUCKETNAME=""
|
S3_BUCKETNAME=""
|
||||||
S3_ENDPOINT="" # Your S3 server, This can be Cloudflare R2, AWS S3, or just your own Minio infra.
|
S3_ENDPOINT=""
|
||||||
|
|
||||||
|
NUXT_GITHUB_CLIENT_ID=""
|
||||||
|
NUXT_GITHUB_CLIENT_SECRET=""
|
||||||
|
|
||||||
|
POSTGRES_URL=""
|
13
README.md
13
README.md
@ -0,0 +1,13 @@
|
|||||||
|
# 新聞解析 / News Analyze
|
||||||
|
|
||||||
|
## Stack:
|
||||||
|
- Postgres
|
||||||
|
- Passport.js
|
||||||
|
- Tailwind
|
||||||
|
- Nuxt
|
||||||
|
- Animate.css
|
||||||
|
- GSAP
|
||||||
|
- Zeabur
|
||||||
|
- Minio S3
|
||||||
|
- Nuxt i18N
|
||||||
|
- BunJS
|
15
bun.lock
15
bun.lock
@ -19,6 +19,7 @@
|
|||||||
"bootstrap-icons": "^1.12.1",
|
"bootstrap-icons": "^1.12.1",
|
||||||
"gsap": "^3.13.0",
|
"gsap": "^3.13.0",
|
||||||
"nuxt": "^3.17.2",
|
"nuxt": "^3.17.2",
|
||||||
|
"passport-github2": "^0.1.12",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"tailwindcss": "3",
|
"tailwindcss": "3",
|
||||||
"tailwindcss-animatecss": "^3.0.5",
|
"tailwindcss-animatecss": "^3.0.5",
|
||||||
@ -671,6 +672,8 @@
|
|||||||
|
|
||||||
"base64-js": ["base64-js@0.0.8", "", {}, "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw=="],
|
"base64-js": ["base64-js@0.0.8", "", {}, "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw=="],
|
||||||
|
|
||||||
|
"base64url": ["base64url@3.0.1", "", {}, "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A=="],
|
||||||
|
|
||||||
"binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
|
"binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
|
||||||
|
|
||||||
"bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="],
|
"bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="],
|
||||||
@ -1505,6 +1508,8 @@
|
|||||||
|
|
||||||
"nypm": ["nypm@0.6.0", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.0", "pathe": "^2.0.3", "pkg-types": "^2.0.0", "tinyexec": "^0.3.2" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg=="],
|
"nypm": ["nypm@0.6.0", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.0", "pathe": "^2.0.3", "pkg-types": "^2.0.0", "tinyexec": "^0.3.2" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg=="],
|
||||||
|
|
||||||
|
"oauth": ["oauth@0.10.2", "", {}, "sha512-JtFnB+8nxDEXgNyniwz573xxbKSOu3R8D40xQKqcjwJ2CDkYqUDI53o6IuzDJBx60Z8VKCm271+t8iFjakrl8Q=="],
|
||||||
|
|
||||||
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
||||||
|
|
||||||
"object-hash": ["object-hash@3.0.0", "", {}, "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="],
|
"object-hash": ["object-hash@3.0.0", "", {}, "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="],
|
||||||
@ -1565,6 +1570,12 @@
|
|||||||
|
|
||||||
"parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="],
|
"parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="],
|
||||||
|
|
||||||
|
"passport-github2": ["passport-github2@0.1.12", "", { "dependencies": { "passport-oauth2": "1.x.x" } }, "sha512-3nPUCc7ttF/3HSP/k9sAXjz3SkGv5Nki84I05kSQPo01Jqq1NzJACgMblCK0fGcv9pKCG/KXU3AJRDGLqHLoIw=="],
|
||||||
|
|
||||||
|
"passport-oauth2": ["passport-oauth2@1.8.0", "", { "dependencies": { "base64url": "3.x.x", "oauth": "0.10.x", "passport-strategy": "1.x.x", "uid2": "0.0.x", "utils-merge": "1.x.x" } }, "sha512-cjsQbOrXIDE4P8nNb3FQRCCmJJ/utnFKEz2NX209f7KOHPoX18gF7gBzBbLLsj2/je4KrgiwLLGjf0lm9rtTBA=="],
|
||||||
|
|
||||||
|
"passport-strategy": ["passport-strategy@1.0.0", "", {}, "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA=="],
|
||||||
|
|
||||||
"path-exists": ["path-exists@5.0.0", "", {}, "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ=="],
|
"path-exists": ["path-exists@5.0.0", "", {}, "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ=="],
|
||||||
|
|
||||||
"path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="],
|
"path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="],
|
||||||
@ -1965,6 +1976,8 @@
|
|||||||
|
|
||||||
"ufo": ["ufo@1.6.1", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="],
|
"ufo": ["ufo@1.6.1", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="],
|
||||||
|
|
||||||
|
"uid2": ["uid2@0.0.4", "", {}, "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA=="],
|
||||||
|
|
||||||
"ultrahtml": ["ultrahtml@1.6.0", "", {}, "sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw=="],
|
"ultrahtml": ["ultrahtml@1.6.0", "", {}, "sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw=="],
|
||||||
|
|
||||||
"uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="],
|
"uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="],
|
||||||
@ -2017,6 +2030,8 @@
|
|||||||
|
|
||||||
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
||||||
|
|
||||||
|
"utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="],
|
||||||
|
|
||||||
"uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="],
|
"uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="],
|
||||||
|
|
||||||
"validate-npm-package-license": ["validate-npm-package-license@3.0.4", "", { "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew=="],
|
"validate-npm-package-license": ["validate-npm-package-license@3.0.4", "", { "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew=="],
|
||||||
|
@ -25,18 +25,19 @@ const toggleDropdown = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div class="text-[0.9em] left-1/2 absolute transform -translate-x-1/2 space-x-4 items-center">
|
<div class="text-[0.9em] left-1/2 absolute transform -translate-x-1/2 space-x-4 items-center">
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
:to="localePath('home')"
|
:to="localePath('/home')"
|
||||||
class="hover:text-blue-500 cursor-pointer transiton-all duration-100"
|
class="hover:text-blue-500 cursor-pointer transiton-all duration-100"
|
||||||
>{{ t("nav.home") }}</NuxtLink
|
>{{ t("nav.home") }}</NuxtLink
|
||||||
>
|
>
|
||||||
|
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
:to="localePath('dailybriefing')"
|
:to="localePath('/dailybriefing')"
|
||||||
class="hover:text-blue-500 cursor-pointer transiton-all duration-100"
|
class="hover:text-blue-500 cursor-pointer transiton-all duration-100"
|
||||||
>{{ t("nav.dailybriefing") }}</NuxtLink
|
>{{ t("nav.dailybriefing") }}</NuxtLink
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="relative">
|
<div class="flex flex-row align-center justify-center text-center">
|
||||||
|
<div class="relative ml-0">
|
||||||
<button
|
<button
|
||||||
@click="toggleDropdown"
|
@click="toggleDropdown"
|
||||||
class="flex items-center space-x-1 px-4 py-2 rounded hover:bg-gray-900 transition-all duration-100 mr-5"
|
class="flex items-center space-x-1 px-4 py-2 rounded hover:bg-gray-900 transition-all duration-100 mr-5"
|
||||||
@ -56,7 +57,6 @@ const toggleDropdown = () => {
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<Transition
|
<Transition
|
||||||
enter-active-class="animate__animated animate__fadeInDown animate_fastest"
|
enter-active-class="animate__animated animate__fadeInDown animate_fastest"
|
||||||
leave-active-class="animate__animated animate__fadeOutUp animate_fastest"
|
leave-active-class="animate__animated animate__fadeOutUp animate_fastest"
|
||||||
@ -77,6 +77,14 @@ const toggleDropdown = () => {
|
|||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mr-2 ml-0">
|
||||||
|
<NuxtLink :to="localePath('/system/login')">
|
||||||
|
<button class="text-white hover:text-[#C6C6C6] transition-all duration-150">
|
||||||
|
<i class="bi bi-person text-3xl"></i>
|
||||||
|
</button>
|
||||||
|
</NuxtLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
50
createDatabase.ts
Normal file
50
createDatabase.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { sql } from "bun";
|
||||||
|
|
||||||
|
const createUsers = await sql`
|
||||||
|
create table if not exists users (
|
||||||
|
uuid text primary key,
|
||||||
|
created_at timestampz default current_timestamp,
|
||||||
|
username text not null unique,
|
||||||
|
oauthProvider text not null,
|
||||||
|
avatarUrl text not null,
|
||||||
|
email text not null,
|
||||||
|
oauthProviderGivenId text not null
|
||||||
|
);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const createNewsProviders = await sql`
|
||||||
|
create table if not exists newsProviders (
|
||||||
|
uuid text primary key,
|
||||||
|
title text not null,
|
||||||
|
slug text unique,
|
||||||
|
website text not null,
|
||||||
|
description text not null,
|
||||||
|
facebookUrl text,
|
||||||
|
twitterUrl text,
|
||||||
|
threadsUrl text,
|
||||||
|
logoUrl text not null,
|
||||||
|
lean text not null
|
||||||
|
)
|
||||||
|
`
|
||||||
|
|
||||||
|
const createAdminPosts = await sql`
|
||||||
|
create table if not exists adminPosts (
|
||||||
|
uuid text primary key,
|
||||||
|
slug text not null unique,
|
||||||
|
content text not null,
|
||||||
|
created_at timestampz default current_timestamp,
|
||||||
|
byUser text not null
|
||||||
|
)
|
||||||
|
`
|
||||||
|
const adminUsers = await sql`
|
||||||
|
create table if not exists adminUsers (
|
||||||
|
uuid text primary key,
|
||||||
|
username text not null unique,
|
||||||
|
passwordHash text not null,
|
||||||
|
created_at timestampz default current_timestamp,
|
||||||
|
lastlogged_at timestampz default current_timestamp,
|
||||||
|
)
|
||||||
|
`
|
||||||
|
|
||||||
|
|
||||||
|
console.log("Creation Complete");
|
7
layouts/admin.vue
Normal file
7
layouts/admin.vue
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<main>
|
||||||
|
<slot />
|
||||||
|
</main>
|
||||||
|
</template>
|
@ -8,7 +8,8 @@
|
|||||||
"generate": "nuxt generate",
|
"generate": "nuxt generate",
|
||||||
"preview": "nuxt preview",
|
"preview": "nuxt preview",
|
||||||
"postinstall": "nuxt prepare",
|
"postinstall": "nuxt prepare",
|
||||||
"prettier": "prettier --write ."
|
"prettier": "prettier --write .",
|
||||||
|
"createdb": "bun run createDatabase.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource-variable/noto-sans-tc": "^5.2.5",
|
"@fontsource-variable/noto-sans-tc": "^5.2.5",
|
||||||
@ -26,6 +27,7 @@
|
|||||||
"bootstrap-icons": "^1.12.1",
|
"bootstrap-icons": "^1.12.1",
|
||||||
"gsap": "^3.13.0",
|
"gsap": "^3.13.0",
|
||||||
"nuxt": "^3.17.2",
|
"nuxt": "^3.17.2",
|
||||||
|
"passport-github2": "^0.1.12",
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"tailwindcss": "3",
|
"tailwindcss": "3",
|
||||||
"tailwindcss-animatecss": "^3.0.5",
|
"tailwindcss-animatecss": "^3.0.5",
|
||||||
|
12
pages/admin/login.vue
Normal file
12
pages/admin/login.vue
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
definePageMeta({
|
||||||
|
layout: "admin"
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="flex justify-center min-h-screen w-full">
|
||||||
|
<input type="text"/>
|
||||||
|
<input type="password" />
|
||||||
|
<button>登入</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
25
pages/system/login.vue
Normal file
25
pages/system/login.vue
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<template>
|
||||||
|
<div class="w-full min-h-screen flex items-center justify-center text-center">
|
||||||
|
<div class="border border-white w-[40%] p-16 justify-center align-center text-center rounded-md backdrop-blur-sm bg-gray-900">
|
||||||
|
<h1 class="text-2xl">Login / Register</h1>
|
||||||
|
<h4 class="text-sm">via OAuth Providers</h4>
|
||||||
|
<div class="m-4 flex flex-col gap-2">
|
||||||
|
<a href="/api/auth/google">
|
||||||
|
<button class="gap-3 px-10 justify-between align-center text-center bg-gray-500 hover:bg-gray-700 p-2 rounded-md transition-all duration-150">
|
||||||
|
<i class="bi bi-google"></i> <span>Google</span>
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
<a href="/api/auth/github">
|
||||||
|
<button class="gap-3 px-10 bg-gray-500 hover:bg-gray-700 p-2 rounded-md transition-all duration-150">
|
||||||
|
<i class="bi bi-github"></i> <span>Github</span>
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
<a href="/api/auth/discord">
|
||||||
|
<button class="gap-3 px-10 bg-gray-500 hover:bg-gray-700 p-2 rounded-md transition-all duration-150">
|
||||||
|
<i class="bi bi-discord"></i> <span>Discord</span>
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
8
server/api/auth/callback/github.get.ts
Normal file
8
server/api/auth/callback/github.get.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
async function findUser(githubUser: any) {
|
||||||
|
console.log("Github User: " + githubUser);
|
||||||
|
|
||||||
|
}
|
16
server/api/auth/github.get.ts
Normal file
16
server/api/auth/github.get.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import crypto from "node:crypto"
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const baseUrl = event.node.req.headers.host
|
||||||
|
const protocol = process.env.NODE_ENV === "production" ? "https": "http"
|
||||||
|
const clientId = process.env.NUXT_GITHUB_CLIENT_ID;
|
||||||
|
const callbackUrl = `${protocol}://${baseUrl}/api/auth/github/callback`;
|
||||||
|
const state = crypto.randomBytes(16).toString("hex");
|
||||||
|
setCookie(event, 'oauth_state', state, {
|
||||||
|
httpOnly: true,
|
||||||
|
secure: process.env.NODE_ENV === 'production',
|
||||||
|
maxAge: 60 * 10,
|
||||||
|
path: '/',
|
||||||
|
})
|
||||||
|
const authorizationUrl = `https://github.com/login/oauth/authorize?client_id=${clientId}&redirect_uri=${encodeURIComponent(callbackUrl)}&scope=read:user,user:email&state=${state}`
|
||||||
|
await sendRedirect(event, authorizationUrl, 302)
|
||||||
|
})
|
@ -14,7 +14,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* noto-sans-tc-chinese-traditional-wght-normal */
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'Noto Sans TC Variable';
|
font-family: 'Noto Sans TC Variable';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user