Clean the awful code.

This commit is contained in:
吳元皓 2025-05-07 10:53:14 +08:00
parent 98ffbec764
commit 569cd087e7
15 changed files with 193 additions and 167 deletions

View File

@ -1,6 +1,7 @@
# 新聞解析 / News Analyze # 新聞解析 / News Analyze
## Stack: ## Stack:
- Postgres - Postgres
- Passport.js - Passport.js
- Tailwind - Tailwind

View File

@ -2,7 +2,7 @@
import "bootstrap-icons/font/bootstrap-icons.css"; import "bootstrap-icons/font/bootstrap-icons.css";
import "animate.css"; import "animate.css";
import "@fontsource/fira-sans"; import "@fontsource/fira-sans";
import '@fontsource-variable/noto-sans-tc'; import "@fontsource-variable/noto-sans-tc";
const { t } = useI18n(); const { t } = useI18n();
</script> </script>
<template> <template>

View File

@ -13,7 +13,6 @@ const availableLocales = computed(() => {
const toggleDropdown = () => { const toggleDropdown = () => {
dropdownOpen.value = !dropdownOpen.value; dropdownOpen.value = !dropdownOpen.value;
}; };
</script> </script>
<template> <template>
<!--Spent too much time trying to set a Navbar....--> <!--Spent too much time trying to set a Navbar....-->
@ -23,7 +22,9 @@ const toggleDropdown = () => {
<div class="text-3xl text-bold"> <div class="text-3xl text-bold">
<NuxtLink :to="localePath('home')" ref="title">BlindSpec</NuxtLink> <NuxtLink :to="localePath('home')" ref="title">BlindSpec</NuxtLink>
</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"
@ -37,54 +38,56 @@ const toggleDropdown = () => {
> >
</div> </div>
<div class="flex flex-row align-center justify-center text-center"> <div class="flex flex-row align-center justify-center text-center">
<div class="relative ml-0"> <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"
>
<span>{{ locale }}</span>
<svg
class="w-4 h-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
> >
<path <span>{{ locale }}</span>
stroke-linecap="round" <svg
stroke-linejoin="round" class="w-4 h-4"
stroke-width="2" fill="none"
d="M19 9l-7 7-7-7" stroke="currentColor"
/> viewBox="0 0 24 24"
</svg>
</button>
<Transition
enter-active-class="animate__animated animate__fadeInDown animate_fastest"
leave-active-class="animate__animated animate__fadeOutUp animate_fastest"
>
<div
v-if="dropdownOpen"
class="absolute top-full right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1"
>
<a
v-for="loc in availableLocales"
:key="loc.code"
:href="switchLocalePath(loc.code)"
v-on:click="dropdownOpen = false"
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 transition-all duration-100"
> >
{{ loc.name || loc.code }} <path
</a> stroke-linecap="round"
</div> stroke-linejoin="round"
</Transition> stroke-width="2"
</div> d="M19 9l-7 7-7-7"
<div class="mr-2 ml-0"> />
</svg>
</button>
<Transition
enter-active-class="animate__animated animate__fadeInDown animate_fastest"
leave-active-class="animate__animated animate__fadeOutUp animate_fastest"
>
<div
v-if="dropdownOpen"
class="absolute top-full right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1"
>
<a
v-for="loc in availableLocales"
:key="loc.code"
:href="switchLocalePath(loc.code)"
v-on:click="dropdownOpen = false"
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 transition-all duration-100"
>
{{ loc.name || loc.code }}
</a>
</div>
</Transition>
</div>
<div class="mr-2 ml-0">
<NuxtLink :to="localePath('/system/login')"> <NuxtLink :to="localePath('/system/login')">
<button class="text-white hover:text-[#C6C6C6] transition-all duration-150"> <button
class="text-white hover:text-[#C6C6C6] transition-all duration-150"
>
<i class="bi bi-person text-3xl"></i> <i class="bi bi-person text-3xl"></i>
</button> </button>
</NuxtLink> </NuxtLink>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<style scoped> <style scoped>

View File

@ -25,7 +25,7 @@ create table if not exists newsProviders (
logoUrl text not null, logoUrl text not null,
lean text not null lean text not null
) )
` `;
const createAdminPosts = await sql` const createAdminPosts = await sql`
create table if not exists adminPosts ( create table if not exists adminPosts (
@ -35,7 +35,7 @@ create table if not exists adminPosts (
created_at timestampz default current_timestamp, created_at timestampz default current_timestamp,
byUser text not null byUser text not null
) )
` `;
const adminUsers = await sql` const adminUsers = await sql`
create table if not exists adminUsers ( create table if not exists adminUsers (
uuid text primary key, uuid text primary key,
@ -44,7 +44,6 @@ create table if not exists adminUsers (
created_at timestampz default current_timestamp, created_at timestampz default current_timestamp,
lastlogged_at timestampz default current_timestamp, lastlogged_at timestampz default current_timestamp,
) )
` `;
console.log("Creation Complete"); console.log("Creation Complete");

View File

@ -1,5 +1,4 @@
<script setup lang="ts"> <script setup lang="ts"></script>
</script>
<template> <template>
<main> <main>
<slot /> <slot />

View File

@ -1,12 +1,12 @@
<script lang="ts" setup> <script lang="ts" setup>
definePageMeta({ definePageMeta({
layout: "admin" layout: "admin",
}) });
</script> </script>
<template> <template>
<div class="flex justify-center min-h-screen w-full"> <div class="flex justify-center min-h-screen w-full">
<input type="text"/> <input type="text" />
<input type="password" /> <input type="password" />
<button>登入</button> <button>登入</button>
</div> </div>
</template> </template>

View File

@ -1,3 +1 @@
<template> <template></template>
</template>

View File

@ -1,47 +1,62 @@
<script setup lang="ts"> <script setup lang="ts">
import { gsap } from 'gsap' import { gsap } from "gsap";
import { TextPlugin } from 'gsap/TextPlugin' import { TextPlugin } from "gsap/TextPlugin";
gsap.registerPlugin(TextPlugin) gsap.registerPlugin(TextPlugin);
const { t } = useI18n(); const { t } = useI18n();
const popMessage = ref(null); const popMessage = ref(null);
const messages = [t("home.moving.newsPlatform"), t("home.moving.miniWikipedia"), t("home.moving.newsComparePlatform"), "BlindSpec"]; const messages = [
t("home.moving.newsPlatform"),
t("home.moving.miniWikipedia"),
t("home.moving.newsComparePlatform"),
"BlindSpec",
];
onMounted(() => { onMounted(() => {
const tl = gsap.timeline({ repeat: -1 }) const tl = gsap.timeline({ repeat: -1 });
messages.forEach((message) => { messages.forEach((message) => {
tl.to(popMessage.value, { tl.to(popMessage.value, {
duration:0.5, duration: 0.5,
text:message, text: message,
ease: "none." ease: "none.",
}).to(popMessage.value, { }).to(popMessage.value, {
duration:2, duration: 2,
text:message, text: message,
ease: "none." ease: "none.",
}) });
for (let i = message.length; i >=0; i--) { for (let i = message.length; i >= 0; i--) {
tl.to(popMessage.value, { tl.to(popMessage.value, {
duration:0.06, duration: 0.06,
text:message.substring(0, i), text: message.substring(0, i),
ease: "none" ease: "none",
}) });
} }
}) });
}) });
</script> </script>
<template> <template>
<div> <div>
<div class="flex flex-col justify-center align-center text-center absolute w-full h-screen inset-x-0 inset-y-0"> <div
<span class="text-3xl"> class="flex flex-col justify-center align-center text-center absolute w-full h-screen inset-x-0 inset-y-0"
<span class="bg-gradient-to-b from-[#eee] to-[#333] bg-clip-text text-transparent">{{ t("home.nonMoving") }}</span> >
<span ref="popMessage" class="bg-gradient-to-r from-[#2a7b9b] then-[#8d57c7] to-[#ed4242] bg-clip-text text-transparent"></span></span> <span class="text-3xl">
<NuxtLink> <span
<button class="m-4 bg-[#8C9393] text-white p-3 rounded-full bg-gradient-to-l from-sky-500 to-purple-600 transition-all duration-100"> class="bg-gradient-to-b from-[#eee] to-[#333] bg-clip-text text-transparent"
<span>{{ t("home.startusing") }}</span> >{{ t("home.nonMoving") }}</span
</button> >
</NuxtLink> <span
ref="popMessage"
class="bg-gradient-to-r from-[#2a7b9b] then-[#8d57c7] to-[#ed4242] bg-clip-text text-transparent"
></span
></span>
<NuxtLink>
<button
class="m-4 bg-[#8C9393] text-white p-3 rounded-full bg-gradient-to-l from-sky-500 to-purple-600 transition-all duration-100"
>
<span>{{ t("home.startusing") }}</span>
</button>
</NuxtLink>
</div> </div>
<div class="h-screen"></div> <div class="h-screen"></div>
</div> </div>

View File

@ -33,12 +33,12 @@ const {
} = useFetch("/api/getData/fetchSidebarData", { } = useFetch("/api/getData/fetchSidebarData", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json" "Content-Type": "application/json",
}, },
body: { body: {
lang: locale, lang: locale,
} },
}) });
watchEffect(() => { watchEffect(() => {
loading.value = pending.value; loading.value = pending.value;
@ -90,25 +90,31 @@ import { GlobeAltIcon } from "@heroicons/vue/24/outline";
> >
</div> </div>
</div> </div>
<div class="flex flex-col gap-3 text-left justify-right align-right bg-[#AAACAA61] w-[28%] rounded-3xl p-3 mt-3 h-screen"> <div
class="flex flex-col gap-3 text-left justify-right align-right bg-[#AAACAA61] w-[28%] rounded-3xl p-3 mt-3 h-screen"
>
<h3 class="text-2xl mt-2s">其他媒體</h3> <h3 class="text-2xl mt-2s">其他媒體</h3>
<hr/> <hr />
<div v-for="data in fetchOtherData" :key="data.id" class="flex flex-col"> <div v-for="data in fetchOtherData" :key="data.id" class="flex flex-col">
<NuxtImg :src="data.image"></NuxtImg> <NuxtImg :src="data.image"></NuxtImg>
<div class="flex flex-row"> <div class="flex flex-row">
<h1 class="text-xl text-bold">{{ data.title }}</h1> <h1 class="text-xl text-bold">{{ data.title }}</h1>
<span class="text-ms ml-2 align-center justify-center text-center"> <span class="text-ms ml-2 align-center justify-center text-center">
( (
<span>{{ data.lean }}</span> <span>{{ data.lean }}</span>
- -
<span>文章分數: <span
<span>{{ data.score }}</span> >文章分數:
</span> ) <span>{{ data.score }}</span>
</span> </span>
)
</span>
</div> </div>
</div> </div>
<NuxtLink class="justify-center align-center text-center"> <NuxtLink class="justify-center align-center text-center">
<button class="bg-red-500 text-black p-2 rounded-full justify-center align-center"> <button
class="bg-red-500 text-black p-2 rounded-full justify-center align-center"
>
<span>查看更多</span> <span>查看更多</span>
</button> </button>
</NuxtLink> </NuxtLink>

View File

@ -1,3 +1 @@
<template> <template></template>
</template>

View File

@ -1,25 +1,33 @@
<template> <template>
<div class="w-full min-h-screen flex items-center justify-center text-center"> <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"> <div
<h1 class="text-2xl">Login / Register</h1> class="border border-white w-[40%] p-16 justify-center align-center text-center rounded-md backdrop-blur-sm bg-gray-900"
<h4 class="text-sm">via OAuth Providers</h4> >
<div class="m-4 flex flex-col gap-2"> <h1 class="text-2xl">Login / Register</h1>
<a href="/api/auth/google"> <h4 class="text-sm">via OAuth Providers</h4>
<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"> <div class="m-4 flex flex-col gap-2">
<i class="bi bi-google"></i>&nbsp;&nbsp;<span>Google</span> <a href="/api/auth/google">
</button> <button
</a> 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"
<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-google"></i>&nbsp;&nbsp;<span>Google</span>
<i class="bi bi-github"></i>&nbsp;&nbsp;<span>Github</span> </button>
</button> </a>
</a> <a href="/api/auth/github">
<a href="/api/auth/discord"> <button
<button class="gap-3 px-10 bg-gray-500 hover:bg-gray-700 p-2 rounded-md transition-all duration-150"> 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>&nbsp;&nbsp;<span>Discord</span> >
</button> <i class="bi bi-github"></i>&nbsp;&nbsp;<span>Github</span>
</a> </button>
</div> </a>
</div> <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>&nbsp;&nbsp;<span>Discord</span>
</button>
</a>
</div>
</div> </div>
</div>
</template> </template>

View File

@ -1,8 +1,5 @@
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {});
})
async function findUser(githubUser: any) { async function findUser(githubUser: any) {
console.log("Github User: " + githubUser); console.log("Github User: " + githubUser);
} }

View File

@ -1,16 +1,16 @@
import crypto from "node:crypto" import crypto from "node:crypto";
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
const baseUrl = event.node.req.headers.host const baseUrl = event.node.req.headers.host;
const protocol = process.env.NODE_ENV === "production" ? "https": "http" const protocol = process.env.NODE_ENV === "production" ? "https" : "http";
const clientId = process.env.NUXT_GITHUB_CLIENT_ID; const clientId = process.env.NUXT_GITHUB_CLIENT_ID;
const callbackUrl = `${protocol}://${baseUrl}/api/auth/github/callback`; const callbackUrl = `${protocol}://${baseUrl}/api/auth/github/callback`;
const state = crypto.randomBytes(16).toString("hex"); const state = crypto.randomBytes(16).toString("hex");
setCookie(event, 'oauth_state', state, { setCookie(event, "oauth_state", state, {
httpOnly: true, httpOnly: true,
secure: process.env.NODE_ENV === 'production', secure: process.env.NODE_ENV === "production",
maxAge: 60 * 10, maxAge: 60 * 10,
path: '/', path: "/",
}) });
const authorizationUrl = `https://github.com/login/oauth/authorize?client_id=${clientId}&redirect_uri=${encodeURIComponent(callbackUrl)}&scope=read:user,user:email&state=${state}` 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) await sendRedirect(event, authorizationUrl, 302);
}) });

View File

@ -2,12 +2,12 @@ export default defineEventHandler(async (event) => {
const body = await readBody(event); const body = await readBody(event);
return { return {
0: { 0: {
id: "1", id: "1",
image: "whatever", image: "whatever",
tags: [], tags: [],
title: "三立新聞", title: "三立新聞",
lean: "left", lean: "left",
score: "40" score: "40",
} },
}; };
}); });

View File

@ -9,15 +9,17 @@
body { body {
@apply bg-black m-0 p-0 min-h-screen text-white; @apply bg-black m-0 p-0 min-h-screen text-white;
} }
html,body { html,
font-family: 'Noto Sans TC Variable', "Fira Sans", sans-serif; body {
font-family: "Noto Sans TC Variable", "Fira Sans", sans-serif;
} }
} }
@font-face { @font-face {
font-family: 'Noto Sans TC Variable'; font-family: "Noto Sans TC Variable";
font-style: normal; font-style: normal;
font-display: auto; font-display: auto;
font-weight: 100 900; font-weight: 100 900;
src: url(@fontsource-variable/noto-sans-tc/files/noto-sans-tc-chinese-traditional-wght-normal.woff2) format('woff2-variations'); src: url(@fontsource-variable/noto-sans-tc/files/noto-sans-tc-chinese-traditional-wght-normal.woff2)
format("woff2-variations");
} }