初始化 Gitea 项目结构,添加核心 API 封装和相关配置文件
This commit is contained in:
19
.cnb.yml
Normal file
19
.cnb.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
# .cnb.yml
|
||||
include:
|
||||
- https://cnb.cool/kevisual/cnb/-/blob/main/.cnb/template.yml
|
||||
|
||||
.common_env: &common_env
|
||||
env:
|
||||
USERNAME: root
|
||||
imports:
|
||||
- https://cnb.cool/kevisual/env/-/blob/main/.env.development
|
||||
|
||||
$:
|
||||
vscode:
|
||||
- docker:
|
||||
image: docker.cnb.cool/kevisual/dev-env:latest
|
||||
services:
|
||||
- vscode
|
||||
- docker
|
||||
imports: !reference [.common_env, imports]
|
||||
stages: !reference [.dev_template, stages]
|
||||
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
.env*
|
||||
!.env*example
|
||||
|
||||
node_modules
|
||||
.pnpm-store
|
||||
|
||||
dist
|
||||
|
||||
storage
|
||||
3
.npmrc
Normal file
3
.npmrc
Normal file
@@ -0,0 +1,3 @@
|
||||
//npm.xiongxiao.me/:_authToken=${ME_NPM_TOKEN}
|
||||
//npm.cnb.cool/kevisual/registry/-/packages/:_authToken=${CNB_API_KEY}
|
||||
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
|
||||
4
bun.config.ts
Normal file
4
bun.config.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { buildWithBun } from '@kevisual/code-builder';
|
||||
|
||||
await buildWithBun({ naming: 'app', entry: 'src/index.ts', dts: true, clean: true });
|
||||
|
||||
18
bun.lock
Normal file
18
bun.lock
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "@kevisual/gitea",
|
||||
"devDependencies": {
|
||||
"@kevisual/code-builder": "^0.0.6",
|
||||
"@kevisual/context": "^0.0.8",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@kevisual/code-builder": ["@kevisual/code-builder@0.0.6", "", { "bin": { "code-builder": "bin/code.js", "builder": "bin/code.js" } }, "sha512-0aqATB31/yw4k4s5/xKnfr4DKbUnx8e3Z3BmKbiXTrc+CqWiWTdlGe9bKI9dZ2Df+xNp6g11W4xM2NICNyyCCw=="],
|
||||
|
||||
"@kevisual/context": ["@kevisual/context@0.0.8", "", {}, "sha512-DTJpyHI34NE76B7g6f+QlIqiCCyqI2qkBMQE736dzeRDGxOjnbe2iQY9W+Rt2PE6kmymM3qyOmSfNovyWyWrkA=="],
|
||||
}
|
||||
}
|
||||
23
package.json
Normal file
23
package.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "@kevisual/gitea",
|
||||
"version": "0.0.2",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"build": "bun run bun.config.ts"
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"dist"
|
||||
],
|
||||
"keywords": [],
|
||||
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@kevisual/code-builder": "^0.0.6",
|
||||
"@kevisual/context": "^0.0.8"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
152
src/core/core.ts
Normal file
152
src/core/core.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
export type CNBCoreOptions<T = {}> = {
|
||||
token: string;
|
||||
gitea?: GiteaCore;
|
||||
baseURL?: string;
|
||||
cors?: {
|
||||
baseUrl?: string
|
||||
}
|
||||
} & T;
|
||||
|
||||
export type RequestOptions = {
|
||||
url?: string;
|
||||
method?: string;
|
||||
data?: Record<string, any>;
|
||||
body?: any;
|
||||
params?: Record<string, any>;
|
||||
headers?: Record<string, any>;
|
||||
useOrigin?: boolean;
|
||||
};
|
||||
const API_BASER_URL = 'https://git.xiongxiao.me'
|
||||
export class GiteaCore {
|
||||
baseURL = API_BASER_URL;
|
||||
public token: string;
|
||||
public cookie?: string;
|
||||
isCors: boolean;
|
||||
constructor(options: CNBCoreOptions) {
|
||||
this.token = options.token;
|
||||
if (options?.gitea) {
|
||||
if (!options.token) {
|
||||
this.token = options.gitea.token;
|
||||
}
|
||||
|
||||
}
|
||||
const baseURL = options.baseURL || API_BASER_URL;
|
||||
if (options?.cors?.baseUrl) {
|
||||
this.baseURL = options.cors.baseUrl + '/' + baseURL.replace('https://', '');
|
||||
} else {
|
||||
this.baseURL = baseURL;
|
||||
}
|
||||
this.isCors = !!options?.cors?.baseUrl;
|
||||
}
|
||||
|
||||
async request({ url, method = 'GET', data, params, headers, body, useOrigin }: RequestOptions): Promise<any> {
|
||||
const defaultHeaders: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
// 'Accept': 'application/json, application/vnd.cnb.api+json, application/vnd.cnb.web+json',
|
||||
'Accept': 'application/json',
|
||||
};
|
||||
if (this.token) {
|
||||
defaultHeaders['Authorization'] = `Bearer ${this.token}`;
|
||||
}
|
||||
if (params) {
|
||||
const queryString = new URLSearchParams(params).toString();
|
||||
url += `?${queryString}`;
|
||||
defaultHeaders['Accept'] = 'application/json';
|
||||
}
|
||||
const _headers = { ...defaultHeaders, ...headers };
|
||||
let _body = undefined;
|
||||
if (data) {
|
||||
_body = JSON.stringify(data);
|
||||
}
|
||||
if (body) {
|
||||
_body = body;
|
||||
}
|
||||
if (!_headers.Authorization) {
|
||||
delete _headers.Authorization;
|
||||
}
|
||||
console.log('Request URL:', url, data, _headers);
|
||||
const response = await fetch(url || '', {
|
||||
method,
|
||||
headers: _headers,
|
||||
body: _body,
|
||||
});
|
||||
const res = (data: any, message?: string, code?: number) => {
|
||||
if (useOrigin) {
|
||||
return data;
|
||||
}
|
||||
return {
|
||||
code: code ?? 200,
|
||||
message: message || 'success',
|
||||
data,
|
||||
};
|
||||
}
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
if (useOrigin)
|
||||
throw new Error(`Request failed with status ${response.status}: ${errorText}`);
|
||||
return res(null, `Request failed with status ${response.status}: ${errorText}`, response.status);
|
||||
}
|
||||
|
||||
const contentType = response.headers.get('Content-Type');
|
||||
if (contentType && contentType.includes('application/json')) {
|
||||
const values = await response.json();
|
||||
return res(values);
|
||||
} else {
|
||||
const text = await response.text();
|
||||
return res(text);
|
||||
}
|
||||
}
|
||||
makeUrl(url: string = '', version = '/api/v1'): string {
|
||||
const baseUrl = this.baseURL + version;
|
||||
if (!url) return baseUrl;
|
||||
if (url && url.startsWith('http')) {
|
||||
return url;
|
||||
}
|
||||
if (url.startsWith('/')) {
|
||||
return baseUrl + url;
|
||||
}
|
||||
return baseUrl + '/' + url;
|
||||
}
|
||||
get<T = any>({ url, ...REST }: RequestOptions): Promise<T> {
|
||||
const fullUrl = this.makeUrl(url);
|
||||
return this.request({ url: fullUrl, method: 'GET', ...REST });
|
||||
}
|
||||
post<T = any>({ url, ...REST }: RequestOptions): Promise<T> {
|
||||
const fullUrl = this.makeUrl(url);
|
||||
return this.request({ url: fullUrl, method: 'POST', ...REST });
|
||||
}
|
||||
put<T = any>({ url, ...REST }: RequestOptions): Promise<T> {
|
||||
const fullUrl = this.makeUrl(url);
|
||||
return this.request({ url: fullUrl, method: 'PUT', ...REST });
|
||||
}
|
||||
delete<T = any>({ url, ...REST }: RequestOptions): Promise<T> {
|
||||
const fullUrl = this.makeUrl(url);
|
||||
return this.request({ url: fullUrl, method: 'DELETE', ...REST });
|
||||
}
|
||||
patch<T = any>({ url, ...REST }: RequestOptions): Promise<T> {
|
||||
const fullUrl = this.makeUrl(url);
|
||||
return this.request({ url: fullUrl, method: 'PATCH', ...REST });
|
||||
}
|
||||
/**
|
||||
* 通过 PUT 请求上传文件内容
|
||||
* @param data 包含 URL、token 和文件内容
|
||||
* @returns 上传结果
|
||||
*/
|
||||
async putFile(data: { url: string, token: string, content: string | Buffer }): Promise<any> {
|
||||
return this.request({
|
||||
url: data.url,
|
||||
method: 'PUT',
|
||||
body: data.content,
|
||||
headers: {
|
||||
'Authorization': `Bearer ${data.token}`,
|
||||
'Content-Type': 'application/octet-stream'
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export type Result<T = any> = {
|
||||
code: number;
|
||||
message: string;
|
||||
data: T
|
||||
};
|
||||
29
src/index.ts
Normal file
29
src/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { GiteaRepo } from './repo';
|
||||
import { GiteaIssue } from './issue';
|
||||
import { GiteaUser } from './user';
|
||||
import { GiteaOrg } from './org';
|
||||
import { GiteaCore, CNBCoreOptions, Result } from './core/core';
|
||||
|
||||
export class Gitea {
|
||||
repo: GiteaRepo;
|
||||
issue: GiteaIssue;
|
||||
user: GiteaUser;
|
||||
org: GiteaOrg;
|
||||
constructor(options: CNBCoreOptions) {
|
||||
const core = new GiteaCore(options);
|
||||
this.repo = new GiteaRepo(core);
|
||||
this.issue = new GiteaIssue(core);
|
||||
this.user = new GiteaUser(core);
|
||||
this.org = new GiteaOrg(core);
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
GiteaRepo,
|
||||
GiteaIssue,
|
||||
GiteaUser,
|
||||
GiteaOrg,
|
||||
GiteaCore,
|
||||
CNBCoreOptions,
|
||||
Result,
|
||||
}
|
||||
172
src/issue/index.ts
Normal file
172
src/issue/index.ts
Normal file
@@ -0,0 +1,172 @@
|
||||
import { GiteaCore } from "../core/core";
|
||||
|
||||
export interface CreateIssueOptions {
|
||||
title: string;
|
||||
body?: string;
|
||||
assignee?: string;
|
||||
milestone?: number;
|
||||
labels?: number[];
|
||||
due_date?: string;
|
||||
}
|
||||
|
||||
export interface UpdateIssueOptions {
|
||||
title?: string;
|
||||
body?: string;
|
||||
assignee?: string;
|
||||
milestone?: number | null;
|
||||
state?: 'open' | 'closed';
|
||||
due_date?: string | null;
|
||||
}
|
||||
|
||||
export interface CreateCommentOptions {
|
||||
body: string;
|
||||
attachments?: Array<{
|
||||
name: string;
|
||||
url: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
export class GiteaIssue extends GiteaCore {
|
||||
/**
|
||||
* 获取仓库问题列表
|
||||
*/
|
||||
async listIssues(owner: string, repo: string, options?: {
|
||||
page?: number;
|
||||
limit?: number;
|
||||
state?: 'open' | 'closed' | 'all';
|
||||
labels?: string;
|
||||
milestone?: number;
|
||||
assignee?: string;
|
||||
since?: string;
|
||||
before?: string;
|
||||
}) {
|
||||
const url = this.makeUrl(`/repos/${owner}/${repo}/issues`);
|
||||
return this.request({ url, method: 'GET', params: options });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单个问题
|
||||
*/
|
||||
async getIssue(owner: string, repo: string, index: number) {
|
||||
const url = this.makeUrl(`/repos/${owner}/${repo}/issues/${index}`);
|
||||
return this.request({ url });
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建问题
|
||||
*/
|
||||
async createIssue(owner: string, repo: string, data: CreateIssueOptions) {
|
||||
const url = this.makeUrl(`/repos/${owner}/${repo}/issues`);
|
||||
return this.request({ url, method: 'POST', data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新问题
|
||||
*/
|
||||
async updateIssue(owner: string, repo: string, index: number, data: UpdateIssueOptions) {
|
||||
const url = this.makeUrl(`/repos/${owner}/${repo}/issues/${index}`);
|
||||
return this.request({ url, method: 'PATCH', data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除问题
|
||||
*/
|
||||
async deleteIssue(owner: string, repo: string, index: number) {
|
||||
const url = this.makeUrl(`/repos/${owner}/${repo}/issues/${index}`);
|
||||
return this.request({ url, method: 'DELETE' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取问题的评论列表
|
||||
*/
|
||||
async listComments(owner: string, repo: string, index: number, options?: {
|
||||
page?: number;
|
||||
limit?: number;
|
||||
since?: string;
|
||||
}) {
|
||||
const url = this.makeUrl(`/repos/${owner}/${repo}/issues/${index}/comments`);
|
||||
return this.request({ url, method: 'GET', params: options });
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建评论
|
||||
*/
|
||||
async createComment(owner: string, repo: string, index: number, data: CreateCommentOptions) {
|
||||
const url = this.makeUrl(`/repos/${owner}/${repo}/issues/${index}/comments`);
|
||||
return this.request({ url, method: 'POST', data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新评论
|
||||
*/
|
||||
async updateComment(owner: string, repo: string, commentId: number, body: string) {
|
||||
const url = this.makeUrl(`/repos/${owner}/${repo}/issues/comments/${commentId}`);
|
||||
return this.request({ url, method: 'PATCH', data: { body } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除评论
|
||||
*/
|
||||
async deleteComment(owner: string, repo: string, commentId: number) {
|
||||
const url = this.makeUrl(`/repos/${owner}/${repo}/issues/comments/${commentId}`);
|
||||
return this.request({ url, method: 'DELETE' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取标签列表
|
||||
*/
|
||||
async listLabels(owner: string, repo: string) {
|
||||
const url = this.makeUrl(`/repos/${owner}/${repo}/labels`);
|
||||
return this.request({ url, method: 'GET' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建标签
|
||||
*/
|
||||
async createLabel(owner: string, repo: string, data: {
|
||||
name: string;
|
||||
color: string;
|
||||
description?: string;
|
||||
}) {
|
||||
const url = this.makeUrl(`/repos/${owner}/${repo}/labels`);
|
||||
return this.request({ url, method: 'POST', data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除标签
|
||||
*/
|
||||
async deleteLabel(owner: string, repo: string, id: number) {
|
||||
const url = this.makeUrl(`/repos/${owner}/${repo}/labels/${id}`);
|
||||
return this.request({ url, method: 'DELETE' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取里程碑列表
|
||||
*/
|
||||
async listMilestones(owner: string, repo: string, options?: {
|
||||
state?: 'open' | 'closed' | 'all';
|
||||
}) {
|
||||
const url = this.makeUrl(`/repos/${owner}/${repo}/milestones`);
|
||||
return this.request({ url, method: 'GET', params: options });
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建里程碑
|
||||
*/
|
||||
async createMilestone(owner: string, repo: string, data: {
|
||||
title: string;
|
||||
description?: string;
|
||||
due_date?: string;
|
||||
}) {
|
||||
const url = this.makeUrl(`/repos/${owner}/${repo}/milestones`);
|
||||
return this.request({ url, method: 'POST', data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭里程碑
|
||||
*/
|
||||
async closeMilestone(owner: string, repo: string, id: number) {
|
||||
const url = this.makeUrl(`/repos/${owner}/${repo}/milestones/${id}`);
|
||||
return this.request({ url, method: 'DELETE' });
|
||||
}
|
||||
}
|
||||
227
src/org/index.ts
Normal file
227
src/org/index.ts
Normal file
@@ -0,0 +1,227 @@
|
||||
import { GiteaCore } from "../core/core";
|
||||
|
||||
export interface CreateOrgOptions {
|
||||
username: string;
|
||||
full_name?: string;
|
||||
description?: string;
|
||||
website?: string;
|
||||
location?: string;
|
||||
visibility?: 'public' | 'limited' | 'private';
|
||||
}
|
||||
|
||||
export interface UpdateOrgOptions {
|
||||
full_name?: string;
|
||||
description?: string;
|
||||
website?: string;
|
||||
location?: string;
|
||||
visibility?: 'public' | 'limited' | 'private';
|
||||
}
|
||||
|
||||
export interface CreateTeamOptions {
|
||||
name: string;
|
||||
description?: string;
|
||||
permission?: 'read' | 'write' | 'admin';
|
||||
repositories?: string[];
|
||||
includes_all_repositories?: boolean;
|
||||
}
|
||||
|
||||
export interface UpdateTeamOptions {
|
||||
name?: string;
|
||||
description?: string;
|
||||
permission?: 'read' | 'write' | 'admin';
|
||||
}
|
||||
|
||||
export class GiteaOrg extends GiteaCore {
|
||||
/**
|
||||
* 获取当前用户所属组织列表
|
||||
*/
|
||||
async listOrgs(options?: {
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}) {
|
||||
const url = this.makeUrl(`/user/orgs`);
|
||||
return this.request({ url, method: 'GET', params: options });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定组织信息
|
||||
*/
|
||||
async getOrg(org: string) {
|
||||
const url = this.makeUrl(`/orgs/${org}`);
|
||||
return this.request({ url });
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建组织
|
||||
*/
|
||||
async createOrg(data: CreateOrgOptions) {
|
||||
const url = this.makeUrl(`/admin/users/${data.username}/orgs`);
|
||||
return this.request({ url, method: 'POST', data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新组织信息
|
||||
*/
|
||||
async updateOrg(org: string, data: UpdateOrgOptions) {
|
||||
const url = this.makeUrl(`/orgs/${org}`);
|
||||
return this.request({ url, method: 'PATCH', data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除组织
|
||||
*/
|
||||
async deleteOrg(org: string) {
|
||||
const url = this.makeUrl(`/orgs/${org}`);
|
||||
return this.request({ url, method: 'DELETE' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组织成员列表
|
||||
*/
|
||||
async listMembers(org: string, options?: {
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}) {
|
||||
const url = this.makeUrl(`/orgs/${org}/members`);
|
||||
return this.request({ url, method: 'GET', params: options });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组织成员信息
|
||||
*/
|
||||
async getMember(org: string, username: string) {
|
||||
const url = this.makeUrl(`/orgs/${org}/members/${username}`);
|
||||
return this.request({ url });
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加组织成员
|
||||
*/
|
||||
async addMember(org: string, username: string) {
|
||||
const url = this.makeUrl(`/orgs/${org}/members/${username}`);
|
||||
return this.request({ url, method: 'PUT' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除组织成员
|
||||
*/
|
||||
async removeMember(org: string, username: string) {
|
||||
const url = this.makeUrl(`/orgs/${org}/members/${username}`);
|
||||
return this.request({ url, method: 'DELETE' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组织仓库列表
|
||||
*/
|
||||
async listRepos(org: string, options?: {
|
||||
page?: number;
|
||||
limit?: number;
|
||||
type?: 'all' | 'fork' | 'source' | 'mirror';
|
||||
}) {
|
||||
const url = this.makeUrl(`/orgs/${org}/repos`);
|
||||
return this.request({ url, method: 'GET', params: options });
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建组织仓库
|
||||
*/
|
||||
async createRepo(org: string, data: {
|
||||
name: string;
|
||||
description?: string;
|
||||
private?: boolean;
|
||||
gitignores?: string;
|
||||
license?: string;
|
||||
readme?: string;
|
||||
}) {
|
||||
const url = this.makeUrl(`/orgs/${org}/repos`);
|
||||
return this.request({ url, method: 'POST', data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取团队列表
|
||||
*/
|
||||
async listTeams(org: string) {
|
||||
const url = this.makeUrl(`/orgs/${org}/teams`);
|
||||
return this.request({ url, method: 'GET' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取团队信息
|
||||
*/
|
||||
async getTeam(org: string, teamId: number) {
|
||||
const url = this.makeUrl(`/orgs/${org}/teams/${teamId}`);
|
||||
return this.request({ url });
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建团队
|
||||
*/
|
||||
async createTeam(org: string, data: CreateTeamOptions) {
|
||||
const url = this.makeUrl(`/orgs/${org}/teams`);
|
||||
return this.request({ url, method: 'POST', data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新团队信息
|
||||
*/
|
||||
async updateTeam(org: string, teamId: number, data: UpdateTeamOptions) {
|
||||
const url = this.makeUrl(`/orgs/${org}/teams/${teamId}`);
|
||||
return this.request({ url, method: 'PATCH', data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除团队
|
||||
*/
|
||||
async deleteTeam(org: string, teamId: number) {
|
||||
const url = this.makeUrl(`/orgs/${org}/teams/${teamId}`);
|
||||
return this.request({ url, method: 'DELETE' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取团队成员列表
|
||||
*/
|
||||
async listTeamMembers(org: string, teamId: number) {
|
||||
const url = this.makeUrl(`/orgs/${org}/teams/${teamId}/members`);
|
||||
return this.request({ url, method: 'GET' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加团队成员
|
||||
*/
|
||||
async addTeamMember(org: string, teamId: number, username: string) {
|
||||
const url = this.makeUrl(`/orgs/${org}/teams/${teamId}/members/${username}`);
|
||||
return this.request({ url, method: 'PUT' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除团队成员
|
||||
*/
|
||||
async removeTeamMember(org: string, teamId: number, username: string) {
|
||||
const url = this.makeUrl(`/orgs/${org}/teams/${teamId}/members/${username}`);
|
||||
return this.request({ url, method: 'DELETE' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取团队仓库列表
|
||||
*/
|
||||
async listTeamRepos(org: string, teamId: number) {
|
||||
const url = this.makeUrl(`/orgs/${org}/teams/${teamId}/repos`);
|
||||
return this.request({ url, method: 'GET' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加团队仓库
|
||||
*/
|
||||
async addTeamRepo(org: string, teamId: number, repo: string) {
|
||||
const url = this.makeUrl(`/orgs/${org}/teams/${teamId}/repos/${repo}`);
|
||||
return this.request({ url, method: 'PUT' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除团队仓库
|
||||
*/
|
||||
async removeTeamRepo(org: string, teamId: number, repo: string) {
|
||||
const url = this.makeUrl(`/orgs/${org}/teams/${teamId}/repos/${repo}`);
|
||||
return this.request({ url, method: 'DELETE' });
|
||||
}
|
||||
}
|
||||
132
src/repo/index.ts
Normal file
132
src/repo/index.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import { GiteaCore } from "../core/core";
|
||||
|
||||
export interface CreateRepoOptions {
|
||||
name: string;
|
||||
description?: string;
|
||||
private?: boolean;
|
||||
auto_init?: boolean;
|
||||
gitignores?: string;
|
||||
license?: string;
|
||||
readme?: string;
|
||||
}
|
||||
|
||||
export interface UpdateRepoOptions {
|
||||
name?: string;
|
||||
description?: string;
|
||||
private?: boolean;
|
||||
allow_rebase?: boolean;
|
||||
allow_rebase_explicit?: boolean;
|
||||
allow_merge_commits?: boolean;
|
||||
allow_squash_merge?: boolean;
|
||||
allow_rebase_update?: boolean;
|
||||
default_branch?: string;
|
||||
}
|
||||
|
||||
export class GiteaRepo extends GiteaCore {
|
||||
/**
|
||||
* 获取仓库信息
|
||||
*/
|
||||
async getRepo(owner: string, repo: string) {
|
||||
const url = this.makeUrl(`/repos/${owner}/${repo}`);
|
||||
return this.request({ url });
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建仓库
|
||||
* 格式: name="repo" -> 当前用户
|
||||
* 格式: name="org/repo" -> 组织
|
||||
*/
|
||||
async createRepo(data: CreateRepoOptions) {
|
||||
const name = data.name;
|
||||
const parts = name.split('/');
|
||||
|
||||
if (parts.length === 2) {
|
||||
const [orgOrUser, repoName] = parts;
|
||||
const url = this.makeUrl(`/${orgOrUser}/repos`);
|
||||
return this.request({ url, method: 'POST', data: { ...data, name: repoName } });
|
||||
}
|
||||
|
||||
const url = this.makeUrl(`/user/repos`);
|
||||
return this.request({ url, method: 'POST', data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新仓库
|
||||
*/
|
||||
async updateRepo(owner: string, repo: string, data: UpdateRepoOptions) {
|
||||
const url = this.makeUrl(`/repos/${owner}/${repo}`);
|
||||
return this.request({ url, method: 'PATCH', data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除仓库
|
||||
*/
|
||||
async deleteRepo(owner: string, repo: string) {
|
||||
const url = this.makeUrl(`/repos/${owner}/${repo}`);
|
||||
return this.request({ url, method: 'DELETE' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取仓库列表
|
||||
*/
|
||||
async listRepos(options?: {
|
||||
page?: number;
|
||||
limit?: number;
|
||||
type?: 'all' | 'owner' | 'public' | 'private';
|
||||
}) {
|
||||
const url = this.makeUrl(`/user/repos`);
|
||||
return this.request({ url, method: 'GET', params: options });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组织仓库列表
|
||||
*/
|
||||
async listOrgRepos(org: string, options?: {
|
||||
page?: number;
|
||||
limit?: number;
|
||||
type?: 'all' | 'fork' | 'source' | 'mirror';
|
||||
}) {
|
||||
const url = this.makeUrl(`/orgs/${org}/repos`);
|
||||
return this.request({ url, method: 'GET', params: options });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取仓库内容(文件或目录)
|
||||
*/
|
||||
async getContents(owner: string, repo: string, path: string, options?: {
|
||||
ref?: string;
|
||||
}) {
|
||||
const url = this.makeUrl(`/repos/${owner}/${repo}/contents/${path}`);
|
||||
return this.request({ url, method: 'GET', params: options });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取仓库分支列表
|
||||
*/
|
||||
async listBranches(owner: string, repo: string, options?: {
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}) {
|
||||
const url = this.makeUrl(`/repos/${owner}/${repo}/branches`);
|
||||
return this.request({ url, method: 'GET', params: options });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取仓库标签列表
|
||||
*/
|
||||
async listTags(owner: string, repo: string, options?: {
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}) {
|
||||
const url = this.makeUrl(`/repos/${owner}/${repo}/tags`);
|
||||
return this.request({ url, method: 'GET', params: options });
|
||||
}
|
||||
|
||||
/**
|
||||
* Fork 仓库
|
||||
*/
|
||||
async forkRepo(owner: string, repo: string, organization?: string) {
|
||||
const url = this.makeUrl(`/repos/${owner}/${repo}/forks`);
|
||||
return this.request({ url, method: 'POST', data: { organization } });
|
||||
}
|
||||
}
|
||||
141
src/user/index.ts
Normal file
141
src/user/index.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
import { GiteaCore } from "../core/core";
|
||||
|
||||
export interface UpdateUserOptions {
|
||||
email?: string;
|
||||
full_name?: string;
|
||||
location?: string;
|
||||
website?: string;
|
||||
description?: string;
|
||||
visibility?: 'public' | 'limited' | 'private';
|
||||
}
|
||||
|
||||
export class GiteaUser extends GiteaCore {
|
||||
/**
|
||||
* 获取当前用户信息
|
||||
*/
|
||||
async getCurrentUser() {
|
||||
const url = this.makeUrl(`/user`);
|
||||
return this.request({ url });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定用户信息
|
||||
*/
|
||||
async getUser(username: string) {
|
||||
const url = this.makeUrl(`/users/${username}`);
|
||||
return this.request({ url });
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新当前用户信息
|
||||
*/
|
||||
async updateCurrentUser(data: UpdateUserOptions) {
|
||||
const url = this.makeUrl(`/user`);
|
||||
return this.request({ url, method: 'PATCH', data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户邮箱列表
|
||||
*/
|
||||
async listEmails() {
|
||||
const url = this.makeUrl(`/user/emails`);
|
||||
return this.request({ url, method: 'GET' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加邮箱
|
||||
*/
|
||||
async addEmail(email: string) {
|
||||
const url = this.makeUrl(`/user/emails`);
|
||||
return this.request({ url, method: 'POST', data: { email } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除邮箱
|
||||
*/
|
||||
async deleteEmail(email: string) {
|
||||
const url = this.makeUrl(`/user/emails`);
|
||||
return this.request({ url, method: 'DELETE', data: { email } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户关注者
|
||||
*/
|
||||
async listFollowers(username: string, options?: {
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}) {
|
||||
const url = this.makeUrl(`/users/${username}/followers`);
|
||||
return this.request({ url, method: 'GET', params: options });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户关注的用户列表
|
||||
*/
|
||||
async listFollowing(username: string, options?: {
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}) {
|
||||
const url = this.makeUrl(`/users/${username}/following`);
|
||||
return this.request({ url, method: 'GET', params: options });
|
||||
}
|
||||
|
||||
/**
|
||||
* 关注用户
|
||||
*/
|
||||
async followUser(username: string) {
|
||||
const url = this.makeUrl(`/user/following/${username}`);
|
||||
return this.request({ url, method: 'PUT' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消关注用户
|
||||
*/
|
||||
async unfollowUser(username: string) {
|
||||
const url = this.makeUrl(`/user/following/${username}`);
|
||||
return this.request({ url, method: 'DELETE' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否关注用户
|
||||
*/
|
||||
async checkFollowing(username: string, target: string) {
|
||||
const url = this.makeUrl(`/users/${username}/following/${target}`);
|
||||
return this.request({ url });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户仓库列表
|
||||
*/
|
||||
async listRepos(username: string, options?: {
|
||||
page?: number;
|
||||
limit?: number;
|
||||
type?: 'all' | 'owner' | 'public' | 'private';
|
||||
sort?: 'created' | 'updated' | 'pushed' | 'full_name';
|
||||
}) {
|
||||
const url = this.makeUrl(`/users/${username}/repos`);
|
||||
return this.request({ url, method: 'GET', params: options });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户活动流
|
||||
*/
|
||||
async listActivities(username: string, options?: {
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}) {
|
||||
const url = this.makeUrl(`/users/${username}/activities`);
|
||||
return this.request({ url, method: 'GET', params: options });
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索用户
|
||||
*/
|
||||
async searchUsers(query: string, options?: {
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}) {
|
||||
const url = this.makeUrl(`/users/search`);
|
||||
return this.request({ url, method: 'GET', params: { q: query, ...options } });
|
||||
}
|
||||
}
|
||||
13
test/common.ts
Normal file
13
test/common.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { GiteaRepo } from "../src/repo";
|
||||
import { useKey } from "@kevisual/context";
|
||||
const repo = new GiteaRepo({
|
||||
token: useKey("GITEA_TOKEN"),
|
||||
baseURL: useKey("GITEA_URL"),
|
||||
});
|
||||
|
||||
const createRepo = async () => {
|
||||
const res = await repo.createRepo({ name: "kevisual/test-repo", description: "This is a test repository", private: false });
|
||||
console.log('createRepo', res);
|
||||
};
|
||||
|
||||
createRepo();
|
||||
Reference in New Issue
Block a user