初始化 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