From 4405b4b816f5dd9a71cd6accc2d2ff7e9a0f85d4 Mon Sep 17 00:00:00 2001 From: hanbings Date: Fri, 6 Oct 2023 03:04:28 +0800 Subject: [PATCH 1/4] refact: refactor repository api. --- cli/src/main.rs | 35 ++++++++++++++++++++++++++++++----- src/pages/api/repository.ts | 19 ++++++++++++++++--- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index 881d7a0..1c0ee76 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,6 +1,8 @@ use std::env; use colored::Colorize; use std::error::Error; +use std::io::{BufRead, BufReader, ErrorKind}; +use std::process::{Command, Stdio}; use serde::{Deserialize, Serialize}; static mut DATA: Vec = Vec::new(); @@ -16,8 +18,8 @@ async fn main() { format!(" {} {} {}", "help", "".yellow(), "Display this help".cyan()), format!(" {} {} {}", "search", "".yellow(), "Search for a keyword".cyan()), format!(" {} {} {}", "run", " ".yellow(), "Run target project (using Docker)".cyan()), - format!(" {} {} {}", "run", "".yellow(), "Run target file".cyan()), - format!(" {} {} {}", "run", "".yellow(), "Get ebpf package by id".cyan()), + format!(" {} {} {}", "run", "".yellow(), "Run target file (NOT YET IMPL)".cyan()), + format!(" {} {} {}", "run", "".yellow(), "Get ebpf package by id (using Docker)".cyan()), ]; if args.len() < 2 { @@ -81,6 +83,17 @@ async fn repository(id: &str) -> Result> { Ok(res) } +async fn repository_with_org_and_project(organization: &str, project: &str) -> Result> { + let res = + reqwest::Client::builder() + .build() + .get(&format!("https://ebpfs.vercel.app/repository?organization={}project={}", organization, project)) + .send().await? + .text().await?; + + Ok(res) +} + // 裁剪搜索结果 fn trim(text: &str) -> String { if let Some(summary_index) = text.find("summary:") { @@ -92,10 +105,22 @@ fn trim(text: &str) -> String { } // 执行 Docker 快速启动 ebpf 包 -fn exec() {} - // 从 repo 获取 docker image -fn get_image() {} +fn exec(loc: &str) { + let stdout = Command::new("docker") + .args(&["run", "-it", loc]) + .stdout(Stdio::piped()) + .spawn()? + .stdout + .ok_or_else(|| Error::new(ErrorKind::Other,"Could not capture standard output."))?; + + let reader = BufReader::new(stdout); + + reader + .lines() + .filter_map(|line| line.ok()) + .for_each(|line| println!("{}", line)); +} #[derive(Serialize, Deserialize, Debug)] struct Message { diff --git a/src/pages/api/repository.ts b/src/pages/api/repository.ts index 8d8442f..986c8a1 100644 --- a/src/pages/api/repository.ts +++ b/src/pages/api/repository.ts @@ -8,7 +8,7 @@ import SearchService from "@/services/search"; export default async function handler(req: NextApiRequest, res: NextApiResponse<{}>) { if (req.method === 'GET') { - const {id} = req.query; + const {id, organization, project} = req.query; const repositories = new DatabaseService(); // 如果没有携带参数则按获取创建时间最新的 10 个仓库 @@ -41,8 +41,21 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< return; } - const repo = await repositories.readRepository(id as string) as Repository[]; - res.status(200).json(new Message(200, 'OK', {repository: repo})); + if (id) { + const repo = await repositories.readRepository(id as string) as Repository[]; + res.status(200).json(new Message(200, 'OK', {repository: repo})); + + return; + } + + if (organization && project) { + const repos = await repositories.readRepositoryByOrganizationAndProject(organization as string, project as string) as Repository[]; + res.status(200).json(new Message(200, 'OK', {repository: repos})); + + return; + } + + res.status(404).json(new Message(404, 'not found', null)); } else if (req.method === 'POST') { // 根据 Token 获取账号 ID const header = req.headers['authorization']; From 2138a31b36e0d5640b6ab37ebf43085c773499d9 Mon Sep 17 00:00:00 2001 From: hanbings Date: Fri, 6 Oct 2023 04:09:31 +0800 Subject: [PATCH 2/4] fix: wrong order of calls. --- src/pages/api/repository.ts | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/pages/api/repository.ts b/src/pages/api/repository.ts index 986c8a1..1aecb77 100644 --- a/src/pages/api/repository.ts +++ b/src/pages/api/repository.ts @@ -11,6 +11,20 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< const {id, organization, project} = req.query; const repositories = new DatabaseService(); + if (id) { + const repo = await repositories.readRepository(id as string) as Repository[]; + res.status(200).json(new Message(200, 'OK', {repository: repo})); + + return; + } + + if (organization && project) { + const repos = await repositories.readRepositoryByOrganizationAndProject(organization as string, project as string) as Repository[]; + res.status(200).json(new Message(200, 'OK', {repository: repos})); + + return; + } + // 如果没有携带参数则按获取创建时间最新的 10 个仓库 if (!id || id === '') { const repository = await repositories.readRepositoryByLimit(10) as Repository[]; @@ -41,20 +55,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< return; } - if (id) { - const repo = await repositories.readRepository(id as string) as Repository[]; - res.status(200).json(new Message(200, 'OK', {repository: repo})); - - return; - } - - if (organization && project) { - const repos = await repositories.readRepositoryByOrganizationAndProject(organization as string, project as string) as Repository[]; - res.status(200).json(new Message(200, 'OK', {repository: repos})); - - return; - } - res.status(404).json(new Message(404, 'not found', null)); } else if (req.method === 'POST') { // 根据 Token 获取账号 ID From d46fcd3b9ca5ae8023cf6f33e1b0be6566cd5da2 Mon Sep 17 00:00:00 2001 From: hanbings Date: Sat, 7 Oct 2023 13:37:14 +0800 Subject: [PATCH 3/4] fix: fix cli build. --- cli/src/main.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index 1c0ee76..0057cdd 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,7 +1,7 @@ use std::env; use colored::Colorize; use std::error::Error; -use std::io::{BufRead, BufReader, ErrorKind}; +use std::io::{BufRead, BufReader}; use std::process::{Command, Stdio}; use serde::{Deserialize, Serialize}; @@ -24,10 +24,14 @@ async fn main() { if args.len() < 2 { println!("{}", help.join("\n")); + + return; } if args[1].eq("help") { println!("{}", help.join("\n")); + + return; } // 匹配指令 @@ -86,7 +90,7 @@ async fn repository(id: &str) -> Result> { async fn repository_with_org_and_project(organization: &str, project: &str) -> Result> { let res = reqwest::Client::builder() - .build() + .build().unwrap() .get(&format!("https://ebpfs.vercel.app/repository?organization={}project={}", organization, project)) .send().await? .text().await?; @@ -106,13 +110,13 @@ fn trim(text: &str) -> String { // 执行 Docker 快速启动 ebpf 包 // 从 repo 获取 docker image -fn exec(loc: &str) { +fn exec(loc: &str) -> Result<(), Box> { let stdout = Command::new("docker") .args(&["run", "-it", loc]) .stdout(Stdio::piped()) .spawn()? .stdout - .ok_or_else(|| Error::new(ErrorKind::Other,"Could not capture standard output."))?; + .expect("Could not capture standard output."); let reader = BufReader::new(stdout); @@ -120,6 +124,8 @@ fn exec(loc: &str) { .lines() .filter_map(|line| line.ok()) .for_each(|line| println!("{}", line)); + + Ok(()) } #[derive(Serialize, Deserialize, Debug)] From ae65ebf8dfac6479071994afab3d27ac4fd6d760 Mon Sep 17 00:00:00 2001 From: hanbings Date: Tue, 19 Mar 2024 15:41:07 +0800 Subject: [PATCH 4/4] refactor: refine the type (1/3), remove unnecessary --- cli/src/main.rs | 41 +----- src/common/message.ts | 11 +- src/common/token.ts | 22 +--- src/data/account.ts | 4 +- src/data/repository.ts | 2 +- src/data/statistic.ts | 2 +- src/pages/_app.tsx | 44 +++---- src/pages/account.tsx | 56 ++++---- src/pages/api/account.ts | 64 +++++---- src/pages/api/data.ts | 13 +- src/pages/api/login.ts | 203 ++++++++++++++++------------- src/pages/api/oauth.ts | 10 +- src/pages/api/repository.ts | 105 +++++++-------- src/pages/api/search.ts | 37 +++--- src/pages/api/statistic.ts | 15 ++- src/pages/api/verify.ts | 64 ++++----- src/pages/components/Avatar.tsx | 6 +- src/pages/components/Button.tsx | 18 +-- src/pages/components/Card.tsx | 9 +- src/pages/components/Input.tsx | 26 ++-- src/pages/components/Loading.tsx | 2 +- src/pages/components/Navbar.tsx | 14 +- src/pages/components/Row.tsx | 16 +-- src/pages/components/Searchbar.tsx | 20 +-- src/pages/index.tsx | 46 +++---- src/pages/login.tsx | 18 +-- src/pages/repository.tsx | 38 +++--- src/pages/search.tsx | 38 +++--- src/pages/upload.tsx | 40 +++--- src/pages/verify.tsx | 40 +++--- src/services/cache.ts | 6 +- src/services/database.ts | 69 +++++----- src/services/mail.ts | 34 ++--- src/services/network.ts | 6 +- src/services/search.ts | 28 ++-- 35 files changed, 576 insertions(+), 591 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index 0057cdd..881d7a0 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,8 +1,6 @@ use std::env; use colored::Colorize; use std::error::Error; -use std::io::{BufRead, BufReader}; -use std::process::{Command, Stdio}; use serde::{Deserialize, Serialize}; static mut DATA: Vec = Vec::new(); @@ -18,20 +16,16 @@ async fn main() { format!(" {} {} {}", "help", "".yellow(), "Display this help".cyan()), format!(" {} {} {}", "search", "".yellow(), "Search for a keyword".cyan()), format!(" {} {} {}", "run", " ".yellow(), "Run target project (using Docker)".cyan()), - format!(" {} {} {}", "run", "".yellow(), "Run target file (NOT YET IMPL)".cyan()), - format!(" {} {} {}", "run", "".yellow(), "Get ebpf package by id (using Docker)".cyan()), + format!(" {} {} {}", "run", "".yellow(), "Run target file".cyan()), + format!(" {} {} {}", "run", "".yellow(), "Get ebpf package by id".cyan()), ]; if args.len() < 2 { println!("{}", help.join("\n")); - - return; } if args[1].eq("help") { println!("{}", help.join("\n")); - - return; } // 匹配指令 @@ -87,17 +81,6 @@ async fn repository(id: &str) -> Result> { Ok(res) } -async fn repository_with_org_and_project(organization: &str, project: &str) -> Result> { - let res = - reqwest::Client::builder() - .build().unwrap() - .get(&format!("https://ebpfs.vercel.app/repository?organization={}project={}", organization, project)) - .send().await? - .text().await?; - - Ok(res) -} - // 裁剪搜索结果 fn trim(text: &str) -> String { if let Some(summary_index) = text.find("summary:") { @@ -109,24 +92,10 @@ fn trim(text: &str) -> String { } // 执行 Docker 快速启动 ebpf 包 +fn exec() {} + // 从 repo 获取 docker image -fn exec(loc: &str) -> Result<(), Box> { - let stdout = Command::new("docker") - .args(&["run", "-it", loc]) - .stdout(Stdio::piped()) - .spawn()? - .stdout - .expect("Could not capture standard output."); - - let reader = BufReader::new(stdout); - - reader - .lines() - .filter_map(|line| line.ok()) - .for_each(|line| println!("{}", line)); - - Ok(()) -} +fn get_image() {} #[derive(Serialize, Deserialize, Debug)] struct Message { diff --git a/src/common/message.ts b/src/common/message.ts index 89b3ecb..64e8cc5 100644 --- a/src/common/message.ts +++ b/src/common/message.ts @@ -1,8 +1,5 @@ -export default class Message { - constructor( - public status: number, - public message: string, - public data: T - ) { - } +export default interface Message { + status: number, + message: string, + data: T, } \ No newline at end of file diff --git a/src/common/token.ts b/src/common/token.ts index 6171f7f..8be284d 100644 --- a/src/common/token.ts +++ b/src/common/token.ts @@ -1,21 +1,13 @@ -export class Token { - constructor( - // TOKEN - public token: string, - // 所属账号 - public belong: string, - // TOKEN 类型 - public type: TokenType, - // 创建时间 - public created: number, - // 失效时间 - public expire: number - ) { - } +export interface Token { + token: string, + belong: string, + type: TokenType, + created: number, + expire: number, } export enum TokenType { OAUTH_TOKEN = "oauth_token", ACCOUNT_API_KEY = "account_api_key", - EMAIL_VERIFY_CODE = "email_verify_code" + EMAIL_VERIFY_CODE = "email_verify_code", } \ No newline at end of file diff --git a/src/data/account.ts b/src/data/account.ts index 4bc256a..013665d 100644 --- a/src/data/account.ts +++ b/src/data/account.ts @@ -1,5 +1,5 @@ -import {Generated} from "kysely"; -import crypto from "crypto"; +import {Generated} from "kysely" +import crypto from "crypto" export class AccountTable { constructor( diff --git a/src/data/repository.ts b/src/data/repository.ts index a5fc587..fed6158 100644 --- a/src/data/repository.ts +++ b/src/data/repository.ts @@ -1,4 +1,4 @@ -import {Generated} from "kysely"; +import {Generated} from "kysely" export class RepositoryTable { constructor( diff --git a/src/data/statistic.ts b/src/data/statistic.ts index 33caf47..97ba5b7 100644 --- a/src/data/statistic.ts +++ b/src/data/statistic.ts @@ -1,4 +1,4 @@ -import {Generated} from "kysely"; +import {Generated} from "kysely" export class StatisticTable { constructor( diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 57f5323..688892c 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,21 +1,21 @@ import '@/styles/globals.css' -import App from "next/app"; -import {Token} from "@/common/token"; -import {Account} from "@/common/account"; -import {withRouter} from "next/router"; +import App from "next/app" +import {Token} from "@/common/token" +import {Account} from "@/common/account" +import {withRouter} from "next/router" export class State { - public static token: Token | null = null; - public static account: Account | null = null; + public static token: Token | null = null + public static account: Account | null = null // 从 localStorage 中读取 token 和 account public static load() { if (typeof window !== "undefined") { - const token = localStorage.getItem("token"); - const account = localStorage.getItem("account"); + const token = localStorage.getItem("token") + const account = localStorage.getItem("account") if (token !== null && account !== null) { - State.token = JSON.parse(token); - State.account = JSON.parse(account); + State.token = JSON.parse(token) + State.account = JSON.parse(account) } } } @@ -23,22 +23,22 @@ export class State { // 清理 localStorage 中的 token 和 account public static clear() { if (typeof window !== "undefined") { - localStorage.removeItem("token"); - localStorage.removeItem("account"); + localStorage.removeItem("token") + localStorage.removeItem("account") } - State.token = null; - State.account = null; + State.token = null + State.account = null } // 写入 localStorage public static save(token: Token, account: Account) { - State.token = token; - State.account = account; + State.token = token + State.account = account if (typeof window !== "undefined") { - localStorage.setItem("token", JSON.stringify(State.token)); - localStorage.setItem("account", JSON.stringify(State.account)); + localStorage.setItem("token", JSON.stringify(State.token)) + localStorage.setItem("account", JSON.stringify(State.account)) } } @@ -52,10 +52,10 @@ export class State { class MyApp extends App { render() { - const {Component, pageProps, router} = this.props; - const WithRouterComponent = withRouter(Component); - return ; + const {Component, pageProps, router} = this.props + const WithRouterComponent = withRouter(Component) + return } } -export default MyApp; \ No newline at end of file +export default MyApp \ No newline at end of file diff --git a/src/pages/account.tsx b/src/pages/account.tsx index 7396e2a..67997a9 100644 --- a/src/pages/account.tsx +++ b/src/pages/account.tsx @@ -1,21 +1,21 @@ import {Inter} from 'next/font/google' -import Navbar from "@/pages/components/Navbar"; -import Button from "@/pages/components/Button"; -import {State} from "@/pages/_app"; -import {Account} from "@/common/account"; -import {Repository} from "@/common/repository"; -import {useEffect, useState} from "react"; -import Message from "@/common/message"; -import {Token} from "@/common/token"; -import {useRouter} from "next/router"; -import Input from "@/pages/components/Input"; +import Navbar from "@/pages/components/Navbar" +import Button from "@/pages/components/Button" +import {State} from "@/pages/_app" +import {Account} from "@/common/account" +import {Repository} from "@/common/repository" +import {useEffect, useState} from "react" +import Message from "@/common/message" +import {Token} from "@/common/token" +import {useRouter} from "next/router" +import Input from "@/pages/components/Input" const inter = Inter({subsets: ['latin']}) interface Response { - id: string; - account: Account; - repositories: Repository[]; + id: string + account: Account + repositories: Repository[] } export default function AccountPage() { @@ -23,33 +23,33 @@ export default function AccountPage() { State.load() }()); - const router = useRouter(); - const token = State.token as Token; + const router = useRouter() + const token = State.token as Token // 不要在任意 return 后使用 useState,因为这会导致每次渲染都会重置 state - const [result, setResult] = useState(); + const [result, setResult] = useState() useEffect(() => { // 获取账号信息的逻辑 // 这里的 Token 在 isLogin 校验中判定为非空 那么这里一定不为空 fetch('/api/account', {headers: {'Authorization': token.token}}) .then(result => result.json() as Promise>) - .then(data => setResult(data.data)); + .then(data => setResult(data.data)) // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, []) const copy = () => { - const input = document.createElement('input'); - input.value = token.token; - document.body.appendChild(input); - input.select(); - document.execCommand('copy'); - document.body.removeChild(input); + const input = document.createElement('input') + input.value = token.token + document.body.appendChild(input) + input.select() + document.execCommand('copy') + document.body.removeChild(input) }; - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); + const [email, setEmail] = useState('') + const [password, setPassword] = useState('') const login = () => { fetch('/api/login', @@ -65,11 +65,11 @@ export default function AccountPage() { }) .then(result => result.json()) .then(() => router.push("/account") - .then(ignore => 0)); + .then(ignore => 0)) } const update = (org: string, project: string) => { - router.push(`/upload?update_organization=${org}&update_project=${project}`).then(ignore => 0); + router.push(`/upload?update_organization=${org}&update_project=${project}`).then(ignore => 0) } function renderLogin() { diff --git a/src/pages/api/account.ts b/src/pages/api/account.ts index 5e80494..29777c2 100644 --- a/src/pages/api/account.ts +++ b/src/pages/api/account.ts @@ -1,38 +1,46 @@ import type {NextApiRequest, NextApiResponse} from 'next' -import Message from "@/common/message"; -import CacheService from "@/services/cache"; -import {Token} from "@/common/token"; -import DatabaseService from "@/services/database"; -import {Repository} from "@/common/repository"; -import {Account} from "@/common/account"; +import CacheService from "@/services/cache" +import {Token} from "@/common/token" +import DatabaseService from "@/services/database" +import {Repository} from "@/common/repository" +import {Account} from "@/common/account" +import Message from "@/common/message" -export default async function handler(req: NextApiRequest, res: NextApiResponse<{}>) { - if (req.method === 'GET') { - // 查询 Token - const header = req.headers['authorization']; +export default async function handler(req: NextApiRequest, res: NextApiResponse>) { + if (req.method !== 'GET') { + res.status(400).json({status: 400, message: 'request method not match.', data: null}) + return + } - if (header === undefined || header === null) { - res.status(400).json(new Message(400, 'token is invalid.', null)); - return; - } + // 查询 Token + const header = req.headers['authorization']; + + if (header === undefined || header === null) { + res.status(400).json({status: 400, message: 'token is invalid.', data: null}) + return + } - const tokens = new CacheService(); - const token = await tokens.get(header as string) as Token; + const tokens = new CacheService() + const token = await tokens.get(header as string) as Token - // 查询账号信息 - const accounts = new DatabaseService(); - const id = token.belong; - const account = await accounts.readAccountById(id); + // 查询账号信息 + const accounts = new DatabaseService() + const id = token.belong + const account = await accounts.readAccountById(id) - // 查询仓库列表 - const repositories = new DatabaseService(); - const repos = await repositories.readRepositoryByAccount(id); + // 查询仓库列表 + const repositories = new DatabaseService() + const repos = await repositories.readRepositoryByAccount(id) - res.status(200).json(new Message(200, 'success.', - { + res.status(200).json( + { + status: 200, + message: 'success.', + data: { id: id, account: account.length !== 0 ? account[0] : {}, - repositories: repos - })); - } else res.status(400).json(new Message(400, 'request method not match.', null)); + repositories: repos, + } + } + ); } \ No newline at end of file diff --git a/src/pages/api/data.ts b/src/pages/api/data.ts index 3a0f831..3db3d09 100644 --- a/src/pages/api/data.ts +++ b/src/pages/api/data.ts @@ -1,8 +1,11 @@ -import {NextApiRequest, NextApiResponse} from "next"; +import {NextApiRequest, NextApiResponse} from "next" import Message from "@/common/message"; -export default async function handler(req: NextApiRequest, res: NextApiResponse<{}>) { - if (req.method === 'GET') { - res.status(200).json(new Message(200, 'OK', null)); - } else res.status(400).json(new Message(400, 'request method not match.', null)); +export default async function handler(req: NextApiRequest, res: NextApiResponse>) { + if (req.method !== 'GET') { + res.status(400).json({status: 400, message: 'request method not match.', data: null}) + return + } + + res.status(200).json({status: 200, message: 'OK', data: null}) } \ No newline at end of file diff --git a/src/pages/api/login.ts b/src/pages/api/login.ts index a746695..6f66279 100644 --- a/src/pages/api/login.ts +++ b/src/pages/api/login.ts @@ -1,51 +1,59 @@ import type {NextApiRequest, NextApiResponse} from 'next' +import DatabaseService from "@/services/database" +import CacheService from "@/services/cache" +import {Token, TokenType} from "@/common/token" +import {Account, AccountType} from "@/common/account" +import {AccountTable} from "@/data/account" +import EmailService from "@/services/mail" import Message from "@/common/message"; -import DatabaseService from "@/services/database"; -import CacheService from "@/services/cache"; -import {Token, TokenType} from "@/common/token"; -import {Account, AccountType} from "@/common/account"; -import {AccountTable} from "@/data/account"; -import EmailService from "@/services/mail"; - -export default async function handler(req: NextApiRequest, res: NextApiResponse<{}>) { - if (req.method === 'GET') { + +export default async function handler(req: NextApiRequest, res: NextApiResponse>) { + if (req.method as string !== 'GET' || req.method !== 'POST') { + res.status(400).json({status: 400, message: 'request method not match.', data: null}) + return + } + + if (req.method as string === 'GET') { const {code, state} = req.query; if (code === null || code === '') { - res.status(400).json(new Message(400, 'code is invalid.', null)); + res.status(400).json({status: 400, message: 'code is invalid.', data: null}); return; } if (state === null || state === '') { - res.status(400).json(new Message(400, 'state is invalid.', null)); + res.status(400).json({status: 400, message: 'state is invalid.', data: null}); return; } - const githubClientID = process.env.GITHUB_OAUTH_CLIENT_ID; - const githubClientSecret = process.env.GITHUB_OAUTH_CLIENT_SECRET; - const tokens = new CacheService(); - const accounts = new DatabaseService(); + const githubClientID = process.env.GITHUB_OAUTH_CLIENT_ID + const githubClientSecret = process.env.GITHUB_OAUTH_CLIENT_SECRET + const tokens = new CacheService() + const accounts = new DatabaseService() // 请求 GitHub API 获取用户信息 - const tokenRes = await fetch(`https://github.com/login/oauth/access_token?client_id=${githubClientID}&client_secret=${githubClientSecret}&code=${code}&state=${state}`, { - method: 'POST', - headers: { - Accept: 'application/json' + const tokenRes = await fetch( + `https://github.com/login/oauth/access_token?client_id=${githubClientID}&client_secret=${githubClientSecret}&code=${code}&state=${state}`, + { + method: 'POST', + headers: { + Accept: 'application/json' + } } - }); + ) - const tokenResJson = await tokenRes.json(); - const {access_token} = tokenResJson; + const tokenResJson = await tokenRes.json() + const {access_token} = tokenResJson const userRes = await fetch('https://api.github.com/user', { headers: { Authorization: `Bearer ${access_token}`, Accept: 'application/json' } - }); + }) - const userResJson = await userRes.json(); - const {avatar_url, id, name} = userResJson; + const userResJson = await userRes.json() + const {avatar_url, id, name} = userResJson // 检查如果三个参数中有一个不为空则正常执行 if ( @@ -53,15 +61,15 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< id === null || id === '' || name === null || name === '' ) { - res.status(400).json(new Message(400, 'invalid response.', null)); - return; + res.status(400).json({status: 400, message: 'invalid response.', data: null}) + return } // 检查表 - await accounts.autoMigrate(); + await accounts.autoMigrate() // 检查用户是否已经记录过 - const accountInDB = await accounts.readAccount(id, AccountType.GITHUB); + const accountInDB = await accounts.readAccount(id, AccountType.GITHUB) // 如果数据库中没有则创建 const account = new Account( @@ -73,45 +81,55 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< null, AccountType.GITHUB, new Date().getTime() - ); + ) if (!account.openid) { - res.status(400).json(new Message(400, 'invalid response.', null)); - return; + res.status(400).json({status: 400, message: 'invalid response.', data: null}); + return } // FIXME: 这里逻辑有点奇怪 为什么要提前创建 Account 呢? 目前还需亚多判断一个 openid 是否存在 accountInDB.length === 0 ? await accounts.createAccount(account) - : await accounts.updateAccount(account.openid, account); + : await accounts.updateAccount(account.openid, account) // 无论如何都返回用户信息和有效 Token - const time = new Date().getTime(); - const expire = new Date(time + 60 * 60 * 1000).getTime(); - - const token = new Token( - crypto.randomUUID(), - accountInDB.length === 0 ? account.id : accountInDB[0].id, - TokenType.OAUTH_TOKEN, - time, - expire - ); + const time = new Date().getTime() + const expire = new Date(time + 60 * 60 * 1000).getTime() + + const token: Token = { + token: crypto.randomUUID(), + belong: accountInDB.length === 0 ? account.id : accountInDB[0].id, + type: TokenType.OAUTH_TOKEN, + created: time, + expire: expire + } - await tokens.set(token.token, token); + await tokens.set(token.token, token) // FIXME: 特别是这个 account 账号字段 三元太别扭了 - res.status(200).json(new Message(200, 'success', { - account: accountInDB.length === 0 ? account : accountInDB[0], - token: token, - origin: userResJson - })); - } else if (req.method === 'POST') { - const header = req.headers['authorization']; - const {email, password, code} = req.body; - - const tokens = new CacheService(); - const token = header ? await tokens.get(header as string) as Token : null; + res.status(200).json( + { + status: 200, + message: 'success', + data: { + account: accountInDB.length === 0 ? account : accountInDB[0], + token: token, + origin: userResJson + } + } + ) + + return + } + + if (req.method === 'POST') { + const header = req.headers['authorization'] + const {email, password, code} = req.body + + const tokens = new CacheService() + const token = header ? await tokens.get(header as string) as Token : null // 使用不同参数进行不同的操作 // email password 为登录 @@ -121,74 +139,81 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< if (email && password) { if (token) { // 修改密码 - const accounts = new DatabaseService(); + const accounts = new DatabaseService() // 检查表 await accounts.autoMigrate(); - const account = await accounts.readAccount(token.belong, AccountType.EMAIL); + const account = await accounts.readAccount(token.belong, AccountType.EMAIL) if (account.length === 0) { - res.status(400).json(new Message(400, 'account not found.', null)); - return; + res.status(400).json({status: 400, message: 'account not found.', data: null}) + return } // 修改密码 - account[0].password = AccountTable.getPassword(password as string); + account[0].password = AccountTable.getPassword(password as string) - await accounts.updateAccount(account[0].id, account[0]); - res.status(200).json(new Message(200, 'success', null)); + await accounts.updateAccount(account[0].id, account[0]) + res.status(200).json({status: 200, message: 'success', data: null}) return; } // 普通登录 // 检查用户是否存在 - const accounts = new DatabaseService(); + const accounts = new DatabaseService() // 检查表 await accounts.autoMigrate(); - const account = await accounts.readAccount(email as string, AccountType.EMAIL); + const account = await accounts.readAccount(email as string, AccountType.EMAIL) // 账号不存在 if (account.length === 0) { // 发送注册邮件 - const sender = new EmailService(); - const verifyCode = Math.floor(Math.random() * 1000000).toString().padStart(6, '0'); + const sender = new EmailService() + const verifyCode = Math.floor(Math.random() * 1000000).toString().padStart(6, '0') // 发送邮件 - await sender.send(email, 'Verify your email address', `Your verification code is ${verifyCode}.`); + await sender.send(email, 'Verify your email address', `Your verification code is ${verifyCode}.`) // 存入 Token const tokens = new CacheService(); - await tokens.set(verifyCode, new Token( + await tokens.set( verifyCode, - email, - TokenType.EMAIL_VERIFY_CODE, - Date.now(), - Date.now() + 1000 * 60 * 10) - ); + { + token: verifyCode, + belong: email, + type: TokenType.EMAIL_VERIFY_CODE, + created: Date.now(), + expire: Date.now() + 1000 * 60 * 10 + } + ) // 跳转到验证页面 - res.status(200).json(new Message(302, 'redirect to verify page.', `/verify?email=${email}&password=${password}`)); - return; + res.status(200).json({ + status: 302, + message: 'redirect to verify page.', + data: `/verify?email=${email}&password=${password}` + }) + return } // 检查密码是否正确 if (!AccountTable.checkPassword(account[0].password as string, password as string)) { - res.status(400).json(new Message(400, 'password is incorrect.', null)); - return; + res.status(400).json({status: 400, message: 'password is incorrect.', data: null}) + return } // 创建 Token - const t = new Token( - crypto.randomUUID(), - account[0].id, - TokenType.ACCOUNT_API_KEY, - new Date().getTime(), - new Date(new Date().getTime() + 60 * 60 * 1000).getTime() - ); - - await tokens.set(t.token, t); - res.status(200).json(new Message(200, 'success', {account: account[0], token: t})); + const t: Token = { + token: crypto.randomUUID(), + belong: account[0].id, + type: TokenType.ACCOUNT_API_KEY, + created: new Date().getTime(), + expire: new Date(new Date().getTime() + 60 * 60 * 1000).getTime() + } + + await tokens.set(t.token, t) + res.status(200).json({status: 200, message: 'success', data: {account: account[0], token: t}}) + + return } - } else { - res.status(400).json(new Message(400, 'request method not match.', null)); } } \ No newline at end of file diff --git a/src/pages/api/oauth.ts b/src/pages/api/oauth.ts index f6363f0..3b929cc 100644 --- a/src/pages/api/oauth.ts +++ b/src/pages/api/oauth.ts @@ -1,9 +1,9 @@ -import {NextApiRequest, NextApiResponse} from "next"; +import {NextApiRequest, NextApiResponse} from "next" export default async function handler(req: NextApiRequest, res: NextApiResponse<{}>) { - const githubClientID = process.env.GITHUB_OAUTH_CLIENT_ID; - const state = crypto.randomUUID(); - const baseUrl = process.env.BASE_URL; + const githubClientID = process.env.GITHUB_OAUTH_CLIENT_ID + const state = crypto.randomUUID() + const baseUrl = process.env.BASE_URL - res.redirect(`https://github.com/login/oauth/authorize?client_id=${githubClientID}&state=${state}&redirect_uri=${baseUrl}/login`); + res.redirect(`https://github.com/login/oauth/authorize?client_id=${githubClientID}&state=${state}&redirect_uri=${baseUrl}/login`) } \ No newline at end of file diff --git a/src/pages/api/repository.ts b/src/pages/api/repository.ts index 1aecb77..6970f1f 100644 --- a/src/pages/api/repository.ts +++ b/src/pages/api/repository.ts @@ -1,78 +1,65 @@ import type {NextApiRequest, NextApiResponse} from 'next' -import Message from "@/common/message"; -import CacheService from "@/services/cache"; -import {Token} from "@/common/token"; -import {Repository} from "@/common/repository"; -import DatabaseService from "@/services/database"; -import SearchService from "@/services/search"; - -export default async function handler(req: NextApiRequest, res: NextApiResponse<{}>) { +import CacheService from "@/services/cache" +import {Token} from "@/common/token" +import {Repository} from "@/common/repository" +import DatabaseService from "@/services/database" +import SearchService from "@/services/search" +import Message from "@/common/message" + +export default async function handler(req: NextApiRequest, res: NextApiResponse>) { if (req.method === 'GET') { - const {id, organization, project} = req.query; - const repositories = new DatabaseService(); - - if (id) { - const repo = await repositories.readRepository(id as string) as Repository[]; - res.status(200).json(new Message(200, 'OK', {repository: repo})); - - return; - } - - if (organization && project) { - const repos = await repositories.readRepositoryByOrganizationAndProject(organization as string, project as string) as Repository[]; - res.status(200).json(new Message(200, 'OK', {repository: repos})); - - return; - } + const {id} = req.query + const repositories = new DatabaseService() // 如果没有携带参数则按获取创建时间最新的 10 个仓库 if (!id || id === '') { - const repository = await repositories.readRepositoryByLimit(10) as Repository[]; + const repository = await repositories.readRepositoryByLimit(10) as Repository[] // 获取 readme for (const item of repository) { - let content = await fetch(item.readme).then(async (response) => response.text()); + let content = await fetch(item.readme).then(async (response) => response.text()) content = content.length > 500 ? content.substring(0, 500).replace(/\n/g, "") : content.replace(/\n/g, "") - item.readme = content; + item.readme = content // 将 tags 和 author 从字符串转换为数组 // @ts-ignore - item.tags = item.tags.split(','); + item.tags = item.tags.split(',') // @ts-ignore - item.author = item.author.split(','); + item.author = item.author.split(',') } res.status(200).json( - new Message( - 200, - 'OK', - {repository: repository} - ) + { + status: 200, + message: 'OK', + data: {repository: repository} + } ); return; } - res.status(404).json(new Message(404, 'not found', null)); + const repo = await repositories.readRepository(id as string) as Repository[] + res.status(200).json({status: 200, message: 'OK', data: {repository: repo}}) } else if (req.method === 'POST') { // 根据 Token 获取账号 ID - const header = req.headers['authorization']; + const header = req.headers['authorization'] if (header === undefined || header === null) { - res.status(400).json(new Message(400, 'token is invalid.', null)); - return; + res.status(400).json({status: 400, message: 'token is invalid.', data: null}) + return } - const tokens = new CacheService(); - const repositories = new DatabaseService(); + const tokens = new CacheService() + const repositories = new DatabaseService() - const token = await tokens.get(header as string) as Token; + const token = await tokens.get(header as string) as Token if (token === undefined || token === null) { - res.status(400).json(new Message(400, 'token is invalid.', null)); - return; + res.status(400).json({status: 400, message: 'token is invalid.', data: null}) + return } // 解构 form-data @@ -80,27 +67,27 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< update, organization, project, version, readme, type, repository, entry, author, tags - }: Repository = req.body; + }: Repository = req.body // 初始化搜索服务 - const search = new SearchService(); + const search = new SearchService() // 获取 readme - const content = await fetch(readme).then(async (response) => response.text()); + const content = await fetch(readme).then(async (response) => response.text()) // 检查是否已经存在 - const repos = await repositories.readRepositoryByOrganizationAndProject(organization, project) as Repository[]; + const repos = await repositories.readRepositoryByOrganizationAndProject(organization, project) as Repository[] if (repos && repos.length !== 0) { - let match = repos.filter((item) => item.account === token.belong); + let match = repos.filter((item) => item.account === token.belong) // 意味着仓库重名 if (match && match.length !== 0) { - res.status(400).json(new Message(400, 'repository is already exists.', null)); - return; + res.status(400).json({status: 400, message: 'repository is already exists.', data: null}) + return } // 不重名则更新 - const repo = repos[0]; + const repo = repos[0] await repositories.updateRepository(repo.id, { id: repo.id, @@ -116,7 +103,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< entry: entry, author: author, tags: tags - }); + }) await search.update({ id: repo.id, @@ -130,10 +117,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< : content.replace(/\n/g, ""), author: repo.author, tags: repo.tags - }); + }) - res.status(200).json(new Message(200, 'OK', repo)); - return; + res.status(200).json({status: 200, message: 'OK', data: repo}) + return } else { const repo = new Repository( crypto.randomUUID(), @@ -151,7 +138,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< tags ) - await repositories.createRepository(repo); + await repositories.createRepository(repo) await search.upload({ id: repo.id, url: repo.repository, @@ -164,9 +151,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< : content.replace(/\n/g, ""), author: repo.author, tags: repo.tags - }); + }) - res.status(200).json(new Message(200, 'OK', repo)); + res.status(200).json({status: 200, message: 'OK', data: repo}) } - } else res.status(400).json(new Message(400, 'request method not match.', null)); + } else res.status(400).json({status: 400, message: 'request method not match.', data: null}) } \ No newline at end of file diff --git a/src/pages/api/search.ts b/src/pages/api/search.ts index 57b3016..12abc7f 100644 --- a/src/pages/api/search.ts +++ b/src/pages/api/search.ts @@ -1,23 +1,26 @@ import type {NextApiRequest, NextApiResponse} from 'next' -import SearchService from "@/services/search"; -import Message from "@/common/message"; +import SearchService from "@/services/search" +import Message from "@/common/message" -export default async function handler(req: NextApiRequest, res: NextApiResponse<{}>) { - if (req.method === 'GET') { - const {query} = req.query; +export default async function handler(req: NextApiRequest, res: NextApiResponse>) { + if (req.method !== 'GET') { + res.status(400).json({status: 400, message: 'request method not match.', data: null}) + return + } - if (!query) { - res.status(400).json(new Message(400, 'Query is not set', null)); - return; - } - if (typeof query !== 'string') { - res.status(400).json(new Message(400, 'Query is not string', null)); - return; - } + const {query} = req.query; - const search = new SearchService(); - const result = await search.search(query); + if (!query) { + res.status(400).json({status: 400, message: 'Query is not set', data: null}) + return + } + if (typeof query !== 'string') { + res.status(400).json({status: 400, message: 'Query is not string', data: null}) + return + } - res.status(200).json(new Message(200, 'OK', result.hits)); - } else res.status(400).json(new Message(400, 'request method not match.', null)); + const search = new SearchService() + const result = await search.search(query) + + res.status(200).json({status: 200, message: 'OK', data: result.hits}) } \ No newline at end of file diff --git a/src/pages/api/statistic.ts b/src/pages/api/statistic.ts index 3a0f831..cbe93dd 100644 --- a/src/pages/api/statistic.ts +++ b/src/pages/api/statistic.ts @@ -1,8 +1,11 @@ -import {NextApiRequest, NextApiResponse} from "next"; -import Message from "@/common/message"; +import {NextApiRequest, NextApiResponse} from "next" +import Message from "@/common/message" -export default async function handler(req: NextApiRequest, res: NextApiResponse<{}>) { - if (req.method === 'GET') { - res.status(200).json(new Message(200, 'OK', null)); - } else res.status(400).json(new Message(400, 'request method not match.', null)); +export default async function handler(req: NextApiRequest, res: NextApiResponse>) { + if (req.method !== 'GET') { + res.status(400).json({status: 400, message: 'request method not match.', data: null}) + return + } + + res.status(200).json({status: 200, message: 'OK', data: null}) } \ No newline at end of file diff --git a/src/pages/api/verify.ts b/src/pages/api/verify.ts index 1bab1c5..7c82efa 100644 --- a/src/pages/api/verify.ts +++ b/src/pages/api/verify.ts @@ -1,37 +1,37 @@ import {NextApiRequest, NextApiResponse} from "next"; -import Message from "@/common/message"; -import CacheService from "@/services/cache"; -import {Token, TokenType} from "@/common/token"; -import {Account, AccountType} from "@/common/account"; -import DatabaseService from "@/services/database"; -import {AccountTable} from "@/data/account"; +import Message from "@/common/message" +import CacheService from "@/services/cache" +import {Token, TokenType} from "@/common/token" +import {Account, AccountType} from "@/common/account" +import DatabaseService from "@/services/database" +import {AccountTable} from "@/data/account" -export default async function handler(req: NextApiRequest, res: NextApiResponse<{}>) { +export default async function handler(req: NextApiRequest, res: NextApiResponse>) { if (req.method === 'GET') { - const {email, password, code} = req.query; + const {email, password, code} = req.query - const tokens = new CacheService(); - const token = await tokens.get(code as string); + const tokens = new CacheService() + const token = await tokens.get(code as string) if (!token) { - res.status(400).json(new Message(400, 'token expired.', null)); - return; + res.status(400).json({status: 400, message: 'token expired.', data: null}) + return } if (token.belong !== email) { - res.status(400).json(new Message(400, 'invalid token.', null)); - return; + res.status(400).json({status: 400, message: 'invalid token.', data: null}) + return } if (token.token !== code) { - res.status(400).json(new Message(400, 'invalid code.', null)); - return; + res.status(400).json({status: 400, message: 'invalid code.', data: null}) + return } // 修改密码 - const accounts = new DatabaseService(); + const accounts = new DatabaseService() // 检查表 - await accounts.autoMigrate(); + await accounts.autoMigrate() // 创建用户 const account = new Account( crypto.randomUUID(), @@ -43,22 +43,22 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< email, AccountType.EMAIL, new Date().getTime() - ); + ) - await accounts.createAccount(account); + await accounts.createAccount(account) - const time = new Date().getTime(); - const expire = new Date(time + 60 * 60 * 1000).getTime(); - const t = new Token( - crypto.randomUUID(), - account.id, - TokenType.ACCOUNT_API_KEY, - time, - expire - ); + const time = new Date().getTime() + const expire = new Date(time + 60 * 60 * 1000).getTime() + const t: Token = { + token: crypto.randomUUID(), + belong: account.id, + type: TokenType.ACCOUNT_API_KEY, + created: time, + expire: expire + } - await tokens.set(t.token, t); + await tokens.set(t.token, t) - res.status(200).json(new Message(200, 'success', {account: account, token: t})); - } else res.status(400).json(new Message(400, 'request method not match.', null)); + res.status(200).json({status: 200, message: 'success', data: {account: account, token: t}}) + } else res.status(400).json({status: 400, message: 'request method not match.', data: null}) } \ No newline at end of file diff --git a/src/pages/components/Avatar.tsx b/src/pages/components/Avatar.tsx index b599e97..f353d0e 100644 --- a/src/pages/components/Avatar.tsx +++ b/src/pages/components/Avatar.tsx @@ -1,6 +1,6 @@ -import {Component} from "react"; -import {withRouter} from "next/router"; -import {WithRouterProps} from "next/dist/client/with-router"; +import {Component} from "react" +import {withRouter} from "next/router" +import {WithRouterProps} from "next/dist/client/with-router" interface AvatarProps extends WithRouterProps { src?: string; diff --git a/src/pages/components/Button.tsx b/src/pages/components/Button.tsx index 878a207..c947e79 100644 --- a/src/pages/components/Button.tsx +++ b/src/pages/components/Button.tsx @@ -1,12 +1,12 @@ -import {Component} from "react"; -import {withRouter} from "next/router"; -import {WithRouterProps} from "next/dist/client/with-router"; +import {Component} from "react" +import {withRouter} from "next/router" +import {WithRouterProps} from "next/dist/client/with-router" interface ButtonProps extends WithRouterProps { - text: string; - icon?: string; - href?: string; - onclick?: () => void; + text: string + icon?: string + href?: string + onclick?: () => void } class Button extends Component { @@ -19,8 +19,8 @@ class Button extends Component { backgroundColor: "#24282f" }} onClick={() => { - this.props.onclick && this.props.onclick(); - this.props.href && this.props.router.push(this.props.href); + this.props.onclick && this.props.onclick() + this.props.href && this.props.router.push(this.props.href) }}> diff --git a/src/pages/components/Card.tsx b/src/pages/components/Card.tsx index a213094..f286eb5 100644 --- a/src/pages/components/Card.tsx +++ b/src/pages/components/Card.tsx @@ -1,10 +1,9 @@ -import {Component} from "react"; -import Link from "next/link"; +import {Component} from "react" interface CardProps { - project: string; - org: string; - url: string; + project: string + org: string + url: string } export default class Card extends Component { diff --git a/src/pages/components/Input.tsx b/src/pages/components/Input.tsx index 64da2c8..7821ec5 100644 --- a/src/pages/components/Input.tsx +++ b/src/pages/components/Input.tsx @@ -1,27 +1,27 @@ import React, {useState} from 'react'; interface Props { - placeholder: string; - height: string; - width: string; - indent?: string; - textColor?: string; - onChange: (value: string) => void; - onEnterPress: (value: string) => void; + placeholder: string + height: string + width: string + indent?: string + textColor?: string + onChange: (value: string) => void + onEnterPress: (value: string) => void } const Input: React.FC = ({onChange, onEnterPress, placeholder, height, width, textColor, indent}) => { - const [inputValue, setInputValue] = useState(''); + const [inputValue, setInputValue] = useState('') const handleInputChange = (event: React.ChangeEvent) => { - const value = event.target.value; - setInputValue(value); - onChange(value); + const value = event.target.value + setInputValue(value) + onChange(value) }; const handleEnterPress = (event: React.KeyboardEvent) => { if (event.key === 'Enter') { - onEnterPress(inputValue); + onEnterPress(inputValue) } } @@ -47,4 +47,4 @@ const Input: React.FC = ({onChange, onEnterPress, placeholder, height, wi ); }; -export default Input; \ No newline at end of file +export default Input \ No newline at end of file diff --git a/src/pages/components/Loading.tsx b/src/pages/components/Loading.tsx index 115ad1e..4456fdd 100644 --- a/src/pages/components/Loading.tsx +++ b/src/pages/components/Loading.tsx @@ -1,4 +1,4 @@ -import {Component} from "react"; +import {Component} from "react" export default class Loading extends Component<{}, {}> { render() { diff --git a/src/pages/components/Navbar.tsx b/src/pages/components/Navbar.tsx index 930a8d2..665360d 100644 --- a/src/pages/components/Navbar.tsx +++ b/src/pages/components/Navbar.tsx @@ -1,12 +1,12 @@ -import {Component} from "react"; -import Avatar from "@/pages/components/Avatar"; -import Searchbar from "@/pages/components/Searchbar"; -import Link from "next/link"; +import {Component} from "react" +import Avatar from "@/pages/components/Avatar" +import Searchbar from "@/pages/components/Searchbar" +import Link from "next/link" interface NavbarProps { - src?: string; - alt?: string; - isGlass?: boolean; + src?: string + alt?: string + isGlass?: boolean } export default class Navbar extends Component { diff --git a/src/pages/components/Row.tsx b/src/pages/components/Row.tsx index 94eb49e..d15574c 100644 --- a/src/pages/components/Row.tsx +++ b/src/pages/components/Row.tsx @@ -1,13 +1,13 @@ -import {Component} from "react"; -import {withRouter} from "next/router"; -import {WithRouterProps} from "next/dist/client/with-router"; +import {Component} from "react" +import {withRouter} from "next/router" +import {WithRouterProps} from "next/dist/client/with-router" interface RowProps extends WithRouterProps { - title: string; - text: string; - author: string[]; - tags: string[]; - url: string; + title: string + text: string + author: string[] + tags: string[] + url: string } class Row extends Component { diff --git a/src/pages/components/Searchbar.tsx b/src/pages/components/Searchbar.tsx index f2f6fcb..f2fc389 100644 --- a/src/pages/components/Searchbar.tsx +++ b/src/pages/components/Searchbar.tsx @@ -1,15 +1,15 @@ -import {Component} from "react"; -import Input from "@/pages/components/Input"; -import {withRouter} from "next/router"; -import {WithRouterProps} from "next/dist/client/with-router"; +import {Component} from "react" +import Input from "@/pages/components/Input" +import {withRouter} from "next/router" +import {WithRouterProps} from "next/dist/client/with-router" interface SearchbarProps extends WithRouterProps { - placeholder: string; - height: string; - width: string; - indent?: string; - textColor?: string; + placeholder: string + height: string + width: string + indent?: string + textColor?: string } class Searchbar extends Component { @@ -31,4 +31,4 @@ class Searchbar extends Component { } } -export default withRouter(Searchbar); \ No newline at end of file +export default withRouter(Searchbar) \ No newline at end of file diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 788e795..abee0d7 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,39 +1,39 @@ import {Inter} from 'next/font/google' -import Card from "@/pages/components/Card"; -import Navbar from "@/pages/components/Navbar"; -import Button from "@/pages/components/Button"; -import {State} from "@/pages/_app"; -import Input from "@/pages/components/Input"; -import {useEffect, useState} from "react"; -import {useRouter} from "next/router"; -import Message from "@/common/message"; -import {Account} from "@/common/account"; -import {Token} from "@/common/token"; -import {Repository} from "@/common/repository"; -import {Statistic} from "@/common/statistic"; +import Card from "@/pages/components/Card" +import Navbar from "@/pages/components/Navbar" +import Button from "@/pages/components/Button" +import {State} from "@/pages/_app" +import Input from "@/pages/components/Input" +import {useEffect, useState} from "react" +import {useRouter} from "next/router" +import Message from "@/common/message" +import {Account} from "@/common/account" +import {Token} from "@/common/token" +import {Repository} from "@/common/repository" +import {Statistic} from "@/common/statistic" const inter = Inter({subsets: ['latin']}) interface LoginResponse { - account: Account; - token: Token; + account: Account + token: Token } interface RepositoryResponse { - repository: Repository[]; + repository: Repository[] } interface StatisticResponse { - statistic: Statistic[]; + statistic: Statistic[] } export default function Home() { - const router = useRouter(); + const router = useRouter() - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [repository, setRepository] = useState([]); - const [statistic, setStatistic] = useState([]); + const [email, setEmail] = useState('') + const [password, setPassword] = useState('') + const [repository, setRepository] = useState([]) + const [statistic, setStatistic] = useState([]) const login = () => { fetch('/api/login', @@ -68,7 +68,7 @@ export default function Home() { return router.push(re.data).then(() => { }); } - }); + }) } useEffect(() => { @@ -93,7 +93,7 @@ export default function Home() { setStatistic(statistic); } }) - }, []); + }, []) return (
{ if (typeof window !== "undefined") { - const {code, state} = router.query; + const {code, state} = router.query if (typeof code === "string" && typeof state === "string") { // POST /api/login fetch(`/api/login?code=${code}&state=${state}`) .then(res => res.json()) .then(res => { - State.save(res.data.token, res.data.account); + State.save(res.data.token, res.data.account) router.push("/").then(ignore => { - }); - }); + }) + }) } } // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/src/pages/repository.tsx b/src/pages/repository.tsx index da0fb74..932460f 100644 --- a/src/pages/repository.tsx +++ b/src/pages/repository.tsx @@ -1,13 +1,13 @@ import {Inter} from 'next/font/google' -import Navbar from "@/pages/components/Navbar"; -import {useRouter} from "next/router"; -import {useState} from "react"; -import {Repository} from "@/common/repository"; -import {State} from "@/pages/_app"; -import {marked} from "marked"; -import Message from "@/common/message"; +import Navbar from "@/pages/components/Navbar" +import {useRouter} from "next/router" +import {useState} from "react" +import {Repository} from "@/common/repository" +import {State} from "@/pages/_app" +import {marked} from "marked" +import Message from "@/common/message" -const inter = Inter({subsets: ['latin']}); +const inter = Inter({subsets: ['latin']}) interface RepositoryData { repository: Repository[] @@ -16,41 +16,41 @@ interface RepositoryData { export default function RepositoryPage() { (function () { State.load() - }()); + }()) - const {id} = useRouter().query; - const [result, setResult] = useState(); - const avatar = State.account?.avatar ? State.account.avatar : `https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png`; + const {id} = useRouter().query + const [result, setResult] = useState() + const avatar = State.account?.avatar ? State.account.avatar : `https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png` marked.setOptions({ renderer: new marked.Renderer(), gfm: true, // 允许 Git Hub标准的markdown. pedantic: false, // 不纠正原始模型任何的不良行为和错误 breaks: false, // 允许回车换行 - }); + }) if (typeof window !== "undefined" && result?.readme) { fetch(result.readme) .then(result => result.text()) .then(data => { // 裁剪第二个 --- 之后的内容 - data = data.substring(data.indexOf("---", 4) + 3); + data = data.substring(data.indexOf("---", 4) + 3) - let dom = document.getElementById('marked'); + let dom = document.getElementById('marked') if (dom) dom.innerHTML = marked(data); - }); + }) } if (id && !result) { fetch('/api/repository?id=' + id) .then(result => result.json() as Promise>) .then(data => { - console.log(data); + console.log(data) if (data.status === 200 && data.data.repository.length > 0) { - setResult(data.data.repository[0]); + setResult(data.data.repository[0]) } - }); + }) } return ( diff --git a/src/pages/search.tsx b/src/pages/search.tsx index c5d4147..90db96e 100644 --- a/src/pages/search.tsx +++ b/src/pages/search.tsx @@ -1,26 +1,26 @@ import {Inter} from 'next/font/google' -import Searchbar from "@/pages/components/Searchbar"; -import {useRouter} from "next/router"; -import {useState} from "react"; -import {Index} from "@/common"; -import Message from "@/common/message"; -import Row from "@/pages/components/Row"; -import Loading from "@/pages/components/Loading"; -import {State} from "@/pages/_app"; -import Navbar from "@/pages/components/Navbar"; +import Searchbar from "@/pages/components/Searchbar" +import {useRouter} from "next/router" +import {useState} from "react" +import {Index} from "@/common" +import Message from "@/common/message" +import Row from "@/pages/components/Row" +import Loading from "@/pages/components/Loading" +import {State} from "@/pages/_app" +import Navbar from "@/pages/components/Navbar" const inter = Inter({subsets: ['latin']}) export default function Search() { (function () { State.load() - }()); + }()) const {query} = useRouter().query; const [result, setResult] = useState() const [tags, setTags] = useState() const [authors, setAuthors] = useState() - const avatar = State.account?.avatar ? State.account.avatar : `https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png`; + const avatar = State.account?.avatar ? State.account.avatar : `https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png` if (query && !result) { fetch('/api/search?query=' + query) @@ -32,24 +32,24 @@ export default function Search() { data && data.data.forEach((index) => { index.tags?.forEach(tag => { if (t.indexOf(tag) === -1) { - t.push(tag); + t.push(tag) } - }); - }); + }) + }) - setTags(t); + setTags(t) let a: string[] = []; data && data.data.forEach((index) => { index.author?.forEach(author => { if (a.indexOf(author) === -1) { - a.push(author); + a.push(author) } - }); + }) }) - setAuthors(a); - }); + setAuthors(a) + }) } return ( diff --git a/src/pages/upload.tsx b/src/pages/upload.tsx index ce31337..1dc087f 100644 --- a/src/pages/upload.tsx +++ b/src/pages/upload.tsx @@ -1,44 +1,44 @@ -import {State} from "@/pages/_app"; -import {Token} from "@/common/token"; -import {useState} from "react"; -import Button from "@/pages/components/Button"; -import {Inter} from "next/font/google"; -import Input from "@/pages/components/Input"; -import {useRouter} from "next/router"; +import {State} from "@/pages/_app" +import {Token} from "@/common/token" +import {useState} from "react" +import Button from "@/pages/components/Button" +import {Inter} from "next/font/google" +import Input from "@/pages/components/Input" +import {useRouter} from "next/router" const inter = Inter({subsets: ['latin']}) export default function UploadPage() { (function () { State.load() - }()); + }()) - const router = useRouter(); + const router = useRouter() // 从上一个页面传递的参数 // 如果不为 undefined 则为更新仓库 - const {update_organization, update_project} = router.query; + const {update_organization, update_project} = router.query // // 提交仓库表单 // // 组织 - const [organization, setOrganization] = useState(update_organization ? update_organization as string : ''); + const [organization, setOrganization] = useState(update_organization ? update_organization as string : '') // 项目 - const [project, setProject] = useState(update_project ? update_project as string : ''); + const [project, setProject] = useState(update_project ? update_project as string : '') // 版本 - const [version, setVersion] = useState(''); + const [version, setVersion] = useState('') // 仓库 - const [repository, setRepository] = useState(''); + const [repository, setRepository] = useState('') // README 链接 - const [readme, setReadme] = useState(''); + const [readme, setReadme] = useState('') // 入口 - const [entry, setEntry] = useState(''); + const [entry, setEntry] = useState('') // 作者 - const [author, setAuthor] = useState([]); + const [author, setAuthor] = useState([]) // 标签 - const [tags, setTags] = useState([]); + const [tags, setTags] = useState([]) - const token = State.token as Token; + const token = State.token as Token // TODO: 实现更新功能 const submit = () => { @@ -62,7 +62,7 @@ export default function UploadPage() { }) }) .then(result => result.json()) - .then(() => router.push("/account").then(ignore => 0)); + .then(() => router.push("/account").then(ignore => 0)) }; return ( diff --git a/src/pages/verify.tsx b/src/pages/verify.tsx index 64624bd..c4794a1 100644 --- a/src/pages/verify.tsx +++ b/src/pages/verify.tsx @@ -1,44 +1,44 @@ -import {State} from "@/pages/_app"; -import {useState} from "react"; -import Button from "@/pages/components/Button"; -import {Inter} from "next/font/google"; -import Input from "@/pages/components/Input"; -import {useRouter} from "next/router"; -import {Account} from "@/common/account"; -import {Token} from "@/common/token"; -import Message from "@/common/message"; +import {State} from "@/pages/_app" +import {useState} from "react" +import Button from "@/pages/components/Button" +import {Inter} from "next/font/google" +import Input from "@/pages/components/Input" +import {useRouter} from "next/router" +import {Account} from "@/common/account" +import {Token} from "@/common/token" +import Message from "@/common/message" const inter = Inter({subsets: ['latin']}) interface LoginResponse { - account: Account; - token: Token; + account: Account + token: Token } export default function VerifyPage() { (function () { State.load() - }()); + }()) - const router = useRouter(); - const {email, password} = router.query; + const router = useRouter() + const {email, password} = router.query // 组织 - const [code, setCode] = useState(''); + const [code, setCode] = useState('') const submit = () => { fetch(`/api/verify?email=${email}&password=${password}&code=${code}`) .then(result => result.json()) .then(res => { - let data = res as Message; + let data = res as Message if (data.status === 200) { - State.token = data.data.token; - State.account = data.data.account; + State.token = data.data.token + State.account = data.data.account - return router.push('/account'); + return router.push('/account') } - }); + }) }; return ( diff --git a/src/services/cache.ts b/src/services/cache.ts index 6828fec..ef38f4a 100644 --- a/src/services/cache.ts +++ b/src/services/cache.ts @@ -1,11 +1,11 @@ -import {kv} from "@vercel/kv"; +import {kv} from "@vercel/kv" export default class CacheService { public set(key: string, value: T): Promise { - return kv.set(key, value); + return kv.set(key, value) } public async get(key: string): Promise { - return kv.get(key); + return kv.get(key) } } \ No newline at end of file diff --git a/src/services/database.ts b/src/services/database.ts index 4049815..ddf05a5 100644 --- a/src/services/database.ts +++ b/src/services/database.ts @@ -1,15 +1,15 @@ -import {AccountTable, AccountType} from "@/data/account"; -import {RepositoryTable} from "@/data/repository"; -import {createKysely} from "@vercel/postgres-kysely"; -import {Account} from "@/common/account"; -import {Repository} from "@/common/repository"; -import {Statistic} from "@/common/statistic"; -import {StatisticTable} from "@/data/statistic"; +import {AccountTable, AccountType} from "@/data/account" +import {RepositoryTable} from "@/data/repository" +import {createKysely} from "@vercel/postgres-kysely" +import {Account} from "@/common/account" +import {Repository} from "@/common/repository" +import {Statistic} from "@/common/statistic" +import {StatisticTable} from "@/data/statistic" interface Database { - account: AccountTable; - repository: RepositoryTable; - statistic: StatisticTable; + account: AccountTable + repository: RepositoryTable + statistic: StatisticTable } export default class DatabaseService { @@ -36,8 +36,7 @@ export default class DatabaseService { .addColumn('author', 'text') .addColumn('tags', 'text') .addColumn('created', 'bigint') - .execute(); - + .execute() } catch (ignore) { } @@ -52,7 +51,7 @@ export default class DatabaseService { .addColumn('password', 'text') .addColumn('type', 'text') .addColumn('created', 'bigint') - .execute(); + .execute() } catch (ignore) { } @@ -64,7 +63,7 @@ export default class DatabaseService { .addColumn('visit', 'integer') .addColumn('search', 'integer') .addColumn('show', 'integer') - .execute(); + .execute() } catch (ignore) { } } @@ -74,7 +73,7 @@ export default class DatabaseService { return await this.db .insertInto('account') .values({id, openid, nickname, avatar, email, password, type, created}) - .execute(); + .execute() } public async readAccount(query: string, type: AccountType) { @@ -83,7 +82,7 @@ export default class DatabaseService { // 按照类型查询 openid(目前就 Github 一种) 或者 email .where(type === AccountType.GITHUB ? 'openid' : 'email', '=', query) .selectAll() - .execute(); + .execute() } public async readAccountById(id: string) { @@ -91,7 +90,7 @@ export default class DatabaseService { .selectFrom('account') .where('id', '=', id) .selectAll() - .execute(); + .execute() } public async updateAccount(queryId: string, {id, openid, nickname, avatar, type, created}: Account) { @@ -99,14 +98,14 @@ export default class DatabaseService { .updateTable('account') .set({id, openid, nickname, avatar, type, created}) .where('id', '=', queryId) - .execute(); + .execute() } public async deleteAccount(queryId: string) { return await this.db .deleteFrom('account') .where('id', '=', queryId) - .execute(); + .execute() } // Repository @@ -122,7 +121,7 @@ export default class DatabaseService { id, account, created, update, organization, project, version, readme, type, repository, entry, author, tags }) - .execute(); + .execute() } public async readRepository(id: string) { @@ -130,7 +129,7 @@ export default class DatabaseService { .selectFrom('repository') .where('id', '=', id) .selectAll() - .execute(); + .execute() } public async readRepositoryByOrganizationAndProject(organization: string, project: string) { @@ -139,7 +138,7 @@ export default class DatabaseService { .where('organization', '=', organization) .where('project', '=', project) .selectAll() - .execute(); + .execute() } public async readRepositoryByAccount(account: string) { @@ -147,7 +146,7 @@ export default class DatabaseService { .selectFrom('repository') .selectAll() .where('account', '=', account) - .execute(); + .execute() } public async readRepositoryByLimit(limit: number) { @@ -157,7 +156,7 @@ export default class DatabaseService { .selectAll() .orderBy('created', 'desc') .limit(limit) - .execute(); + .execute() } public async updateRepository( @@ -174,14 +173,14 @@ export default class DatabaseService { readme, type, repository, entry, author, tags }) .where('id', '=', queryId) - .execute(); + .execute() } public async deleteRepository(id: string) { return await this.db .deleteFrom('repository') .where('id', '=', id) - .execute(); + .execute() } // Statistic @@ -193,7 +192,7 @@ export default class DatabaseService { .values({ id, organization, project, visit, search, show }) - .execute(); + .execute() } public async readStatistic(id: string) { @@ -201,7 +200,7 @@ export default class DatabaseService { .selectFrom('statistic') .where('id', '=', id) .selectAll() - .execute(); + .execute() } public async readStatisticByOrganizationAndProject(organization: string, project: string) { @@ -210,7 +209,7 @@ export default class DatabaseService { .where('organization', '=', organization) .where('project', '=', project) .selectAll() - .execute(); + .execute() } public async readStatisticByVisit(limit: number) { @@ -219,7 +218,7 @@ export default class DatabaseService { .selectAll() .orderBy('visit', 'desc') .limit(limit) - .execute(); + .execute() } public async readStatisticBySearch(limit: number) { @@ -228,7 +227,7 @@ export default class DatabaseService { .selectAll() .orderBy('search', 'desc') .limit(limit) - .execute(); + .execute() } public async readStatisticByShow(limit: number) { @@ -237,7 +236,7 @@ export default class DatabaseService { .selectAll() .orderBy('show', 'desc') .limit(limit) - .execute(); + .execute() } public async updateStatistic( @@ -252,7 +251,7 @@ export default class DatabaseService { id, organization, project, visit, search, show }) .where('id', '=', queryId) - .execute(); + .execute() } public async updateStatisticByOrganizationAndProject( @@ -269,13 +268,13 @@ export default class DatabaseService { }) .where('organization', '=', organization) .where('project', '=', project) - .execute(); + .execute() } public async deleteStatistic(id: string) { return await this.db .deleteFrom('statistic') .where('id', '=', id) - .execute(); + .execute() } } \ No newline at end of file diff --git a/src/services/mail.ts b/src/services/mail.ts index cfcd34d..d7f653d 100644 --- a/src/services/mail.ts +++ b/src/services/mail.ts @@ -1,23 +1,23 @@ export default class EmailService { - config: any = {}; - nodemailer: any; - emailSender: string; + config: any = {} + nodemailer: any + emailSender: string constructor() { - const emailSender = process.env.EMAIL_SENDER; - const emailSenderPassword = process.env.EMAIL_SENDER_PASSWORD; - const emailSmtpHost = process.env.EMAIL_SMTP_HOST; - const emailSmtpPort = process.env.EMAIL_SMTP_PORT; - const emailSmtpSecure = process.env.EMAIL_SMTP_SECURE; + const emailSender = process.env.EMAIL_SENDER + const emailSenderPassword = process.env.EMAIL_SENDER_PASSWORD + const emailSmtpHost = process.env.EMAIL_SMTP_HOST + const emailSmtpPort = process.env.EMAIL_SMTP_PORT + const emailSmtpSecure = process.env.EMAIL_SMTP_SECURE - if (emailSender === null || !emailSender) throw new Error('emailSender is invalid.'); - if (emailSenderPassword === null || !emailSenderPassword) throw new Error('emailSenderPassword is invalid.'); - if (emailSmtpHost === null || !emailSmtpHost) throw new Error('emailSmtpHost is invalid.'); - if (emailSmtpPort === null || !emailSmtpPort) throw new Error('emailSmtpPort is invalid.'); - if (emailSmtpSecure === null || !emailSmtpSecure) throw new Error('emailSmtpSecure is invalid.'); + if (emailSender === null || !emailSender) throw new Error('emailSender is invalid.') + if (emailSenderPassword === null || !emailSenderPassword) throw new Error('emailSenderPassword is invalid.') + if (emailSmtpHost === null || !emailSmtpHost) throw new Error('emailSmtpHost is invalid.') + if (emailSmtpPort === null || !emailSmtpPort) throw new Error('emailSmtpPort is invalid.') + if (emailSmtpSecure === null || !emailSmtpSecure) throw new Error('emailSmtpSecure is invalid.') - this.emailSender = emailSender; - this.nodemailer = require('nodemailer'); + this.emailSender = emailSender + this.nodemailer = require('nodemailer') this.config = { host: emailSmtpHost, port: emailSmtpPort, @@ -26,7 +26,7 @@ export default class EmailService { user: emailSender, pass: emailSenderPassword, }, - }; + } } public async send(to: string, subject: string, html: string) { @@ -36,6 +36,6 @@ export default class EmailService { to, subject, html, - }); + }) } } \ No newline at end of file diff --git a/src/services/network.ts b/src/services/network.ts index 582dfd4..2463f73 100644 --- a/src/services/network.ts +++ b/src/services/network.ts @@ -1,11 +1,11 @@ export default class NetworkService { public async fetchGithubReadme(repository: string) { - return await fetch(`https://raw.githubusercontent.com/${repository}/master/README.md`).then(res => res.text()); + return await fetch(`https://raw.githubusercontent.com/${repository}/master/README.md`).then(res => res.text()) } public checkIfUrlIsGithub(url: string) { - let target = new URL(url); + let target = new URL(url) - return target.hostname.toLowerCase() === 'github.com'; + return target.hostname.toLowerCase() === 'github.com' } } \ No newline at end of file diff --git a/src/services/search.ts b/src/services/search.ts index 2a7d168..37b5de2 100644 --- a/src/services/search.ts +++ b/src/services/search.ts @@ -1,27 +1,27 @@ -import algoliasearch, {SearchClient} from "algoliasearch"; -import {Index} from "@/common"; +import algoliasearch, {SearchClient} from "algoliasearch" +import {Index} from "@/common" export default class SearchService { client: SearchClient; constructor() { - const algoliaApplicationID = process.env.ALGOLIA_APPLICATION_ID; - const algoliaAPIKey = process.env.ALGOLIA_API_KEY; + const algoliaApplicationID = process.env.ALGOLIA_APPLICATION_ID + const algoliaAPIKey = process.env.ALGOLIA_API_KEY - if (!algoliaApplicationID) throw new Error('ALGOLIA_APPLICATION_ID is not set'); - if (!algoliaAPIKey) throw new Error('ALGOLIA_API_KEY is not set'); + if (!algoliaApplicationID) throw new Error('ALGOLIA_APPLICATION_ID is not set') + if (!algoliaAPIKey) throw new Error('ALGOLIA_API_KEY is not set') - this.client = algoliasearch(algoliaApplicationID, algoliaAPIKey); + this.client = algoliasearch(algoliaApplicationID, algoliaAPIKey) } async search(query: string) { - const index = this.client.initIndex('docs'); + const index = this.client.initIndex('docs') - return index.search(query); + return index.search(query) } async upload({id, url, organization, project, readme, content, author, tags}: Index) { - const index = this.client.initIndex('docs'); + const index = this.client.initIndex('docs') const save = { objectID: id, id: id, @@ -34,11 +34,11 @@ export default class SearchService { tags: tags } - return index.saveObject(save).wait(); + return index.saveObject(save).wait() } async update({id, url, organization, project, readme, content, author, tags}: Index) { - const index = this.client.initIndex('docs'); + const index = this.client.initIndex('docs') index.partialUpdateObject({ objectID: id, @@ -54,8 +54,8 @@ export default class SearchService { } async delete(id: string) { - const index = this.client.initIndex('docs'); + const index = this.client.initIndex('docs') - return index.deleteObject(id).wait(); + return index.deleteObject(id).wait() } } \ No newline at end of file