This commit is contained in:
eric
2025-11-13 05:05:53 +08:00
parent cbdc65f56e
commit 891af61146
691 changed files with 83952 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
import { NextResponse } from 'next/server'
import { auth } from '@/lib/auth'
import { commitFile, getFileContent } from '@/lib/github'
import type { NavigationData, NavigationItem } from '@/types/navigation'
export const runtime = 'edge'
export async function DELETE(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const session = await auth()
if (!session?.user?.accessToken) {
return new Response('Unauthorized', { status: 401 })
}
const { categoryId } = await request.json()
if (!categoryId) {
return NextResponse.json({ error: 'Category ID is required' }, { status: 400 })
}
const data = await getFileContent('navsphere/content/navigation.json') as NavigationData
const navigation = data.navigationItems.find(nav => nav.id === id)
if (!navigation) {
return NextResponse.json({ error: 'Navigation not found' }, { status: 404 })
}
// 检查分类是否存在
const categoryExists = navigation.subCategories?.some(cat => cat.id === categoryId)
if (!categoryExists) {
return NextResponse.json({ error: 'Category not found' }, { status: 404 })
}
// 删除指定的分类
const updatedSubCategories = navigation.subCategories?.filter(cat => cat.id !== categoryId) || []
const updatedNavigations = data.navigationItems.map(nav => {
if (nav.id === id) {
return {
...nav,
subCategories: updatedSubCategories
}
}
return nav
})
await commitFile(
'navsphere/content/navigation.json',
JSON.stringify({ navigationItems: updatedNavigations }, null, 2),
`Delete category: ${categoryId}`,
session.user.accessToken
)
return NextResponse.json({ success: true })
} catch (error) {
console.error('Delete category error:', error)
return NextResponse.json({ error: 'Failed to delete category' }, { status: 500 })
}
}

View File

@@ -0,0 +1,150 @@
import { NextResponse } from 'next/server'
import { auth } from '@/lib/auth'
import { commitFile, getFileContent } from '@/lib/github'
import type { NavigationData, NavigationItem, NavigationSubItem } from '@/types/navigation'
export const runtime = 'edge'
export async function GET(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const data = await getFileContent('navsphere/content/navigation.json') as NavigationData
const item = data.navigationItems.find(item => item.id === id)
if (!item) {
return NextResponse.json({ error: 'Navigation not found' }, { status: 404 })
}
return NextResponse.json(item.items)
} catch (error) {
return NextResponse.json({ error: 'Failed to fetch items' }, { status: 500 })
}
}
export async function POST(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const session = await auth()
if (!session?.user?.accessToken) {
return new Response('Unauthorized', { status: 401 })
}
const newItem: NavigationSubItem = await request.json()
const data = await getFileContent('navsphere/content/navigation.json') as NavigationData
const updatedItems = data.navigationItems.map(item => {
if (item.id === id) {
return {
...item,
items: [...(item.items || []), newItem]
}
}
return item
})
await commitFile(
'navsphere/content/navigation.json',
JSON.stringify({ navigationItems: updatedItems }, null, 2),
'Add navigation item',
session.user.accessToken
)
return NextResponse.json({ success: true })
} catch (error) {
return NextResponse.json({ error: 'Failed to add item' }, { status: 500 })
}
}
export async function PUT(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const session = await auth()
if (!session?.user?.accessToken) {
return new Response('Unauthorized', { status: 401 })
}
const { index, item }: { index: number, item: NavigationSubItem } = await request.json()
const data = await getFileContent('navsphere/content/navigation.json') as NavigationData
const navigation = data.navigationItems.find(nav => nav.id === id)
if (!navigation) {
return NextResponse.json({ error: 'Navigation not found' }, { status: 404 })
}
const updatedItems = [...(navigation.items || [])]
updatedItems[index] = item
const updatedNavigations = data.navigationItems.map(nav => {
if (nav.id === id) {
return {
...nav,
items: updatedItems
}
}
return nav
})
await commitFile(
'navsphere/content/navigation.json',
JSON.stringify({ navigationItems: updatedNavigations }, null, 2),
'Update navigation item',
session.user.accessToken
)
return NextResponse.json({ success: true })
} catch (error) {
return NextResponse.json({ error: 'Failed to update item' }, { status: 500 })
}
}
export async function DELETE(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const session = await auth()
if (!session?.user?.accessToken) {
return new Response('Unauthorized', { status: 401 })
}
const { index } = await request.json()
const data = await getFileContent('navsphere/content/navigation.json') as NavigationData
const navigation = data.navigationItems.find(nav => nav.id === id)
if (!navigation) {
return NextResponse.json({ error: 'Navigation not found' }, { status: 404 })
}
const updatedItems = (navigation.items || []).filter((_, i) => i !== index)
const updatedNavigations = data.navigationItems.map(nav => {
if (nav.id === id) {
return {
...nav,
items: updatedItems
}
}
return nav
})
await commitFile(
'navsphere/content/navigation.json',
JSON.stringify({ navigationItems: updatedNavigations }, null, 2),
'Delete navigation item',
session.user.accessToken
)
return NextResponse.json({ success: true })
} catch (error) {
return NextResponse.json({ error: 'Failed to delete item' }, { status: 500 })
}
}

View File

@@ -0,0 +1,100 @@
import { NextResponse } from 'next/server'
import { auth } from '@/lib/auth'
import { commitFile, getFileContent } from '@/lib/github'
import type { NavigationData, NavigationItem } from '@/types/navigation'
export const runtime = 'edge'
export async function GET(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const data = await getFileContent('navsphere/content/navigation.json') as NavigationData
const item = data.navigationItems.find(item => item.id === id)
if (!item) {
return new Response('Not Found', { status: 404 })
}
return NextResponse.json(item)
} catch (error) {
return NextResponse.json({ error: 'Failed to fetch navigation item' }, { status: 500 })
}
}
export async function PUT(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const session = await auth()
if (!session?.user?.accessToken) {
return new Response('Unauthorized', { status: 401 })
}
const updatedItem: NavigationItem = await request.json()
const data = await getFileContent('navsphere/content/navigation.json') as NavigationData
// 确保更新的导航项包含所有必需的字段
const existingItem = data.navigationItems.find(item => item.id === id)
if (!existingItem) {
return new Response('Navigation item not found', { status: 404 })
}
// 更新导航项,保持原有的 ID
const mergedItem: NavigationItem = {
...existingItem,
...updatedItem,
id: id,
items: updatedItem.items || existingItem.items || [],
subCategories: updatedItem.subCategories || existingItem.subCategories || []
}
const updatedItems = data.navigationItems.map(item =>
item.id === id ? mergedItem : item
)
await commitFile(
'navsphere/content/navigation.json',
JSON.stringify({ navigationItems: updatedItems }, null, 2),
'Update navigation item',
session.user.accessToken
)
return NextResponse.json(mergedItem)
} catch (error) {
console.error('Update error:', error)
return NextResponse.json({ error: 'Failed to update navigation' }, { status: 500 })
}
}
export async function DELETE(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params
const session = await auth()
if (!session?.user?.accessToken) {
return new Response('Unauthorized', { status: 401 })
}
const data = await getFileContent('navsphere/content/navigation.json') as NavigationData
const updatedItems = data.navigationItems.filter(item => item.id !== id)
await commitFile(
'navsphere/content/navigation.json',
JSON.stringify({ navigationItems: updatedItems }, null, 2),
'Delete navigation item',
session.user.accessToken
)
return NextResponse.json({ success: true })
} catch (error) {
console.error('Delete error:', error)
return NextResponse.json({ error: 'Failed to delete navigation' }, { status: 500 })
}
}

View File

@@ -0,0 +1,48 @@
import { NextResponse } from 'next/server'
import { auth } from '@/lib/auth'
import { getFileContent } from '@/lib/github'
export const runtime = 'edge'
export async function GET() {
try {
const session = await auth()
if (!session?.user?.accessToken) {
return new Response('Unauthorized', { status: 401 })
}
try {
const defaultData = await getFileContent('navsphere/content/navigation-default.json')
// 验证文件格式
const isValid = defaultData &&
typeof defaultData === 'object' &&
Array.isArray(defaultData.navigationItems)
return NextResponse.json({
exists: true,
valid: isValid,
itemCount: isValid ? defaultData.navigationItems.length : 0
})
} catch (error) {
// 文件不存在
if ((error as Error).message.includes('404') || (error as Error).message.includes('not found')) {
return NextResponse.json({
exists: false,
valid: false,
itemCount: 0
})
}
throw error
}
} catch (error) {
console.error('Failed to check default file:', error)
return NextResponse.json(
{
error: 'Failed to check default file',
details: (error as Error).message
},
{ status: 500 }
)
}
}

View File

@@ -0,0 +1,53 @@
import { NextResponse } from 'next/server'
import { auth } from '@/lib/auth'
import { commitFile, getFileContent } from '@/lib/github'
import type { NavigationData, NavigationItem } from '@/types/navigation'
export const runtime = 'edge'
export async function POST(request: Request) {
try {
const session = await auth()
if (!session?.user?.accessToken) {
return new Response('Unauthorized', { status: 401 })
}
const { sourceIndex, destinationIndex, itemId } = await request.json()
// 获取当前导航数据
const data = await getFileContent('navsphere/content/navigation.json') as NavigationData
// 确保导航项存在
if (!data.navigationItems || !Array.isArray(data.navigationItems)) {
throw new Error('无效的导航数据')
}
// 创建新的导航项数组副本
const updatedItems = [...data.navigationItems]
// 找到要移动的项目
const [movedItem] = updatedItems.splice(sourceIndex, 1)
// 将项目插入到新位置
updatedItems.splice(destinationIndex, 0, movedItem)
// 更新数据
data.navigationItems = updatedItems
// 提交更改到 GitHub
await commitFile(
'navsphere/content/navigation.json',
JSON.stringify(data, null, 2),
`重新排序导航项 - ${new Date().toISOString()}`,
session.user.accessToken
)
return NextResponse.json(data.navigationItems, { status: 200 })
} catch (error) {
console.error('重新排序导航项错误:', error)
return NextResponse.json({
error: '重新排序导航项失败',
details: (error as Error).message
}, { status: 500 })
}
}

View File

@@ -0,0 +1,61 @@
import { NextResponse } from 'next/server'
import { auth } from '@/lib/auth'
import { commitFile, getFileContent } from '@/lib/github'
export const runtime = 'edge'
export async function POST() {
try {
const session = await auth()
if (!session?.user?.accessToken) {
return new Response('Unauthorized', { status: 401 })
}
// 检查默认数据文件是否存在
try {
const defaultData = await getFileContent('navsphere/content/navigation-default.json')
// 验证默认数据格式
if (!defaultData || typeof defaultData !== 'object' || !defaultData.navigationItems) {
return NextResponse.json(
{
error: 'Invalid default data format',
details: 'navigation-default.json does not contain valid navigation data'
},
{ status: 400 }
)
}
// 将默认数据写入到navigation.json
await commitFile(
'navsphere/content/navigation.json',
JSON.stringify(defaultData, null, 2),
'Restore navigation data to default',
session.user.accessToken
)
return NextResponse.json(defaultData)
} catch (fileError) {
// 检查是否是文件不存在的错误
if ((fileError as Error).message.includes('404') || (fileError as Error).message.includes('not found')) {
return NextResponse.json(
{
error: 'Default data file not found',
details: 'navigation-default.json file does not exist in the repository'
},
{ status: 404 }
)
}
throw fileError
}
} catch (error) {
console.error('Failed to restore navigation data:', error)
return NextResponse.json(
{
error: 'Failed to restore navigation data',
details: (error as Error).message
},
{ status: 500 }
)
}
}

106
app/api/navigation/route.ts Normal file
View File

@@ -0,0 +1,106 @@
import { NextResponse } from 'next/server'
import { auth } from '@/lib/auth'
import { commitFile, getFileContent } from '@/lib/github'
import type { NavigationData, NavigationItem } from '@/types/navigation'
export const runtime = 'edge'
export async function GET() {
try {
const data = await getFileContent('navsphere/content/navigation.json')
return NextResponse.json(data)
} catch (error) {
console.error('Failed to fetch navigation data:', error)
// 返回默认数据结构
return NextResponse.json({
navigationItems: []
})
}
}
async function validateAndSaveNavigationData(data: any, accessToken: string) {
// 详细的数据结构验证和日志
console.log('Received navigation data:', JSON.stringify(data, null, 2))
// 严格验证数据结构
if (!data || typeof data !== 'object') {
console.error('Invalid data: not an object')
throw new Error('Invalid navigation data: not an object')
}
if (!('navigationItems' in data)) {
console.error('Missing navigationItems key')
throw new Error('Invalid navigation data: missing navigationItems')
}
if (!Array.isArray(data.navigationItems)) {
console.error('navigationItems is not an array', typeof data.navigationItems)
throw new Error('Invalid navigation data: navigationItems must be an array')
}
// 额外的数据验证
const invalidItems = data.navigationItems.filter((item: NavigationItem) =>
!item.id ||
!item.title ||
(item.items && !Array.isArray(item.items)) ||
(item.subCategories && !Array.isArray(item.subCategories))
)
if (invalidItems.length > 0) {
console.error('Invalid navigation items:', invalidItems)
throw new Error('Invalid navigation data: some items are malformed')
}
await commitFile(
'navsphere/content/navigation.json',
JSON.stringify(data, null, 2),
'Update navigation data',
accessToken
)
}
export async function POST(request: Request) {
try {
const session = await auth()
if (!session?.user?.accessToken) {
return new Response('Unauthorized', { status: 401 })
}
const data = await request.json()
await validateAndSaveNavigationData(data, session.user.accessToken)
return NextResponse.json({ success: true })
} catch (error) {
console.error('Failed to save navigation data:', error)
return NextResponse.json(
{
error: 'Failed to save navigation data',
details: (error as Error).message
},
{ status: 500 }
)
}
}
export async function PUT(request: Request) {
try {
const session = await auth()
if (!session?.user?.accessToken) {
return new Response('Unauthorized', { status: 401 })
}
const data = await request.json()
await validateAndSaveNavigationData(data, session.user.accessToken)
return NextResponse.json({ success: true })
} catch (error) {
console.error('Failed to update navigation data:', error)
return NextResponse.json(
{
error: 'Failed to update navigation data',
details: (error as Error).message
},
{ status: 500 }
)
}
}