Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
67a62c1
feat(RepositoryCard): 添加多语言支持按钮标题
Apr 14, 2026
d439362
feat(settings): 添加设置模块及相关面板组件
Apr 14, 2026
a125de6
feat(设置): 新增数据管理面板功能
Apr 14, 2026
033ce97
feat(侧边栏): 添加可折叠侧边栏功能并实现返回顶部按钮
Apr 14, 2026
1cb6843
fix
Apr 14, 2026
500b1d6
fix(accessibility): 为按钮添加aria-label属性提升可访问性
Apr 14, 2026
7b08f43
feat(ui): 优化移动端设置面板的标签导航体验
Apr 15, 2026
46a14f4
fix(SettingsPanel): 为标签导航添加ARIA属性以提升可访问性
Apr 15, 2026
25b77d0
feat: 添加批量操作功能及预设资源筛选器
Apr 15, 2026
db8dce4
feat: 添加仓库卡片点击查看 README 功能
Apr 15, 2026
c8d3c34
feat: 优化组件可访问性与代码结构
Apr 15, 2026
ff62ecf
feat: 优化批量操作工具栏和分类侧边栏交互
Apr 15, 2026
332f0a9
Merge branch 'main' of https://github.com/SummerRay160/GithubStarsMan…
Apr 15, 2026
2aa1cee
fix: 解决合并冲突并移除冲突标记
Apr 15, 2026
e0c3355
feat: 增加rehype-sanitize插件并改进无障碍功能
Apr 15, 2026
448285c
fix(组件): 修复代码块渲染和侧边栏定时器内存泄漏问题
Apr 15, 2026
faf6097
feat(分类管理): 新增分类排序功能及侧边栏折叠优化
Apr 15, 2026
a5a6a42
ci(workflow): 更新 Node.js 版本至 22
Apr 15, 2026
dbff152
feat: 添加批量取消订阅功能并优化性能
Apr 15, 2026
4339b4c
feat: 添加浏览器兼容性支持并优化分类管理功能
Apr 16, 2026
07dd2e5
chore: 更新依赖包版本及添加新依赖
Apr 16, 2026
dd11b42
refactor: 优化代码结构和类型安全
Apr 16, 2026
072d9e0
feat: 优化性能并修复多个问题
Apr 17, 2026
5eafb9f
Merge branch 'AmintaCCCP:main' into main
SummerRay160 Apr 18, 2026
2f6c140
feat: 添加AI配置面板测试连接功能并增强提示词管理
Apr 18, 2026
5844fa8
feat(响应式布局): 改进移动端和桌面端的响应式设计
Apr 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 13 additions & 1 deletion server/src/db/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,11 @@ export function initializeSchema(db: Database.Database): void {
CREATE TABLE IF NOT EXISTS categories (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
description TEXT,
icon TEXT NOT NULL DEFAULT '📁',
keywords TEXT,
color TEXT,
sort_order INTEGER DEFAULT 0,
is_custom INTEGER DEFAULT 1
);

Expand Down Expand Up @@ -93,7 +96,10 @@ export function initializeSchema(db: Database.Database): void {
CREATE TABLE IF NOT EXISTS asset_filters (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
keywords TEXT
description TEXT,
keywords TEXT,
platform TEXT,
sort_order INTEGER DEFAULT 0
);

CREATE TABLE IF NOT EXISTS settings (
Expand All @@ -106,4 +112,10 @@ export function initializeSchema(db: Database.Database): void {
addColumnIfMissing(db, 'repositories', 'category_locked', 'INTEGER DEFAULT 0');
addColumnIfMissing(db, 'releases', 'zipball_url', 'TEXT');
addColumnIfMissing(db, 'releases', 'tarball_url', 'TEXT');
addColumnIfMissing(db, 'categories', 'description', 'TEXT');
addColumnIfMissing(db, 'categories', 'color', 'TEXT');
addColumnIfMissing(db, 'categories', 'sort_order', 'INTEGER DEFAULT 0');
addColumnIfMissing(db, 'asset_filters', 'description', 'TEXT');
addColumnIfMissing(db, 'asset_filters', 'platform', 'TEXT');
addColumnIfMissing(db, 'asset_filters', 'sort_order', 'INTEGER DEFAULT 0');
}
13 changes: 13 additions & 0 deletions server/src/routes/proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ function buildApiUrl(baseUrl: string, pathWithVersion: string): string {
const base = new URL(baseUrlWithSlash);
const basePath = base.pathname.replace(/\/$/, '');

// 检测 baseUrl 是否已经以任何版本号结尾(v1, v2, v3, v1beta, v1alpha 等)
// 这样可以兼容火山引擎(/v3)、OpenAI(/v1)、Gemini(/v1beta)等不同版本号
const anyVersionPattern = /\/v\d+(?:beta|alpha)?$/;
const hasVersionInBase = anyVersionPattern.test(basePath);

if (hasVersionInBase) {
// baseUrl 已包含版本号,只补全端点路径(去掉版本号部分)
const endpointPath = pathWithVersion.includes('/')
? pathWithVersion.split('/').slice(1).join('/')
: pathWithVersion;
return new URL(endpointPath, baseUrlWithSlash).toString();
}

if (versionPrefix) {
const versionRe = new RegExp(`/${versionPrefix}$`);
if (versionRe.test(basePath) && pathWithVersion.startsWith(`${versionPrefix}/`)) {
Expand Down
25 changes: 25 additions & 0 deletions server/src/routes/releases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,4 +175,29 @@ router.post('/api/releases/mark-all-read', (_req, res) => {
}
});

// DELETE /api/releases/:id
router.delete('/api/releases/:id', (req, res) => {
try {
const db = getDb();
const id = parseInt(req.params.id);

if (isNaN(id) || id <= 0) {
res.status(400).json({ error: 'Valid release id required', code: 'INVALID_RELEASE_ID' });
Comment thread
SummerRay160 marked this conversation as resolved.
return;
}

const result = db.prepare('DELETE FROM releases WHERE id = ?').run(id);

if (result.changes === 0) {
res.status(404).json({ error: 'Release not found', code: 'RELEASE_NOT_FOUND' });
return;
}

res.json({ deleted: true, id });
} catch (err) {
console.error('DELETE /api/releases/:id error:', err);
res.status(500).json({ error: 'Failed to delete release', code: 'DELETE_RELEASE_FAILED' });
}
});

export default router;
41 changes: 41 additions & 0 deletions server/src/routes/repositories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,4 +241,45 @@ router.patch('/api/repositories/:id', (req, res) => {
}
});

// DELETE /api/repositories/:id
router.delete('/api/repositories/:id', (req, res) => {
try {
const db = getDb();
const id = parseInt(req.params.id);

if (isNaN(id) || id <= 0) {
res.status(400).json({ error: 'Valid repository id required', code: 'INVALID_REPOSITORY_ID' });
Comment thread
SummerRay160 marked this conversation as resolved.
return;
}

const deleteReleases = db.prepare('DELETE FROM releases WHERE repo_id = ?');
const deleteRepo = db.prepare('DELETE FROM repositories WHERE id = ?');

const deleteAll = db.transaction(() => {
const releaseResult = deleteReleases.run(id);
const repoResult = deleteRepo.run(id);

if (repoResult.changes === 0) {
throw new Error('Repository not found or already deleted');
}
Comment thread
SummerRay160 marked this conversation as resolved.

return {
releasesDeleted: releaseResult.changes,
repoDeleted: repoResult.changes
};
});

const result = deleteAll();

res.json({
deleted: true,
id,
releasesDeleted: result.releasesDeleted
});
} catch (err) {
console.error('DELETE /api/repositories/:id error:', err);
res.status(500).json({ error: 'Failed to delete repository', code: 'DELETE_REPOSITORY_FAILED' });
}
});

export default router;
155 changes: 155 additions & 0 deletions server/src/types/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
export interface RepositoryRow {
id: number;
name: string;
full_name: string;
description: string | null;
html_url: string;
stargazers_count: number;
language: string | null;
created_at: string | null;
updated_at: string | null;
pushed_at: string | null;
starred_at: string | null;
owner_login: string;
owner_avatar_url: string | null;
topics: string;
ai_summary: string | null;
ai_tags: string;
ai_platforms: string;
analyzed_at: string | null;
analysis_failed: number;
custom_description: string | null;
custom_tags: string;
Comment thread
SummerRay160 marked this conversation as resolved.
custom_category: string | null;
category_locked: number;
last_edited: string | null;
subscribed_to_releases: number;
}

export interface ReleaseRow {
id: number;
tag_name: string;
name: string | null;
body: string | null;
published_at: string | null;
html_url: string | null;
assets: string;
repo_id: number;
repo_full_name: string;
repo_name: string;
prerelease: number;
draft: number;
is_read: number;
zipball_url: string | null;
tarball_url: string | null;
}

export interface CategoryRow {
id: string;
name: string;
description: string | null;
icon: string;
keywords: string;
color: string | null;
sort_order: number;
is_custom: number;
}

export interface AIConfigRow {
id: string;
name: string;
api_type: string;
base_url: string;
api_key_encrypted: string;
model: string;
is_active: number;
custom_prompt: string | null;
use_custom_prompt: number;
concurrency: number;
reasoning_effort: string | null;
}

export interface WebDAVConfigRow {
id: string;
name: string;
url: string;
username: string;
password_encrypted: string;
path: string;
is_active: number;
}

export interface AssetFilterRow {
id: string;
name: string;
description: string | null;
keywords: string;
platform: string | null;
sort_order: number;
}

export interface SettingsRow {
key: string;
value: string | null;
}

export interface ApiResponse<T = unknown> {
data?: T;
error?: string;
code?: string;
}

export interface PaginatedResponse<T> {
items: T[];
total: number;
page: number;
limit: number;
}

export interface SyncRepositoriesRequest {
repositories: Record<string, unknown>[];
isFullSync?: boolean;
}

export interface SyncReleasesRequest {
releases: Record<string, unknown>[];
}

export interface SyncAIConfigsRequest {
configs: Array<{
id: string;
name: string;
apiType?: string;
baseUrl: string;
apiKey: string;
model: string;
isActive: boolean;
customPrompt?: string;
useCustomPrompt?: boolean;
concurrency?: number;
reasoningEffort?: string;
}>;
}

export interface SyncWebDAVConfigsRequest {
configs: Array<{
id: string;
name: string;
url: string;
username: string;
password: string;
path: string;
isActive: boolean;
}>;
}

export interface SyncSettingsRequest {
activeAIConfig?: string | null;
activeWebDAVConfig?: string | null;
hiddenDefaultCategoryIds?: string[];
categoryOrder?: string[];
customCategories?: unknown[];
assetFilters?: unknown[];
collapsedSidebarCategoryCount?: number;
github_token?: string;
}
Loading
Loading