From de8a8ec4b7db1851e3cea2fe117f9bf32b961340 Mon Sep 17 00:00:00 2001 From: goodspeed Date: Sat, 12 Oct 2024 21:26:46 +0800 Subject: [PATCH] auto update repo info --- components/CommentBox.vue | 18 +- pages/blog/[id].vue | 1 - .../[repo_owner]/[repo_name]/blog/[id].vue | 1 - pages/repo/[repo_owner]/[repo_name]/index.vue | 2 +- server/api/github/create-comment.ts | 2 +- server/api/github/issues.ts | 50 ++++++ server/api/github/repos.ts | 25 +++ .../[repo_owner]/[repo_name]/blog-posts.ts | 103 +++++------ server/api/repo/list.ts | 19 +- server/api/repos.ts | 169 ++++++++++++++++++ 10 files changed, 320 insertions(+), 70 deletions(-) create mode 100644 server/api/github/issues.ts create mode 100644 server/api/github/repos.ts create mode 100644 server/api/repos.ts diff --git a/components/CommentBox.vue b/components/CommentBox.vue index 6188896..e57a7f5 100644 --- a/components/CommentBox.vue +++ b/components/CommentBox.vue @@ -1,6 +1,6 @@ diff --git a/pages/repo/[repo_owner]/[repo_name]/blog/[id].vue b/pages/repo/[repo_owner]/[repo_name]/blog/[id].vue index 7c7efc1..b74b43b 100644 --- a/pages/repo/[repo_owner]/[repo_name]/blog/[id].vue +++ b/pages/repo/[repo_owner]/[repo_name]/blog/[id].vue @@ -35,7 +35,6 @@
-

No comments yet.

diff --git a/pages/repo/[repo_owner]/[repo_name]/index.vue b/pages/repo/[repo_owner]/[repo_name]/index.vue index a1e1963..017a7eb 100644 --- a/pages/repo/[repo_owner]/[repo_name]/index.vue +++ b/pages/repo/[repo_owner]/[repo_name]/index.vue @@ -37,7 +37,7 @@ const fetchBlogPosts = async (page = 1) => { // Fetch initial data await fetchBlogPosts() -console.log(totalItems.value, perPage.value, currentPage.value) +// console.log(totalItems.value, perPage.value, currentPage.value) // Handle page changes const onPageChange = async (page: number) => { diff --git a/server/api/github/create-comment.ts b/server/api/github/create-comment.ts index 90d38a9..f15c138 100644 --- a/server/api/github/create-comment.ts +++ b/server/api/github/create-comment.ts @@ -9,7 +9,7 @@ export default defineEventHandler(async (event) => { const githubUser = getCookie(event, 'github_username') // console.log("github user:", githubUser, "repo:", config.public.repoName, "token:", token) - if (!token) { + if (!token || !githubUser) { throw createError({ statusCode: 401, message: 'GitHub token not found' diff --git a/server/api/github/issues.ts b/server/api/github/issues.ts new file mode 100644 index 0000000..d667200 --- /dev/null +++ b/server/api/github/issues.ts @@ -0,0 +1,50 @@ +import { H3Event } from 'h3' +import { useRuntimeConfig } from '#imports' +import { Octokit } from '@octokit/rest' + +export async function getIssuesList(event: H3Event, repo_owner: string, repo_name: string, labels: string, perPage: number, page: number) { + const config = useRuntimeConfig() + var token = getCookie(event, 'github_token') + if (!token) { + token = config.private.githubToken + } + const octokit = new Octokit({ auth: token }) + var req_data = { + owner: repo_owner, + repo: repo_name, + // state: 'open', // 只获取开放的 issues + sort: 'created', + direction: 'desc', + per_page: perPage,// 限制返回的数量,你可以根据需要调整 + page: page, + labels: labels + } + req_data.labels = labels + // console.log("req: ", req_data) + + try { + + const { data: issues, headers } = await octokit.issues.listForRepo(req_data) + + const formattedIssues = issues.map(issue => ({ + number: issue.number, + title: issue.title, + body: issue.body, + created_at: issue.created_at, + labels: issue.labels, + reactions: issue.reactions, + comments: issue.comments, + html_url: `/repo/${repo_owner}/${repo_name}/blog/${issue.number}`, + github_url: issue.html_url, + updated_at: issue.updated_at, + author: issue.user.login, + repo_url: `/repo/${repo_owner}/${repo_name}`, + url: issue.html_url + })) + + return { issues: formattedIssues, headers } + } catch (error) { + console.error('GitHub API error:', error) + throw createError({ statusCode: 500, statusMessage: 'Failed to fetch issues from GitHub' }) + } +} \ No newline at end of file diff --git a/server/api/github/repos.ts b/server/api/github/repos.ts new file mode 100644 index 0000000..3c4a1d5 --- /dev/null +++ b/server/api/github/repos.ts @@ -0,0 +1,25 @@ +import { H3Event } from 'h3' +import { Octokit } from '@octokit/rest' + +export async function getGitHubRepoInfo(event: H3Event, owner: string, repo: string) { + const config = useRuntimeConfig() + const octokit = new Octokit({ auth: config.githubToken }) + + try { + const { data: githubRepo } = await octokit.repos.get({ owner, repo }) + + return { + repo_id: githubRepo.id, + repo_owner: owner, + repo_name: repo, + repo_description: githubRepo.description, + stars_count: githubRepo.stargazers_count, + forks_count: githubRepo.forks_count, + updated_at: new Date().toISOString(), + repo_detail: githubRepo + } + } catch (error) { + console.error('GitHub API error:', error) + throw createError({ statusCode: 500, statusMessage: 'Failed to fetch repo info from GitHub' }) + } +} \ No newline at end of file diff --git a/server/api/repo/[repo_owner]/[repo_name]/blog-posts.ts b/server/api/repo/[repo_owner]/[repo_name]/blog-posts.ts index c79feea..d6cf918 100644 --- a/server/api/repo/[repo_owner]/[repo_name]/blog-posts.ts +++ b/server/api/repo/[repo_owner]/[repo_name]/blog-posts.ts @@ -1,89 +1,68 @@ // server/api/repo/[repo_owner]/[repo_name]/blog-posts.ts import { defineEventHandler } from 'h3' -import { Octokit } from '@octokit/rest' +import { getGitHubRepoInfo } from '../../../github/repos' +import { getIssuesList } from '../../../github/issues' +import { getRepoById, updateOrCreateRepo } from '../../../repos' + export default defineEventHandler(async (event) => { - const { repo_owner, repo_name } = event.context.params const query = getQuery(event) - const tag = query.tag as string const page = parseInt(query.page as string) || 1 const perPage = parseInt(query.perPage as string) || 20 + const { repo_owner, repo_name } = event.context.params + const tag = query.tag as string + + try { + let repoData + try { + // 尝试从 /api/repos/list 获取数据 + repoData = await getRepoById(event, `${repo_owner}/${repo_name}`) + } catch (error) { + console.error('Failed to fetch repos from DB, errpr: ', error) + } - // console.log("github issus: ", repo_owner, repo_name, tag) - var token = getCookie(event, 'github_token') + const now = new Date() + const shouldFetchFromGitHub = !repoData || + (now.getTime() - new Date(repoData.updated_at).getTime() > 3600000) // 1 hour in milliseconds - const config = useRuntimeConfig() - if (!token) { - token = config.private.githubToken - } - // console.log("github token: ", token) - const octokit = new Octokit({ - auth: token - }) + // console.log("update time,", repoData) + // console.log("shouldFetchFromGitHub", shouldFetchFromGitHub, now.getTime(), new Date(repoData.updated_at).getTime()) - try { - // 获取仓库信息 - const { data: repoData } = await octokit.repos.get({ - owner: repo_owner, - repo: repo_name, - }) + if (shouldFetchFromGitHub) { + try { + const githubRepoInfo = await getGitHubRepoInfo(event, repo_owner, repo_name) - var req_data = { - owner: repo_owner, - repo: repo_name, - // state: 'open', // 只获取开放的 issues - sort: 'created', - direction: 'desc', - per_page: perPage,// 限制返回的数量,你可以根据需要调整 - page: page, - labels: tag + // console.log("githubRepoInfo, ", githubRepoInfo) + try { + // 更新或插入数据库 + repoData = await updateOrCreateRepo(event, githubRepoInfo, repoData) + } catch (error) { + console.error('Failed to fetch repos from DB, errpr: ', error) + } + } catch (error) { + console.error('Failed to fetch or update repo info:', error) + } } - req_data.labels = tag - // console.log("request: ", req_data) - // 获取仓库的 issues 列表 - const { data: issues, headers } = await octokit.issues.listForRepo(req_data) - // console.log("issues: ", headers) + const { issues, headers } = await getIssuesList(event, repo_owner, repo_name, tag, perPage, page) const totalItems = getTotalPages(headers, page) * perPage + // console.log("totalItems: ", totalItems, page, perPage) + // console.log("repoData: ", repoData) - // console.log("query:", query, "page:", page, "perPage: ", perPage, "total: ", totalItems, repo_owner, repo_name) - // Get total count of issues - // console.log("total: ", totalItems) return { - repo: { - name: repoData.name, - description: repoData.description, - stars: repoData.stargazers_count, - forks: repoData.forks_count, - }, - blogPosts: issues.map(issue => ({ - number: issue.number, - title: issue.title, - body: issue.body, - created_at: issue.created_at, - labels: issue.labels, - reactions: issue.reactions, - comments: issue.comments, - html_url: `/repo/${repo_owner}/${repo_name}/blog/${issue.number}`, - github_url: issue.html_url, - updated_at: issue.updated_at, - author: issue.user.login, - repo_url: `/repo/${repo_owner}/${repo_name}`, - url: issue.html_url - })), + repo: repoData, + blogPosts: issues, pagination: { currentPage: page, totalItems: totalItems, perPage: perPage } + } } catch (error) { - console.error('Error fetching data from GitHub:', error) - throw createError({ - statusCode: 500, - statusMessage: 'Error fetching data from GitHub' - }) + console.error('Error in blog-posts:', error) + throw createError({ statusCode: 500, statusMessage: 'Internal Server Error' }) } }) diff --git a/server/api/repo/list.ts b/server/api/repo/list.ts index 0704352..15b5cd4 100644 --- a/server/api/repo/list.ts +++ b/server/api/repo/list.ts @@ -1,6 +1,7 @@ import { defineEventHandler, getQuery } from 'h3' +import { getReposList } from '../repos' -const repos = [ +const defaultRepos = [ { name: "path-meme-db", description: "一些胡言乱语", @@ -28,16 +29,28 @@ const repos = [ // ... 添加更多仓库数据 ] -export default defineEventHandler((event) => { +export default defineEventHandler(async (event) => { const query = getQuery(event) const page = parseInt(query.page as string) || 1 const limit = parseInt(query.limit as string) || 10 + let repos + try { + // 尝试从 /api/repos/list 获取数据 + repos = await getReposList(event) + if (repos.length == 0) { + repos = defaultRepos + } + } catch (error) { + console.error('Failed to fetch repos from API, using default data', error) + repos = defaultRepos + } + + console.log("repos: ", repos) const startIndex = (page - 1) * limit const endIndex = page * limit const paginatedRepos = repos.slice(startIndex, endIndex) - return { repos: paginatedRepos, totalItems: repos.length, diff --git a/server/api/repos.ts b/server/api/repos.ts new file mode 100644 index 0000000..5da8e3f --- /dev/null +++ b/server/api/repos.ts @@ -0,0 +1,169 @@ +import { serverSupabaseClient } from '#supabase/server' +import { H3Event } from 'h3' +interface RepoData { + name: string; + description: string; + stars: number; + forks: number; + owner_name: string; + created_at: string; + updated_at: string; +} + +interface RepoInfoTab { + // id: number; + repo_id: string; + repo_owner: string; + repo_name: string; + is_show: Boolean; + rank_score: number, + created_at: string; + updated_at: string; + repo_detail: object; +} +// 列表查询函数 +export async function getReposList(event: H3Event) { + const client = await serverSupabaseClient(event) + const { data, error } = await client + .from('repo_info_tab') + .select('*') + .order('rank_score', { ascending: false }) + + if (error) throw createError({ statusCode: 400, statusMessage: error.message }) + const formattedData: RepoData[] = data.map(repo => ({ + name: repo.repo_name || '', + description: repo.repo_detail.description || '', + stars: repo.repo_detail.stars || 0, + forks: repo.repo_detail.forks || 0, + owner_name: repo.repo_owner || '', + updated_at: repo.updated_at || new Date().toISOString() + })) + + return formattedData +} + +// 按 repo id 查询函数 +export async function getRepoById(event: H3Event, id: string) { + const client = await serverSupabaseClient(event) + const { data, error } = await client + .from('repo_info_tab') + .select('*') + .eq('repo_id', id) + .single() + + if (error) throw createError({ statusCode: 400, statusMessage: error.message }) + const formattedData: RepoData = { + name: data.repo_name || '', + description: data.repo_detail.description || '', + stars: data.repo_detail.stars || 0, + forks: data.repo_detail.forks || 0, + owner_name: data.repo_owner || '', + created_at: data.created_at, + updated_at: data.updated_at + } + return formattedData +} + +// repo 更新函数 +export async function updateRepo(event: H3Event, id: string, updateData: any) { + const client = await serverSupabaseClient(event) + const { data, error } = await client + .from('repos') + .update(updateData) + .eq('id', id) + .select() + + if (error) throw createError({ statusCode: 400, statusMessage: error.message }) + return data +} + +// repo 创建函数 +export async function createRepo(event: H3Event, repoData: any) { + const client = await serverSupabaseClient(event) + const { data, error } = await client + .from('repos') + .insert(repoData) + .select() + + if (error) throw createError({ statusCode: 400, statusMessage: error.message }) + return data +} + +// repo update or create +export async function updateOrCreateRepo(event: H3Event, githubRepoData: any, repoData: any) { + const client = await serverSupabaseClient(event) + // console.log("repoData: ", repoData) + const repoTab: RepoInfoTab = { + repo_id: `${githubRepoData.repo_owner}/${githubRepoData.repo_name}`, + repo_owner: githubRepoData.repo_owner, + repo_name: githubRepoData.repo_name, + repo_detail: { + "stars": githubRepoData.stars_count, + "forks": githubRepoData.forks_count, + "description": githubRepoData.repo_description, + }, + rank_score: githubRepoData.stars_count + githubRepoData.forks_count, + is_show: true, + created_at: new Date().toISOString(), + updated_at: new Date().toISOString() + } + if (!!repoData && repoData.created_at) { + repoTab.is_show = repoData.is_show + repoTab.created_at = repoData.created_at + } + // console.log("repo_tab: ", repoTab) + // 更新或插入数据库 + const { data, error } = await client + .from('repo_info_tab') + .upsert(repoTab, { onConflict: 'repo_id' }) + .select() + .single() + + if (error) throw createError({ statusCode: 400, statusMessage: error.message }) + + const formattedData: RepoData = { + name: repoTab.repo_name || '', + description: repoTab.repo_detail.description || '', + stars: repoTab.repo_detail.stars || 0, + forks: repoTab.repo_detail.forks || 0, + owner_name: repoTab.repo_owner || '', + created_at: repoTab.created_at, + updated_at: repoTab.updated_at + } + // console.log("formattedData: ", formattedData) + return formattedData + +} + +// 主处理函数 +export default defineEventHandler(async (event: H3Event) => { + const method = event.node.req.method + const url = new URL(event.node.req.url, `http://${event.node.req.headers.host}`) + const path = url.pathname.split('/').pop() + + // 列表查询接口 + if (method === 'GET' && path === 'list') { + return await getReposList(event) + } + + // 按 repo id 查询接口 + if (method === 'GET' && path === 'get') { + const { id } = getQuery(event) + return await getRepoById(event, id as string) + } + + // repo 更新接口 + if (method === 'PUT' && path === 'update') { + const body = await readBody(event) + const { id, ...updateData } = body + return await updateRepo(event, id, updateData) + } + + // repo 创建接口 + if (method === 'POST' && path === 'create') { + const body = await readBody(event) + return await createRepo(event, body) + } + + throw createError({ statusCode: 404, statusMessage: 'Not Found' }) +}) \ No newline at end of file