mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-06 23:56:37 +08:00
Merge remote-tracking branch 'origin/main'
# Conflicts: # AGENTS.md # backend/docs/docs.go # backend/docs/swagger.json # backend/docs/swagger.yaml # frontend/components/providers/title-updater.tsx # frontend/messages/fragments/admin.en.json # frontend/messages/fragments/admin.zh-CN.json # frontend/proxy.ts
This commit is contained in:
@@ -212,6 +212,10 @@ export interface TaskExecution {
|
||||
export interface ListTaskExecutionsRequest {
|
||||
status?: TaskExecutionStatus;
|
||||
task_type?: string;
|
||||
/** Prefix match on stored asynq task type (ignored when task_type / task_types is set). */
|
||||
task_type_prefix?: string;
|
||||
/** Comma-separated exact asynq task types for IN filter (ignored when task_type is set). */
|
||||
task_types?: string;
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
}
|
||||
@@ -413,6 +417,8 @@ export interface LogDatabaseStatus {
|
||||
retention_days: Record<string, number>;
|
||||
/** 当前主库的合法迁移目标 */
|
||||
available_targets: string[];
|
||||
/** ClickHouse 运行指标(仅主库为 ClickHouse 时) */
|
||||
clickhouse?: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,8 +4,8 @@ import type {
|
||||
CreateUserRequest,
|
||||
ListUsersRequest,
|
||||
ListUsersResponse,
|
||||
UpdateUserStatusRequest,
|
||||
UpdateUserRequest,
|
||||
UpdateUserStatusRequest,
|
||||
} from './types';
|
||||
|
||||
export class AdminUserService extends BaseService {
|
||||
|
||||
@@ -90,7 +90,7 @@ function getRequestKey(config: {
|
||||
*/
|
||||
apiClient.interceptors.request.use(
|
||||
(config: InternalAxiosRequestConfig) => {
|
||||
// Browser must use same-origin relative URLs so Session Cookie hits Next rewrite.
|
||||
// Browser must use same-origin relative URLs so Session Cookie hits Next rewrite (3010→3000).
|
||||
if (typeof window !== 'undefined') {
|
||||
config.baseURL = getApiBaseUrl();
|
||||
}
|
||||
|
||||
@@ -1,24 +1,19 @@
|
||||
/**
|
||||
* API 配置
|
||||
*
|
||||
* 浏览器默认走同源相对路径(`/api/...`),由 `next.config` rewrites 反代到后端,
|
||||
* 避免 dev 下跨域。仅当设置了 `NEXT_PUBLIC_WAVELET_BACKEND_URL` 时浏览器才直连后端
|
||||
*(静态导出 embed / WebSocket 等场景)。
|
||||
*/
|
||||
|
||||
/**
|
||||
* 获取 API 基础 URL
|
||||
* @returns API 基础 URL;浏览器侧空字符串表示同源相对路径
|
||||
* @returns API 基础 URL
|
||||
*/
|
||||
export function getApiBaseUrl(): string {
|
||||
if (typeof window === 'undefined') {
|
||||
return (
|
||||
process.env.WAVELET_BACKEND_URL ||
|
||||
process.env.NEXT_PUBLIC_WAVELET_BACKEND_URL ||
|
||||
'http://localhost:8000'
|
||||
'http://localhost:3000'
|
||||
);
|
||||
}
|
||||
// 空字符串 → axios 请求 `/api/...`,经 Next rewrites 到后端,无 CORS 问题
|
||||
return process.env.NEXT_PUBLIC_WAVELET_BACKEND_URL || '';
|
||||
}
|
||||
|
||||
@@ -30,6 +25,11 @@ export const apiConfig = {
|
||||
baseURL: getApiBaseUrl(),
|
||||
/** 超时时间(毫秒) */
|
||||
timeout: 15000,
|
||||
/**
|
||||
* 文件上传超时(毫秒)。
|
||||
* Pages 部署包上限 100 MiB,需覆盖上传 + 服务端解压校验与入库时间。
|
||||
*/
|
||||
uploadTimeout: 10 * 60 * 1000,
|
||||
/** 携带凭证 */
|
||||
withCredentials: true,
|
||||
} as const;
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Serialize query params for Gin-compatible repeated array keys.
|
||||
* Axios default uses hosts[]= which Gin QueryArray("hosts") ignores.
|
||||
*/
|
||||
export function serializeSearchParams(
|
||||
params?: Record<string, unknown>,
|
||||
): string {
|
||||
if (!params) return '';
|
||||
const search = new URLSearchParams();
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
if (value === undefined || value === null || value === '') continue;
|
||||
if (Array.isArray(value)) {
|
||||
for (const item of value) {
|
||||
if (item === undefined || item === null || item === '') continue;
|
||||
search.append(key, String(item));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
search.append(key, String(value));
|
||||
}
|
||||
return search.toString();
|
||||
}
|
||||
@@ -61,7 +61,7 @@ export class DbManageService extends BaseService {
|
||||
const disposition = response.headers['content-disposition'] as
|
||||
| string
|
||||
| undefined;
|
||||
let filename = 'wavelet_export';
|
||||
let filename = 'openflare_export';
|
||||
if (disposition) {
|
||||
const match = disposition.match(/filename="?([^";]+)"?/);
|
||||
if (match) filename = match[1];
|
||||
|
||||
@@ -23,13 +23,27 @@ import {
|
||||
import { AuthService } from './auth';
|
||||
import { ConfigService } from './config';
|
||||
import { DbManageService } from './db-manage';
|
||||
import {
|
||||
AdminMessageGatewayService,
|
||||
UserMessageGatewayService,
|
||||
} from './message-gateway';
|
||||
import { PushService } from './push';
|
||||
import { AdminUploadService, UploadService } from './upload';
|
||||
import { UserService } from './user';
|
||||
import {
|
||||
AccessLogService,
|
||||
ApplyLogService,
|
||||
ConfigVersionService,
|
||||
DashboardService,
|
||||
DnsAccountService,
|
||||
NodeService,
|
||||
OptionService,
|
||||
OriginService,
|
||||
PagesService,
|
||||
ProxyRouteService,
|
||||
StatusService,
|
||||
TlsCertificateService,
|
||||
UptimeKumaService,
|
||||
WafService,
|
||||
ZoneDomainService,
|
||||
ZoneService,
|
||||
} from './openflare';
|
||||
|
||||
const services = {
|
||||
auth: AuthService,
|
||||
@@ -47,8 +61,22 @@ const services = {
|
||||
adminUpload: AdminUploadService,
|
||||
dbManage: DbManageService,
|
||||
push: PushService,
|
||||
adminMessageGateway: AdminMessageGatewayService,
|
||||
userMessageGateway: UserMessageGatewayService,
|
||||
openflareNode: NodeService,
|
||||
openflareProxyRoute: ProxyRouteService,
|
||||
openflareConfigVersion: ConfigVersionService,
|
||||
openflareApplyLog: ApplyLogService,
|
||||
openflareDashboard: DashboardService,
|
||||
openflareWaf: WafService,
|
||||
openflareZone: ZoneService,
|
||||
openflareZoneDomain: ZoneDomainService,
|
||||
openflareTls: TlsCertificateService,
|
||||
openflareDns: DnsAccountService,
|
||||
openflarePages: PagesService,
|
||||
openflareOrigin: OriginService,
|
||||
openflareAccessLog: AccessLogService,
|
||||
openflareOption: OptionService,
|
||||
openflareUptimeKuma: UptimeKumaService,
|
||||
openflareStatus: StatusService,
|
||||
} as const;
|
||||
|
||||
export default services;
|
||||
@@ -173,19 +201,6 @@ export type {
|
||||
} from './db-manage';
|
||||
|
||||
export { PushService } from './push';
|
||||
export {
|
||||
AdminMessageGatewayService,
|
||||
UserMessageGatewayService,
|
||||
} from './message-gateway';
|
||||
export type {
|
||||
MessageChannel,
|
||||
MessageChannelDefinition,
|
||||
CreateMessageChannelRequest,
|
||||
UpdateMessageChannelRequest,
|
||||
PublicMessageChannel,
|
||||
MessageBinding,
|
||||
BindMessageChannelRequest,
|
||||
} from './message-gateway';
|
||||
export type {
|
||||
PushEvent,
|
||||
PushHistory,
|
||||
@@ -195,3 +210,56 @@ export type {
|
||||
UpdatePushEventRequest,
|
||||
TestPushRequest,
|
||||
} from './push';
|
||||
|
||||
// ==================== OpenFlare 业务服务导出 ====================
|
||||
|
||||
export {
|
||||
OpenFlareBaseService,
|
||||
NodeService,
|
||||
ProxyRouteService,
|
||||
ConfigVersionService,
|
||||
ApplyLogService,
|
||||
DashboardService,
|
||||
WafService,
|
||||
TlsCertificateService,
|
||||
DnsAccountService,
|
||||
PagesService,
|
||||
OriginService,
|
||||
AccessLogService,
|
||||
OptionService,
|
||||
StatusService,
|
||||
UptimeKumaService,
|
||||
} from './openflare';
|
||||
|
||||
export type {
|
||||
NodeItem,
|
||||
ProxyRouteItem,
|
||||
ProxyRouteZoneDomain,
|
||||
ProxyRouteConfigSection,
|
||||
ConfigVersionSummary,
|
||||
ConfigVersionDetail,
|
||||
ConfigDiffResult,
|
||||
ConfigPreviewResult,
|
||||
ApplyLogList,
|
||||
DashboardOverview,
|
||||
WAFIPGroup,
|
||||
WAFRule,
|
||||
WAFRuleGraph,
|
||||
WAFRuleNode,
|
||||
WAFRuleGroup,
|
||||
WAFSiteRuleGroups,
|
||||
TlsCertificateItem,
|
||||
DnsAccountItem,
|
||||
PagesProject,
|
||||
PagesDeployment,
|
||||
PagesSource,
|
||||
PagesSourceActionReceipt,
|
||||
PagesSourceStatus,
|
||||
OriginItem,
|
||||
OriginDetail,
|
||||
AccessLogList,
|
||||
AccessLogOverview,
|
||||
OptionItem,
|
||||
GeoIPLookupResult,
|
||||
OpenFlarePublicStatus,
|
||||
} from './openflare';
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { BaseService } from '@/lib/services/core';
|
||||
import type {
|
||||
CreateMessageChannelRequest,
|
||||
MessageChannel,
|
||||
MessageChannelDefinition,
|
||||
UpdateMessageChannelRequest,
|
||||
} from './types';
|
||||
|
||||
export class AdminMessageGatewayService extends BaseService {
|
||||
protected static readonly basePath = '/api/v1/admin/message-gateway';
|
||||
|
||||
static async list(): Promise<MessageChannel[]> {
|
||||
return this.get<MessageChannel[]>('/channels');
|
||||
}
|
||||
|
||||
static async definitions(): Promise<MessageChannelDefinition[]> {
|
||||
return this.get<MessageChannelDefinition[]>('/channels/definitions');
|
||||
}
|
||||
|
||||
static async create(
|
||||
data: CreateMessageChannelRequest,
|
||||
): Promise<MessageChannel> {
|
||||
return this.post<MessageChannel>('/channels', data);
|
||||
}
|
||||
|
||||
static async update(
|
||||
id: string,
|
||||
data: UpdateMessageChannelRequest,
|
||||
): Promise<MessageChannel> {
|
||||
return this.patch<MessageChannel>(`/channels/${id}`, data);
|
||||
}
|
||||
|
||||
static async remove(id: string): Promise<void> {
|
||||
return this.delete<void>(`/channels/${id}`);
|
||||
}
|
||||
|
||||
static async test(id: string): Promise<void> {
|
||||
return this.post<void>(`/channels/${id}/test`);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export * from './types';
|
||||
export * from './admin.service';
|
||||
export * from './user.service';
|
||||
@@ -1,73 +0,0 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export type MessageChannelType = 'telegram' | 'qq';
|
||||
|
||||
export interface MessageChannelField {
|
||||
key: string;
|
||||
type?: string;
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
export interface MessageChannelDefinition {
|
||||
type: MessageChannelType | string;
|
||||
name: string;
|
||||
fields: MessageChannelField[];
|
||||
}
|
||||
|
||||
export interface MessageChannel {
|
||||
id: string;
|
||||
name: string;
|
||||
type: MessageChannelType | string;
|
||||
owner_scope: string;
|
||||
enabled: boolean;
|
||||
bot_token?: string;
|
||||
app_id?: string;
|
||||
app_secret?: string;
|
||||
base_url?: string;
|
||||
portal_host?: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface CreateMessageChannelRequest {
|
||||
name: string;
|
||||
type: string;
|
||||
enabled?: boolean;
|
||||
bot_token?: string;
|
||||
app_id?: string;
|
||||
app_secret?: string;
|
||||
base_url?: string;
|
||||
portal_host?: string;
|
||||
}
|
||||
|
||||
export interface UpdateMessageChannelRequest {
|
||||
name?: string;
|
||||
enabled?: boolean;
|
||||
bot_token?: string;
|
||||
app_id?: string;
|
||||
app_secret?: string;
|
||||
base_url?: string;
|
||||
portal_host?: string;
|
||||
}
|
||||
|
||||
export interface PublicMessageChannel {
|
||||
id: string;
|
||||
name: string;
|
||||
type: MessageChannelType | string;
|
||||
}
|
||||
|
||||
export interface MessageBinding {
|
||||
id: string;
|
||||
user_id: string;
|
||||
channel_id: string;
|
||||
channel_name: string;
|
||||
channel_type: string;
|
||||
platform_user_id: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface BindMessageChannelRequest {
|
||||
channel_id: string;
|
||||
code: string;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
// Copyright 2026 Arctel.net
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { BaseService } from '@/lib/services/core';
|
||||
import type {
|
||||
BindMessageChannelRequest,
|
||||
MessageBinding,
|
||||
PublicMessageChannel,
|
||||
} from './types';
|
||||
|
||||
export class UserMessageGatewayService extends BaseService {
|
||||
protected static readonly basePath = '/api/v1/message-gateway';
|
||||
|
||||
static async listBindings(): Promise<MessageBinding[]> {
|
||||
return this.get<MessageBinding[]>('/bindings');
|
||||
}
|
||||
|
||||
static async listChannels(): Promise<PublicMessageChannel[]> {
|
||||
return this.get<PublicMessageChannel[]>('/channels');
|
||||
}
|
||||
|
||||
static async bind(data: BindMessageChannelRequest): Promise<MessageBinding> {
|
||||
return this.post<MessageBinding>('/bindings', data);
|
||||
}
|
||||
|
||||
static async unbind(id: string): Promise<void> {
|
||||
return this.delete<void>(`/bindings/${id}`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import { OpenFlareBaseService } from './base.service';
|
||||
import type {
|
||||
AccessLogCleanupPayload,
|
||||
AccessLogCleanupResult,
|
||||
AccessLogFilters,
|
||||
AccessLogIPAnalysis,
|
||||
AccessLogIPAnalysisFilters,
|
||||
AccessLogIPSummaryFilters,
|
||||
AccessLogIPSummaryList,
|
||||
AccessLogIPTrend,
|
||||
AccessLogIPTrendFilters,
|
||||
AccessLogList,
|
||||
AccessLogOverview,
|
||||
AccessLogOverviewFilters,
|
||||
FoldedAccessLogFilters,
|
||||
FoldedAccessLogIPFilters,
|
||||
FoldedAccessLogIPList,
|
||||
FoldedAccessLogList,
|
||||
} from './types';
|
||||
|
||||
function buildSearchParams(filters: object): Record<string, unknown> {
|
||||
const params: Record<string, unknown> = {};
|
||||
Object.entries(filters as Record<string, unknown>).forEach(([key, value]) => {
|
||||
if (value === undefined || value === null || value === '') return;
|
||||
if (Array.isArray(value)) {
|
||||
const items = value
|
||||
.map((item) => String(item).trim())
|
||||
.filter((item) => item !== '');
|
||||
if (items.length === 0) return;
|
||||
params[key] = items;
|
||||
return;
|
||||
}
|
||||
params[key] = value;
|
||||
});
|
||||
return params;
|
||||
}
|
||||
|
||||
export class AccessLogService extends OpenFlareBaseService {
|
||||
protected static override readonly basePath: string = '/api/v1/d/access-logs';
|
||||
|
||||
static list(filters: AccessLogFilters = {}): Promise<AccessLogList> {
|
||||
return this.get<AccessLogList>('/', buildSearchParams(filters));
|
||||
}
|
||||
|
||||
static getOverview(
|
||||
filters: AccessLogOverviewFilters = {},
|
||||
): Promise<AccessLogOverview> {
|
||||
return this.get<AccessLogOverview>('/overview', buildSearchParams(filters));
|
||||
}
|
||||
|
||||
static listFolds(
|
||||
filters: FoldedAccessLogFilters,
|
||||
): Promise<FoldedAccessLogList> {
|
||||
return this.get<FoldedAccessLogList>('/folds', buildSearchParams(filters));
|
||||
}
|
||||
|
||||
static listFoldIPs(
|
||||
filters: FoldedAccessLogIPFilters,
|
||||
): Promise<FoldedAccessLogIPList> {
|
||||
return this.get<FoldedAccessLogIPList>(
|
||||
'/folds/ip-summary',
|
||||
buildSearchParams(filters),
|
||||
);
|
||||
}
|
||||
|
||||
static listIPSummaries(
|
||||
filters: AccessLogIPSummaryFilters = {},
|
||||
): Promise<AccessLogIPSummaryList> {
|
||||
return this.get<AccessLogIPSummaryList>(
|
||||
'/ip-summary',
|
||||
buildSearchParams(filters),
|
||||
);
|
||||
}
|
||||
|
||||
static getIPTrend(
|
||||
filters: AccessLogIPTrendFilters,
|
||||
): Promise<AccessLogIPTrend> {
|
||||
return this.get<AccessLogIPTrend>(
|
||||
'/ip-summary/trend',
|
||||
buildSearchParams(filters),
|
||||
);
|
||||
}
|
||||
|
||||
static getIPAnalysis(
|
||||
filters: AccessLogIPAnalysisFilters,
|
||||
): Promise<AccessLogIPAnalysis> {
|
||||
return this.get<AccessLogIPAnalysis>(
|
||||
'/ip-summary/analysis',
|
||||
buildSearchParams(filters),
|
||||
);
|
||||
}
|
||||
|
||||
static cleanup(
|
||||
payload: AccessLogCleanupPayload,
|
||||
): Promise<AccessLogCleanupResult> {
|
||||
return this.post<AccessLogCleanupResult>('/cleanup', payload);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { OpenFlareBaseService } from './base.service';
|
||||
import type {
|
||||
ApplyLogCleanupPayload,
|
||||
ApplyLogCleanupResult,
|
||||
ApplyLogList,
|
||||
ApplyLogListQuery,
|
||||
} from './types';
|
||||
|
||||
export class ApplyLogService extends OpenFlareBaseService {
|
||||
protected static override readonly basePath: string = '/api/v1/d/apply-logs';
|
||||
|
||||
static list(query: ApplyLogListQuery = {}): Promise<ApplyLogList> {
|
||||
const params: Record<string, unknown> = {};
|
||||
|
||||
const normalizedNodeId = query.node_id?.trim();
|
||||
if (normalizedNodeId) {
|
||||
params.node_id = normalizedNodeId;
|
||||
}
|
||||
if (query.pageNo) {
|
||||
params.pageNo = query.pageNo;
|
||||
}
|
||||
if (query.pageSize) {
|
||||
params.pageSize = query.pageSize;
|
||||
}
|
||||
|
||||
return this.get<ApplyLogList>('/', params);
|
||||
}
|
||||
|
||||
static cleanup(
|
||||
payload: ApplyLogCleanupPayload,
|
||||
): Promise<ApplyLogCleanupResult> {
|
||||
return this.post<ApplyLogCleanupResult>('/cleanup', payload);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { BaseService } from '@/lib/services/core';
|
||||
|
||||
export class OpenFlareBaseService extends BaseService {
|
||||
protected static override readonly basePath: string = '/api/v1/d';
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
import { OpenFlareBaseService } from './base.service';
|
||||
import type {
|
||||
CloudflareAvailableDomain,
|
||||
CloudflareConnection,
|
||||
CloudflareConnectionPayload,
|
||||
CloudflareGroup,
|
||||
CloudflareGroupDetail,
|
||||
CloudflareGroupPayload,
|
||||
CloudflareMember,
|
||||
CloudflareMemberCreatePayload,
|
||||
CloudflareOverview,
|
||||
CloudflareSyncReceipt,
|
||||
} from './types';
|
||||
|
||||
export const cloudflareQueryKey = ['openflare', 'cloudflare'] as const;
|
||||
|
||||
export class CloudflareService extends OpenFlareBaseService {
|
||||
protected static override readonly basePath = '/api/v1/d/cloudflare';
|
||||
|
||||
static getConnection(): Promise<CloudflareConnection> {
|
||||
return this.get<CloudflareConnection>('/connection');
|
||||
}
|
||||
|
||||
static saveConnection(
|
||||
payload: CloudflareConnectionPayload,
|
||||
): Promise<CloudflareConnection> {
|
||||
return this.put<CloudflareConnection>('/connection', payload);
|
||||
}
|
||||
|
||||
static verifyConnection(): Promise<CloudflareConnection> {
|
||||
return this.post<CloudflareConnection>('/connection/verify');
|
||||
}
|
||||
|
||||
static clearConnection(): Promise<void> {
|
||||
return this.post<void>('/connection/clear');
|
||||
}
|
||||
|
||||
static getOverview(): Promise<CloudflareOverview> {
|
||||
return this.get<CloudflareOverview>('/overview');
|
||||
}
|
||||
|
||||
static listGroups(): Promise<CloudflareGroup[]> {
|
||||
return this.get<CloudflareGroup[]>('/groups');
|
||||
}
|
||||
|
||||
static getGroup(id: number): Promise<CloudflareGroupDetail> {
|
||||
return this.get<CloudflareGroupDetail>(`/groups/${id}`);
|
||||
}
|
||||
|
||||
static createGroup(
|
||||
payload: CloudflareGroupPayload,
|
||||
): Promise<CloudflareGroup> {
|
||||
return this.post<CloudflareGroup>('/groups', payload);
|
||||
}
|
||||
|
||||
static updateGroup(
|
||||
id: number,
|
||||
payload: CloudflareGroupPayload,
|
||||
): Promise<CloudflareGroup> {
|
||||
return this.post<CloudflareGroup>(`/groups/${id}/update`, payload);
|
||||
}
|
||||
|
||||
static deleteGroup(id: number): Promise<void> {
|
||||
return this.post<void>(`/groups/${id}/delete`);
|
||||
}
|
||||
|
||||
static syncGroup(id: number): Promise<CloudflareSyncReceipt> {
|
||||
return this.post<CloudflareSyncReceipt>(`/groups/${id}/sync`);
|
||||
}
|
||||
|
||||
static listAvailableDomains(): Promise<CloudflareAvailableDomain[]> {
|
||||
return this.get<CloudflareAvailableDomain[]>('/domains/available');
|
||||
}
|
||||
|
||||
static createMember(
|
||||
groupId: number,
|
||||
payload: CloudflareMemberCreatePayload,
|
||||
): Promise<CloudflareMember> {
|
||||
return this.post<CloudflareMember>(`/groups/${groupId}/members`, payload);
|
||||
}
|
||||
|
||||
static updateMember(
|
||||
groupId: number,
|
||||
memberId: number,
|
||||
proxied: boolean,
|
||||
): Promise<CloudflareMember> {
|
||||
return this.post<CloudflareMember>(
|
||||
`/groups/${groupId}/members/${memberId}/update`,
|
||||
{ proxied },
|
||||
);
|
||||
}
|
||||
|
||||
static removeMember(groupId: number, memberId: number): Promise<void> {
|
||||
return this.post<void>(`/groups/${groupId}/members/${memberId}/remove`);
|
||||
}
|
||||
|
||||
static syncMember(
|
||||
groupId: number,
|
||||
memberId: number,
|
||||
): Promise<CloudflareSyncReceipt> {
|
||||
return this.post<CloudflareSyncReceipt>(
|
||||
`/groups/${groupId}/members/${memberId}/sync`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import type { InternalAxiosRequestConfig } from 'axios';
|
||||
|
||||
import { OpenFlareBaseService } from './base.service';
|
||||
import type {
|
||||
ConfigDiffResult,
|
||||
ConfigPreviewResult,
|
||||
ConfigVersionCleanupPayload,
|
||||
ConfigVersionCleanupResult,
|
||||
ConfigVersionDetail,
|
||||
ConfigVersionSummary,
|
||||
} from './types';
|
||||
|
||||
export class ConfigVersionService extends OpenFlareBaseService {
|
||||
protected static override readonly basePath: string =
|
||||
'/api/v1/d/config-versions';
|
||||
|
||||
static list(): Promise<ConfigVersionSummary[]> {
|
||||
return this.get<ConfigVersionSummary[]>('/');
|
||||
}
|
||||
|
||||
static getActive(): Promise<ConfigVersionDetail> {
|
||||
return this.get<ConfigVersionDetail>('/active');
|
||||
}
|
||||
|
||||
static preview(): Promise<ConfigPreviewResult> {
|
||||
return this.get<ConfigPreviewResult>('/preview');
|
||||
}
|
||||
|
||||
static diff(): Promise<ConfigDiffResult> {
|
||||
return this.get<ConfigDiffResult>('/diff');
|
||||
}
|
||||
|
||||
static getById(id: string): Promise<ConfigVersionDetail> {
|
||||
return this.get<ConfigVersionDetail>(`/${id}`);
|
||||
}
|
||||
|
||||
static publish(force?: boolean): Promise<ConfigVersionDetail> {
|
||||
return this.post<ConfigVersionDetail>(
|
||||
'/publish',
|
||||
undefined,
|
||||
force
|
||||
? ({ params: { force: true } } as InternalAxiosRequestConfig)
|
||||
: undefined,
|
||||
);
|
||||
}
|
||||
|
||||
static activate(id: string): Promise<ConfigVersionDetail> {
|
||||
return this.post<ConfigVersionDetail>(`/${id}/activate`);
|
||||
}
|
||||
|
||||
static cleanup(
|
||||
payload: ConfigVersionCleanupPayload,
|
||||
): Promise<ConfigVersionCleanupResult> {
|
||||
return this.post<ConfigVersionCleanupResult>('/cleanup', payload);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
import { OpenFlareBaseService } from './base.service';
|
||||
import type {
|
||||
CompactCapacityTrendPoint,
|
||||
CompactDashboardNodeHealth,
|
||||
CompactDiskIOTrendPoint,
|
||||
CompactDistributionItem,
|
||||
CompactNetworkTrendPoint,
|
||||
CompactTrafficTrendPoint,
|
||||
DashboardCapacity,
|
||||
DashboardNodeHealth,
|
||||
DashboardOverview,
|
||||
DashboardOverviewCompact,
|
||||
DashboardSummary,
|
||||
DashboardTraffic,
|
||||
DistributionItem,
|
||||
} from './types';
|
||||
|
||||
function arrayOrEmpty<T>(value: T[] | null | undefined) {
|
||||
return Array.isArray(value) ? value : [];
|
||||
}
|
||||
|
||||
function isCompactDistributionItem(
|
||||
value: DistributionItem | CompactDistributionItem,
|
||||
): value is CompactDistributionItem {
|
||||
return Array.isArray(value);
|
||||
}
|
||||
|
||||
function isCompactTrafficTrendPoint(
|
||||
value:
|
||||
| DashboardOverview['trends']['traffic_24h'][number]
|
||||
| CompactTrafficTrendPoint,
|
||||
): value is CompactTrafficTrendPoint {
|
||||
return Array.isArray(value);
|
||||
}
|
||||
|
||||
function isCompactCapacityTrendPoint(
|
||||
value:
|
||||
| DashboardOverview['trends']['capacity_24h'][number]
|
||||
| CompactCapacityTrendPoint,
|
||||
): value is CompactCapacityTrendPoint {
|
||||
return Array.isArray(value);
|
||||
}
|
||||
|
||||
function isCompactNetworkTrendPoint(
|
||||
value:
|
||||
| DashboardOverview['trends']['network_24h'][number]
|
||||
| CompactNetworkTrendPoint,
|
||||
): value is CompactNetworkTrendPoint {
|
||||
return Array.isArray(value);
|
||||
}
|
||||
|
||||
function isCompactDiskIOTrendPoint(
|
||||
value:
|
||||
| DashboardOverview['trends']['disk_io_24h'][number]
|
||||
| CompactDiskIOTrendPoint,
|
||||
): value is CompactDiskIOTrendPoint {
|
||||
return Array.isArray(value);
|
||||
}
|
||||
|
||||
function isCompactDashboardNode(
|
||||
value: DashboardNodeHealth | CompactDashboardNodeHealth,
|
||||
): value is CompactDashboardNodeHealth {
|
||||
return Array.isArray(value);
|
||||
}
|
||||
|
||||
function normalizeDistributionItems(
|
||||
items: Array<DistributionItem | CompactDistributionItem> | null | undefined,
|
||||
): DistributionItem[] {
|
||||
return arrayOrEmpty(items).map((item) =>
|
||||
isCompactDistributionItem(item)
|
||||
? { key: String(item[0] ?? ''), value: Number(item[1] ?? 0) }
|
||||
: item,
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeTrafficTrendPoints(
|
||||
items:
|
||||
| Array<
|
||||
| DashboardOverview['trends']['traffic_24h'][number]
|
||||
| CompactTrafficTrendPoint
|
||||
>
|
||||
| null
|
||||
| undefined,
|
||||
) {
|
||||
return arrayOrEmpty(items).map((item) =>
|
||||
isCompactTrafficTrendPoint(item)
|
||||
? {
|
||||
bucket_started_at: String(item[0] ?? ''),
|
||||
request_count: Number(item[1] ?? 0),
|
||||
error_count: Number(item[2] ?? 0),
|
||||
unique_visitor_count: Number(item[3] ?? 0),
|
||||
status_2xx_count: Number(item[4] ?? 0),
|
||||
status_4xx_count: Number(item[5] ?? 0),
|
||||
status_5xx_count: Number(item[6] ?? 0),
|
||||
}
|
||||
: item,
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeCapacityTrendPoints(
|
||||
items:
|
||||
| Array<
|
||||
| DashboardOverview['trends']['capacity_24h'][number]
|
||||
| CompactCapacityTrendPoint
|
||||
>
|
||||
| null
|
||||
| undefined,
|
||||
) {
|
||||
return arrayOrEmpty(items).map((item) =>
|
||||
isCompactCapacityTrendPoint(item)
|
||||
? {
|
||||
bucket_started_at: String(item[0] ?? ''),
|
||||
average_cpu_usage_percent: Number(item[1] ?? 0),
|
||||
average_memory_usage_percent: Number(item[2] ?? 0),
|
||||
reported_nodes: Number(item[3] ?? 0),
|
||||
}
|
||||
: item,
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeNetworkTrendPoints(
|
||||
items:
|
||||
| Array<
|
||||
| DashboardOverview['trends']['network_24h'][number]
|
||||
| CompactNetworkTrendPoint
|
||||
>
|
||||
| null
|
||||
| undefined,
|
||||
) {
|
||||
return arrayOrEmpty(items).map((item) =>
|
||||
isCompactNetworkTrendPoint(item)
|
||||
? {
|
||||
bucket_started_at: String(item[0] ?? ''),
|
||||
bytes_received: Number(item[1] ?? 0),
|
||||
bytes_provided: Number(item[2] ?? 0),
|
||||
reported_nodes: Number(item[3] ?? 0),
|
||||
}
|
||||
: {
|
||||
...item,
|
||||
bytes_received: Number(
|
||||
(item as { bytes_received?: number }).bytes_received ?? 0,
|
||||
),
|
||||
bytes_provided: Number(
|
||||
(item as { bytes_provided?: number }).bytes_provided ?? 0,
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeDiskIOTrendPoints(
|
||||
items:
|
||||
| Array<
|
||||
| DashboardOverview['trends']['disk_io_24h'][number]
|
||||
| CompactDiskIOTrendPoint
|
||||
>
|
||||
| null
|
||||
| undefined,
|
||||
) {
|
||||
return arrayOrEmpty(items).map((item) =>
|
||||
isCompactDiskIOTrendPoint(item)
|
||||
? {
|
||||
bucket_started_at: String(item[0] ?? ''),
|
||||
disk_read_bytes: Number(item[1] ?? 0),
|
||||
disk_write_bytes: Number(item[2] ?? 0),
|
||||
reported_nodes: Number(item[3] ?? 0),
|
||||
}
|
||||
: item,
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeDashboardNodes(
|
||||
items:
|
||||
| Array<DashboardNodeHealth | CompactDashboardNodeHealth>
|
||||
| null
|
||||
| undefined,
|
||||
): DashboardNodeHealth[] {
|
||||
return arrayOrEmpty(items).map((item) =>
|
||||
isCompactDashboardNode(item)
|
||||
? {
|
||||
id: Number(item[0] ?? 0),
|
||||
node_id: String(item[1] ?? ''),
|
||||
name: String(item[2] ?? ''),
|
||||
geo_name: String(item[3] ?? ''),
|
||||
geo_latitude:
|
||||
item[4] === null || item[4] === undefined ? null : Number(item[4]),
|
||||
geo_longitude:
|
||||
item[5] === null || item[5] === undefined ? null : Number(item[5]),
|
||||
status: (item[6] ?? 'pending') as DashboardNodeHealth['status'],
|
||||
openresty_status: (item[7] ??
|
||||
'unknown') as DashboardNodeHealth['openresty_status'],
|
||||
current_version: String(item[8] ?? ''),
|
||||
last_seen_at: String(item[9] ?? ''),
|
||||
active_event_count: Number(item[10] ?? 0),
|
||||
cpu_usage_percent: Number(item[11] ?? 0),
|
||||
memory_usage_percent: Number(item[12] ?? 0),
|
||||
storage_usage_percent: Number(item[13] ?? 0),
|
||||
request_count: Number(item[14] ?? 0),
|
||||
error_count: Number(item[15] ?? 0),
|
||||
unique_visitor_count: Number(item[16] ?? 0),
|
||||
}
|
||||
: item,
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeDashboardOverview(
|
||||
overview: DashboardOverview | DashboardOverviewCompact | null | undefined,
|
||||
): DashboardOverview | null {
|
||||
if (!overview) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const summary = (overview.summary ?? {}) as DashboardSummary;
|
||||
const traffic = (overview.traffic ?? {}) as DashboardTraffic;
|
||||
const capacity = (overview.capacity ?? {}) as DashboardCapacity;
|
||||
|
||||
return {
|
||||
generated_at: String(overview.generated_at ?? ''),
|
||||
summary,
|
||||
traffic,
|
||||
capacity,
|
||||
nodes: normalizeDashboardNodes(overview.nodes),
|
||||
distributions: {
|
||||
source_countries: normalizeDistributionItems(
|
||||
overview.distributions?.source_countries,
|
||||
),
|
||||
status_codes: normalizeDistributionItems(
|
||||
overview.distributions?.status_codes,
|
||||
),
|
||||
top_domains: normalizeDistributionItems(
|
||||
overview.distributions?.top_domains,
|
||||
),
|
||||
},
|
||||
trends: {
|
||||
traffic_24h: normalizeTrafficTrendPoints(overview.trends?.traffic_24h),
|
||||
capacity_24h: normalizeCapacityTrendPoints(overview.trends?.capacity_24h),
|
||||
network_24h: normalizeNetworkTrendPoints(overview.trends?.network_24h),
|
||||
disk_io_24h: normalizeDiskIOTrendPoints(overview.trends?.disk_io_24h),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export class DashboardService extends OpenFlareBaseService {
|
||||
protected static override readonly basePath: string = '/api/v1/d/dashboard';
|
||||
|
||||
static async getOverview(): Promise<DashboardOverview | null> {
|
||||
const overview = await this.get<
|
||||
DashboardOverview | DashboardOverviewCompact
|
||||
>('/overview');
|
||||
return normalizeDashboardOverview(overview);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { OpenFlareBaseService } from './base.service';
|
||||
import type { DnsAccountItem, DnsAccountMutationPayload } from './types';
|
||||
|
||||
export class DnsAccountService extends OpenFlareBaseService {
|
||||
protected static override readonly basePath: string =
|
||||
'/api/v1/d/dns-accounts';
|
||||
|
||||
static async list(): Promise<DnsAccountItem[]> {
|
||||
return this.get<DnsAccountItem[]>('/');
|
||||
}
|
||||
|
||||
static async create(
|
||||
payload: DnsAccountMutationPayload,
|
||||
): Promise<DnsAccountItem> {
|
||||
return this.post<DnsAccountItem>('/', payload);
|
||||
}
|
||||
|
||||
static async update(
|
||||
id: number,
|
||||
payload: DnsAccountMutationPayload,
|
||||
): Promise<DnsAccountItem> {
|
||||
return this.post<DnsAccountItem>(`/${id}/update`, payload);
|
||||
}
|
||||
|
||||
static async deleteById(id: number): Promise<void> {
|
||||
return this.post<void>(`/${id}/delete`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
export { OpenFlareBaseService } from './base.service';
|
||||
|
||||
export { NodeService } from './node.service';
|
||||
export { ProxyRouteService } from './proxy-route.service';
|
||||
export { ConfigVersionService } from './config-version.service';
|
||||
export { ApplyLogService } from './apply-log.service';
|
||||
export { DashboardService } from './dashboard.service';
|
||||
export { WafService } from './waf.service';
|
||||
export { ZoneDomainService, ZoneService, zoneQueryKey } from './zone.service';
|
||||
export { TlsCertificateService } from './tls-certificate.service';
|
||||
export { DnsAccountService } from './dns-account.service';
|
||||
export { PagesService } from './pages.service';
|
||||
export { OriginService } from './origin.service';
|
||||
export { AccessLogService } from './access-log.service';
|
||||
export { OptionService } from './option.service';
|
||||
export { UptimeKumaService } from './uptimekuma.service';
|
||||
export { StatusService } from './status.service';
|
||||
export {
|
||||
CloudflareService,
|
||||
cloudflareQueryKey,
|
||||
} from './cloudflare.service';
|
||||
|
||||
export type {
|
||||
ApplyLogCleanupPayload,
|
||||
ApplyLogCleanupResult,
|
||||
ApplyLogItem,
|
||||
ApplyLogList,
|
||||
ApplyLogListQuery,
|
||||
ApplyResult,
|
||||
ConfigDiffResult,
|
||||
ConfigOptionDiffItem,
|
||||
ConfigPreviewResult,
|
||||
ConfigVersionCleanupPayload,
|
||||
ConfigVersionCleanupResult,
|
||||
ConfigVersionDetail,
|
||||
ConfigVersionSummary,
|
||||
NodeAgentReleaseInfo,
|
||||
NodeAgentUpdatePayload,
|
||||
NodeBootstrapToken,
|
||||
NodeItem,
|
||||
NodeMutationPayload,
|
||||
NodeHealthEvent,
|
||||
NodeObservability,
|
||||
NodeObservabilityAnalytics,
|
||||
NodeObservabilityTrends,
|
||||
NodeSystemProfile,
|
||||
NodeStatus,
|
||||
NodeType,
|
||||
OpenrestyStatus,
|
||||
ProxyRouteConfigSection,
|
||||
ProxyRouteCustomHeader,
|
||||
ProxyRouteItem,
|
||||
ProxyRouteMutationPayload,
|
||||
ProxyRoutePoWConfig,
|
||||
ProxyRouteZoneDomain,
|
||||
ReleaseChannel,
|
||||
SupportFile,
|
||||
DashboardOverview,
|
||||
DashboardNodeHealth,
|
||||
DashboardSummary,
|
||||
DashboardTraffic,
|
||||
DashboardCapacity,
|
||||
DistributionItem,
|
||||
TrafficDistributions,
|
||||
TrafficTrendPoint,
|
||||
CapacityTrendPoint,
|
||||
NetworkTrendPoint,
|
||||
DiskIOTrendPoint,
|
||||
WAFIPGroup,
|
||||
WAFIPGroupAutoTestPayload,
|
||||
WAFIPGroupAutoTestResult,
|
||||
WAFIPGroupPayload,
|
||||
WAFIPGroupSyncResult,
|
||||
WAFIPGroupSubscriptionFormat,
|
||||
WAFIPGroupType,
|
||||
WAFCreateRulePayload,
|
||||
WAFRule,
|
||||
WAFRuleEdge,
|
||||
WAFRuleGraph,
|
||||
WAFRuleNode,
|
||||
WAFSaveRuleGraphPayload,
|
||||
WAFUpdateRuleMetaPayload,
|
||||
WAFRuleGroup,
|
||||
WAFRuleGroupPayload,
|
||||
WAFSiteRuleGroups,
|
||||
AccessLogCleanupPayload,
|
||||
AccessLogCleanupResult,
|
||||
AccessLogFilters,
|
||||
AccessLogIPAnalysis,
|
||||
AccessLogIPAnalysisFilters,
|
||||
AccessLogIPSummaryFilters,
|
||||
AccessLogIPSummaryItem,
|
||||
AccessLogIPSummaryList,
|
||||
AccessLogIPTrend,
|
||||
AccessLogIPTrendFilters,
|
||||
AccessLogItem,
|
||||
AccessLogList,
|
||||
AccessLogOverview,
|
||||
AccessLogOverviewFilters,
|
||||
AccessLogOverviewMetricPoint,
|
||||
FoldedAccessLogFilters,
|
||||
FoldedAccessLogIPFilters,
|
||||
FoldedAccessLogIPList,
|
||||
FoldedAccessLogList,
|
||||
OptionItem,
|
||||
GeoIPLookupResult,
|
||||
OpenFlarePublicStatus,
|
||||
OriginDetail,
|
||||
OriginItem,
|
||||
OriginMutationPayload,
|
||||
PagesDeployment,
|
||||
PagesDeploymentFile,
|
||||
PagesGitHubLatestSourceUpdatePayload,
|
||||
PagesGitHubReleaseSelector,
|
||||
PagesGitHubReleaseSource,
|
||||
PagesGitHubSourceUpdatePayload,
|
||||
PagesGitHubTagSourceUpdatePayload,
|
||||
PagesManualSource,
|
||||
PagesProject,
|
||||
PagesProjectPayload,
|
||||
PagesRemoteSourceUpdatePayload,
|
||||
PagesRemoteURLSource,
|
||||
PagesSource,
|
||||
PagesSourceActionPayload,
|
||||
PagesSourceActionReceipt,
|
||||
PagesSourceRevision,
|
||||
PagesSourceStatus,
|
||||
PagesSourceUpdatePayload,
|
||||
PagesSourceUpdateResult,
|
||||
AcmeAccountItem,
|
||||
DnsAccountItem,
|
||||
DnsAccountMutationPayload,
|
||||
ZoneDomainItem,
|
||||
ZoneDomainMutationPayload,
|
||||
ZoneItem,
|
||||
ZoneMutationPayload,
|
||||
ZoneOverview,
|
||||
ZoneStats,
|
||||
ZoneStatsPoint,
|
||||
ZoneStatsRange,
|
||||
TlsCertificateApplyPayload,
|
||||
TlsCertificateContentItem,
|
||||
TlsCertificateDetailItem,
|
||||
TlsCertificateFileImportPayload,
|
||||
TlsCertificateItem,
|
||||
TlsCertificateMutationPayload,
|
||||
CloudflareAvailableDomain,
|
||||
CloudflareConnection,
|
||||
CloudflareConnectionPayload,
|
||||
CloudflareConnectionSource,
|
||||
CloudflareGroup,
|
||||
CloudflareGroupDetail,
|
||||
CloudflareGroupPayload,
|
||||
CloudflareMember,
|
||||
CloudflareMemberCreatePayload,
|
||||
CloudflareNodeOption,
|
||||
CloudflareOverview,
|
||||
CloudflareSyncReceipt,
|
||||
CloudflareSyncStatus,
|
||||
} from './types';
|
||||
|
||||
import { AccessLogService } from './access-log.service';
|
||||
import { ApplyLogService } from './apply-log.service';
|
||||
import { ConfigVersionService } from './config-version.service';
|
||||
import { DashboardService } from './dashboard.service';
|
||||
import { DnsAccountService } from './dns-account.service';
|
||||
import { NodeService } from './node.service';
|
||||
import { OptionService } from './option.service';
|
||||
import { StatusService } from './status.service';
|
||||
import { UptimeKumaService } from './uptimekuma.service';
|
||||
import { OriginService } from './origin.service';
|
||||
import { PagesService } from './pages.service';
|
||||
import { ProxyRouteService } from './proxy-route.service';
|
||||
import { TlsCertificateService } from './tls-certificate.service';
|
||||
import { WafService } from './waf.service';
|
||||
import { ZoneDomainService, ZoneService } from './zone.service';
|
||||
import { CloudflareService } from './cloudflare.service';
|
||||
|
||||
export const openflareServices = {
|
||||
node: NodeService,
|
||||
proxyRoute: ProxyRouteService,
|
||||
configVersion: ConfigVersionService,
|
||||
applyLog: ApplyLogService,
|
||||
dashboard: DashboardService,
|
||||
waf: WafService,
|
||||
zone: ZoneService,
|
||||
zoneDomain: ZoneDomainService,
|
||||
tlsCertificate: TlsCertificateService,
|
||||
dnsAccount: DnsAccountService,
|
||||
pages: PagesService,
|
||||
origin: OriginService,
|
||||
accessLog: AccessLogService,
|
||||
option: OptionService,
|
||||
uptimeKuma: UptimeKumaService,
|
||||
status: StatusService,
|
||||
cloudflare: CloudflareService,
|
||||
} as const;
|
||||
@@ -0,0 +1,81 @@
|
||||
import { OpenFlareBaseService } from './base.service';
|
||||
import type {
|
||||
NodeAgentReleaseInfo,
|
||||
NodeAgentUpdatePayload,
|
||||
NodeBootstrapToken,
|
||||
NodeItem,
|
||||
NodeMutationPayload,
|
||||
NodeObservability,
|
||||
ReleaseChannel,
|
||||
} from './types';
|
||||
|
||||
export class NodeService extends OpenFlareBaseService {
|
||||
protected static override readonly basePath: string = '/api/v1/d/nodes';
|
||||
|
||||
static async listNodes(): Promise<NodeItem[]> {
|
||||
return this.get<NodeItem[]>('/');
|
||||
}
|
||||
|
||||
static async createNode(payload: NodeMutationPayload): Promise<NodeItem> {
|
||||
return this.post<NodeItem>('/', payload);
|
||||
}
|
||||
|
||||
static async updateNode(
|
||||
id: number,
|
||||
payload: NodeMutationPayload,
|
||||
): Promise<NodeItem> {
|
||||
return this.post<NodeItem>(`/${id}/update`, payload);
|
||||
}
|
||||
|
||||
static async deleteNode(id: number): Promise<void> {
|
||||
return this.post<void>(`/${id}/delete`);
|
||||
}
|
||||
|
||||
static async getBootstrapToken(): Promise<NodeBootstrapToken> {
|
||||
return this.get<NodeBootstrapToken>('/bootstrap-token');
|
||||
}
|
||||
|
||||
static async rotateBootstrapToken(): Promise<NodeBootstrapToken> {
|
||||
return this.post<NodeBootstrapToken>('/bootstrap-token/rotate');
|
||||
}
|
||||
|
||||
static async requestAgentUpdate(
|
||||
id: number,
|
||||
payload?: NodeAgentUpdatePayload,
|
||||
): Promise<NodeItem> {
|
||||
return this.post<NodeItem>(`/${id}/agent-update`, payload ?? {});
|
||||
}
|
||||
|
||||
static async requestForceSync(id: number): Promise<NodeItem> {
|
||||
return this.post<NodeItem>(`/${id}/force-sync`);
|
||||
}
|
||||
|
||||
static async requestOpenrestyRestart(id: number): Promise<NodeItem> {
|
||||
return this.post<NodeItem>(`/${id}/openresty-restart`);
|
||||
}
|
||||
|
||||
static async getAgentRelease(
|
||||
id: number,
|
||||
channel: ReleaseChannel = 'stable',
|
||||
): Promise<NodeAgentReleaseInfo> {
|
||||
return this.get<NodeAgentReleaseInfo>(`/${id}/agent-release`, { channel });
|
||||
}
|
||||
|
||||
static async getObservability(
|
||||
id: number,
|
||||
options?: { hours?: number; limit?: number },
|
||||
): Promise<NodeObservability> {
|
||||
return this.get<NodeObservability>(`/${id}/observability`, {
|
||||
hours: options?.hours,
|
||||
limit: options?.limit,
|
||||
});
|
||||
}
|
||||
|
||||
static async cleanupHealthEvents(
|
||||
id: number,
|
||||
): Promise<{ node_id: string; deleted_count: number }> {
|
||||
return this.post<{ node_id: string; deleted_count: number }>(
|
||||
`/${id}/observability/cleanup`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { OpenFlareBaseService } from './base.service';
|
||||
import type {
|
||||
GeoIPLookupResult,
|
||||
OptionBatchPayload,
|
||||
OptionItem,
|
||||
} from './types';
|
||||
|
||||
export class OptionService extends OpenFlareBaseService {
|
||||
protected static override readonly basePath: string = '/api/v1/d/option';
|
||||
|
||||
static list(): Promise<OptionItem[]> {
|
||||
return this.get<OptionItem[]>('/');
|
||||
}
|
||||
|
||||
static update(key: string, value: string): Promise<void> {
|
||||
return this.post<void>('/update', { key, value });
|
||||
}
|
||||
|
||||
static updateBatch(options: OptionItem[]): Promise<void> {
|
||||
const payload: OptionBatchPayload = { options };
|
||||
return this.post<void>('/update-batch', payload);
|
||||
}
|
||||
|
||||
static lookupGeoIP(provider: string, ip: string): Promise<GeoIPLookupResult> {
|
||||
return this.post<GeoIPLookupResult>('/geoip/lookup', { provider, ip });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { OpenFlareBaseService } from './base.service';
|
||||
import type { OriginDetail, OriginItem, OriginMutationPayload } from './types';
|
||||
|
||||
export class OriginService extends OpenFlareBaseService {
|
||||
protected static override readonly basePath: string = '/api/v1/d/origins';
|
||||
|
||||
static list(): Promise<OriginItem[]> {
|
||||
return this.get<OriginItem[]>('/');
|
||||
}
|
||||
|
||||
static getById(id: number): Promise<OriginDetail> {
|
||||
return this.get<OriginDetail>(`/${id}`);
|
||||
}
|
||||
|
||||
static create(payload: OriginMutationPayload): Promise<OriginItem> {
|
||||
return this.post<OriginItem>('/', payload);
|
||||
}
|
||||
|
||||
static update(
|
||||
id: number,
|
||||
payload: OriginMutationPayload,
|
||||
): Promise<OriginItem> {
|
||||
return this.post<OriginItem>(`/${id}/update`, payload);
|
||||
}
|
||||
|
||||
static deleteById(id: number): Promise<void> {
|
||||
return this.post<void>(`/${id}/delete`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
import type { AxiosProgressEvent, InternalAxiosRequestConfig } from 'axios';
|
||||
|
||||
import apiClient from '@/lib/services/core/api-client';
|
||||
import { apiConfig } from '@/lib/services/core/config';
|
||||
import type { ApiResponse } from '@/lib/services/core';
|
||||
|
||||
import { OpenFlareBaseService } from './base.service';
|
||||
import type {
|
||||
PagesDeployment,
|
||||
PagesDeploymentFile,
|
||||
PagesDeploymentUploadFromURLPayload,
|
||||
PagesDeploymentUploadPayload,
|
||||
PagesProject,
|
||||
PagesProjectPayload,
|
||||
PagesSource,
|
||||
PagesSourceActionPayload,
|
||||
PagesSourceActionReceipt,
|
||||
PagesSourceUpdatePayload,
|
||||
PagesSourceUpdateResult,
|
||||
} from './types';
|
||||
|
||||
export class PagesService extends OpenFlareBaseService {
|
||||
protected static override readonly basePath: string = '/api/v1/d/pages';
|
||||
|
||||
static listProjects(): Promise<PagesProject[]> {
|
||||
return this.get<PagesProject[]>('/');
|
||||
}
|
||||
|
||||
static getProject(id: number): Promise<PagesProject> {
|
||||
return this.get<PagesProject>(`/${id}`);
|
||||
}
|
||||
|
||||
static createProject(payload: PagesProjectPayload): Promise<PagesProject> {
|
||||
return this.post<PagesProject>('/', payload);
|
||||
}
|
||||
|
||||
static updateProject(
|
||||
id: number,
|
||||
payload: PagesProjectPayload,
|
||||
): Promise<PagesProject> {
|
||||
return this.post<PagesProject>(`/${id}/update`, payload);
|
||||
}
|
||||
|
||||
static deleteProject(id: number): Promise<void> {
|
||||
return this.post<void>(`/${id}/delete`);
|
||||
}
|
||||
|
||||
static getSource(projectId: number): Promise<PagesSource> {
|
||||
return this.get<PagesSource>(`/${projectId}/source`);
|
||||
}
|
||||
|
||||
static updateSource(
|
||||
projectId: number,
|
||||
payload: PagesSourceUpdatePayload,
|
||||
): Promise<PagesSourceUpdateResult> {
|
||||
return this.post<PagesSourceUpdateResult>(
|
||||
`/${projectId}/source/update`,
|
||||
payload,
|
||||
);
|
||||
}
|
||||
|
||||
static deleteSource(projectId: number): Promise<PagesSource> {
|
||||
return this.post<PagesSource>(`/${projectId}/source/delete`);
|
||||
}
|
||||
|
||||
static checkSource(projectId: number): Promise<PagesSourceActionReceipt> {
|
||||
return this.post<PagesSourceActionReceipt>(
|
||||
`/${projectId}/source/check`,
|
||||
{},
|
||||
);
|
||||
}
|
||||
|
||||
static syncSource(
|
||||
projectId: number,
|
||||
payload: PagesSourceActionPayload = {},
|
||||
): Promise<PagesSourceActionReceipt> {
|
||||
return this.post<PagesSourceActionReceipt>(
|
||||
`/${projectId}/source/sync`,
|
||||
payload,
|
||||
);
|
||||
}
|
||||
|
||||
static listDeployments(projectId: number): Promise<PagesDeployment[]> {
|
||||
return this.get<PagesDeployment[]>(`/${projectId}/deployments`);
|
||||
}
|
||||
|
||||
static listDeploymentFiles(
|
||||
deploymentId: number,
|
||||
): Promise<PagesDeploymentFile[]> {
|
||||
return this.get<PagesDeploymentFile[]>(
|
||||
`/deployments/${deploymentId}/files`,
|
||||
);
|
||||
}
|
||||
|
||||
static uploadDeployment(
|
||||
projectId: number,
|
||||
payload: PagesDeploymentUploadPayload,
|
||||
): Promise<PagesDeployment> {
|
||||
const formData = new FormData();
|
||||
formData.append('package', payload.file);
|
||||
|
||||
return this.postFormData<PagesDeployment>(
|
||||
`/${projectId}/deployments/upload`,
|
||||
formData,
|
||||
payload.onProgress,
|
||||
);
|
||||
}
|
||||
|
||||
static uploadDeploymentFromURL(
|
||||
projectId: number,
|
||||
payload: PagesDeploymentUploadFromURLPayload,
|
||||
): Promise<PagesDeployment> {
|
||||
return this.post<PagesDeployment>(
|
||||
`/${projectId}/deployments/upload-from-url`,
|
||||
payload,
|
||||
{ timeout: apiConfig.uploadTimeout } as InternalAxiosRequestConfig,
|
||||
);
|
||||
}
|
||||
|
||||
static activateDeployment(
|
||||
projectId: number,
|
||||
deploymentId: number,
|
||||
): Promise<PagesProject> {
|
||||
return this.post<PagesProject>(
|
||||
`/${projectId}/deployments/${deploymentId}/activate`,
|
||||
);
|
||||
}
|
||||
|
||||
static deleteDeployment(
|
||||
projectId: number,
|
||||
deploymentId: number,
|
||||
): Promise<void> {
|
||||
return this.post<void>(`/${projectId}/deployments/${deploymentId}/delete`);
|
||||
}
|
||||
|
||||
private static async postFormData<T>(
|
||||
path: string,
|
||||
formData: FormData,
|
||||
onProgress?: (percent: number) => void,
|
||||
): Promise<T> {
|
||||
const response = await apiClient.post<ApiResponse<T>>(
|
||||
this.getFullPath(path),
|
||||
formData,
|
||||
{
|
||||
timeout: apiConfig.uploadTimeout,
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
onUploadProgress: (event: AxiosProgressEvent) => {
|
||||
if (!onProgress || !event.total) return;
|
||||
const percent = Math.round((event.loaded / event.total) * 100);
|
||||
onProgress(percent);
|
||||
},
|
||||
} as InternalAxiosRequestConfig,
|
||||
);
|
||||
|
||||
return response.data.data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { OpenFlareBaseService } from './base.service';
|
||||
import type { ProxyRouteItem, ProxyRouteMutationPayload } from './types';
|
||||
|
||||
export class ProxyRouteService extends OpenFlareBaseService {
|
||||
protected static override readonly basePath: string =
|
||||
'/api/v1/d/proxy-routes';
|
||||
|
||||
static async list(): Promise<ProxyRouteItem[]> {
|
||||
return this.get<ProxyRouteItem[]>('/');
|
||||
}
|
||||
|
||||
static async getById(id: number): Promise<ProxyRouteItem> {
|
||||
return this.get<ProxyRouteItem>(`/${id}`);
|
||||
}
|
||||
|
||||
static async create(
|
||||
payload: ProxyRouteMutationPayload,
|
||||
): Promise<ProxyRouteItem> {
|
||||
return this.post<ProxyRouteItem>('/', payload);
|
||||
}
|
||||
|
||||
static async update(
|
||||
id: number,
|
||||
payload: ProxyRouteMutationPayload,
|
||||
): Promise<ProxyRouteItem> {
|
||||
return this.post<ProxyRouteItem>(`/${id}/update`, payload);
|
||||
}
|
||||
|
||||
static async deleteById(id: number): Promise<void> {
|
||||
return this.post<void>(`/${id}/delete`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { OpenFlareBaseService } from './base.service';
|
||||
import type { OpenFlarePublicStatus } from './types';
|
||||
|
||||
export class StatusService extends OpenFlareBaseService {
|
||||
static getPublicStatus(): Promise<OpenFlarePublicStatus> {
|
||||
return this.get<OpenFlarePublicStatus>('/status');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import { OpenFlareBaseService } from './base.service';
|
||||
import type {
|
||||
AcmeAccountItem,
|
||||
TlsCertificateApplyPayload,
|
||||
TlsCertificateContentItem,
|
||||
TlsCertificateDetailItem,
|
||||
TlsCertificateFileImportPayload,
|
||||
TlsCertificateItem,
|
||||
TlsCertificateMutationPayload,
|
||||
} from './types';
|
||||
|
||||
class AcmeAccountApi extends OpenFlareBaseService {
|
||||
protected static override readonly basePath: string =
|
||||
'/api/v1/d/acme-accounts';
|
||||
|
||||
static getDefault(): Promise<AcmeAccountItem> {
|
||||
return this.get<AcmeAccountItem>('/default');
|
||||
}
|
||||
}
|
||||
|
||||
export class TlsCertificateService extends OpenFlareBaseService {
|
||||
protected static override readonly basePath: string =
|
||||
'/api/v1/d/tls-certificates';
|
||||
|
||||
static async list(): Promise<TlsCertificateItem[]> {
|
||||
return this.get<TlsCertificateItem[]>('/');
|
||||
}
|
||||
|
||||
static async getById(id: number): Promise<TlsCertificateDetailItem> {
|
||||
return this.get<TlsCertificateDetailItem>(`/${id}`);
|
||||
}
|
||||
|
||||
static async getContent(id: number): Promise<TlsCertificateContentItem> {
|
||||
return this.get<TlsCertificateContentItem>(`/${id}/content`);
|
||||
}
|
||||
|
||||
static async create(
|
||||
payload: TlsCertificateMutationPayload,
|
||||
): Promise<TlsCertificateItem> {
|
||||
return this.post<TlsCertificateItem>('/', payload);
|
||||
}
|
||||
|
||||
static async update(
|
||||
id: number,
|
||||
payload: TlsCertificateMutationPayload,
|
||||
): Promise<TlsCertificateItem> {
|
||||
return this.post<TlsCertificateItem>(`/${id}/update`, payload);
|
||||
}
|
||||
|
||||
static async deleteById(id: number): Promise<void> {
|
||||
return this.post<void>(`/${id}/delete`);
|
||||
}
|
||||
|
||||
static async apply(
|
||||
payload: TlsCertificateApplyPayload,
|
||||
): Promise<TlsCertificateItem> {
|
||||
return this.post<TlsCertificateItem>('/apply', payload);
|
||||
}
|
||||
|
||||
static async renew(id: number): Promise<TlsCertificateItem> {
|
||||
return this.post<TlsCertificateItem>(`/${id}/renew`);
|
||||
}
|
||||
|
||||
static async updateAcme(
|
||||
id: number,
|
||||
payload: TlsCertificateApplyPayload,
|
||||
): Promise<TlsCertificateItem> {
|
||||
return this.post<TlsCertificateItem>(`/${id}/update-acme`, payload);
|
||||
}
|
||||
|
||||
static async convertToAcme(
|
||||
id: number,
|
||||
payload: TlsCertificateApplyPayload,
|
||||
): Promise<TlsCertificateItem> {
|
||||
return this.post<TlsCertificateItem>(`/${id}/convert-acme`, payload);
|
||||
}
|
||||
|
||||
static async importFile(
|
||||
payload: TlsCertificateFileImportPayload,
|
||||
): Promise<TlsCertificateItem> {
|
||||
const formData = new FormData();
|
||||
formData.append('name', payload.name);
|
||||
formData.append('remark', payload.remark);
|
||||
formData.append('cert_file', payload.certFile);
|
||||
formData.append('key_file', payload.keyFile);
|
||||
|
||||
return this.post<TlsCertificateItem>('/import-file', formData);
|
||||
}
|
||||
|
||||
static getDefaultAcmeAccount(): Promise<AcmeAccountItem> {
|
||||
return AcmeAccountApi.getDefault();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,9 @@
|
||||
import { OpenFlareBaseService } from './base.service';
|
||||
|
||||
export class UptimeKumaService extends OpenFlareBaseService {
|
||||
protected static override readonly basePath: string = '/api/v1/d/uptimekuma';
|
||||
|
||||
static sync(): Promise<void> {
|
||||
return this.post<void>('/sync');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import { OpenFlareBaseService } from './base.service';
|
||||
import type {
|
||||
WAFIPGroup,
|
||||
WAFIPGroupAutoTestPayload,
|
||||
WAFIPGroupAutoTestResult,
|
||||
WAFIPGroupPayload,
|
||||
WAFIPGroupSyncResult,
|
||||
WAFCreateRulePayload,
|
||||
WAFRule,
|
||||
WAFSaveRuleGraphPayload,
|
||||
WAFSiteRuleGroups,
|
||||
WAFUpdateRuleMetaPayload,
|
||||
} from './types';
|
||||
|
||||
export class WafService extends OpenFlareBaseService {
|
||||
protected static override readonly basePath: string = '/api/v1/d/waf';
|
||||
|
||||
static async listRuleGroups(): Promise<WAFRule[]> {
|
||||
return this.get<WAFRule[]>('/rule-groups');
|
||||
}
|
||||
|
||||
static async getRule(id: number): Promise<WAFRule> {
|
||||
return this.get<WAFRule>(`/rule-groups/${id}`);
|
||||
}
|
||||
|
||||
static async createRule(payload: WAFCreateRulePayload): Promise<WAFRule> {
|
||||
return this.post<WAFRule>('/rule-groups', payload);
|
||||
}
|
||||
|
||||
static async saveRuleGraph(
|
||||
id: number,
|
||||
payload: WAFSaveRuleGraphPayload,
|
||||
): Promise<WAFRule> {
|
||||
return this.post<WAFRule>(`/rule-groups/${id}/graph`, payload);
|
||||
}
|
||||
|
||||
static async updateRuleMeta(
|
||||
id: number,
|
||||
payload: WAFUpdateRuleMetaPayload,
|
||||
): Promise<WAFRule> {
|
||||
return this.post<WAFRule>(`/rule-groups/${id}/meta`, payload);
|
||||
}
|
||||
|
||||
static async deleteRuleGroup(id: number): Promise<void> {
|
||||
return this.post<void>(`/rule-groups/${id}/delete`);
|
||||
}
|
||||
|
||||
static async listSiteRuleGroups(routeId: number): Promise<WAFSiteRuleGroups> {
|
||||
return this.get<WAFSiteRuleGroups>(`/sites/${routeId}/rule-groups`);
|
||||
}
|
||||
|
||||
static async updateSiteRuleGroups(
|
||||
routeId: number,
|
||||
ids: number[],
|
||||
): Promise<WAFSiteRuleGroups> {
|
||||
return this.post<WAFSiteRuleGroups>(`/sites/${routeId}/rule-groups`, {
|
||||
ids,
|
||||
});
|
||||
}
|
||||
|
||||
static async listIPGroups(): Promise<WAFIPGroup[]> {
|
||||
return this.get<WAFIPGroup[]>('/ip-groups');
|
||||
}
|
||||
|
||||
static async getIPGroup(id: number): Promise<WAFIPGroup> {
|
||||
return this.get<WAFIPGroup>(`/ip-groups/${id}`);
|
||||
}
|
||||
|
||||
static async createIPGroup(payload: WAFIPGroupPayload): Promise<WAFIPGroup> {
|
||||
return this.post<WAFIPGroup>('/ip-groups', payload);
|
||||
}
|
||||
|
||||
static async updateIPGroup(
|
||||
id: number,
|
||||
payload: WAFIPGroupPayload,
|
||||
): Promise<WAFIPGroup> {
|
||||
return this.post<WAFIPGroup>(`/ip-groups/${id}/update`, payload);
|
||||
}
|
||||
|
||||
static async deleteIPGroup(id: number): Promise<void> {
|
||||
return this.post<void>(`/ip-groups/${id}/delete`);
|
||||
}
|
||||
|
||||
static async testIPGroup(
|
||||
payload: WAFIPGroupAutoTestPayload,
|
||||
): Promise<WAFIPGroupAutoTestResult> {
|
||||
return this.post<WAFIPGroupAutoTestResult>('/ip-groups/test', payload);
|
||||
}
|
||||
|
||||
static async syncIPGroup(id: number): Promise<WAFIPGroupSyncResult> {
|
||||
return this.post<WAFIPGroupSyncResult>(`/ip-groups/${id}/sync`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import { OpenFlareBaseService } from './base.service';
|
||||
import type {
|
||||
ZoneDomainItem,
|
||||
ZoneDomainMutationPayload,
|
||||
ZoneItem,
|
||||
ZoneMutationPayload,
|
||||
ZoneOverview,
|
||||
ZoneStats,
|
||||
ZoneStatsRange,
|
||||
} from './types';
|
||||
|
||||
export const zoneQueryKey = ['openflare', 'zones'] as const;
|
||||
|
||||
export class ZoneService extends OpenFlareBaseService {
|
||||
protected static override readonly basePath = '/api/v1/d/zones';
|
||||
|
||||
static list(): Promise<ZoneItem[]> {
|
||||
return this.get<ZoneItem[]>('/');
|
||||
}
|
||||
static getOverview(id: number): Promise<ZoneOverview> {
|
||||
return this.get<ZoneOverview>(`/${id}/overview`);
|
||||
}
|
||||
static getStats(
|
||||
id: number,
|
||||
range: ZoneStatsRange = '24h',
|
||||
): Promise<ZoneStats> {
|
||||
return this.get<ZoneStats>(`/${id}/stats`, { range });
|
||||
}
|
||||
static create(payload: ZoneMutationPayload): Promise<ZoneItem> {
|
||||
return this.post<ZoneItem>('/', payload);
|
||||
}
|
||||
static update(id: number, payload: ZoneMutationPayload): Promise<ZoneItem> {
|
||||
return this.post<ZoneItem>(`/${id}/update`, payload);
|
||||
}
|
||||
static deleteById(id: number): Promise<void> {
|
||||
return this.post<void>(`/${id}/delete`);
|
||||
}
|
||||
}
|
||||
|
||||
export class ZoneDomainService extends OpenFlareBaseService {
|
||||
protected static override readonly basePath = '/api/v1/d/zones';
|
||||
|
||||
static create(
|
||||
zoneId: number,
|
||||
payload: ZoneDomainMutationPayload,
|
||||
): Promise<ZoneDomainItem> {
|
||||
return this.post<ZoneDomainItem>(`/${zoneId}/domains`, payload);
|
||||
}
|
||||
static update(
|
||||
zoneId: number,
|
||||
id: number,
|
||||
payload: ZoneDomainMutationPayload,
|
||||
): Promise<ZoneDomainItem> {
|
||||
return this.post<ZoneDomainItem>(
|
||||
`/${zoneId}/domains/${id}/update`,
|
||||
payload,
|
||||
);
|
||||
}
|
||||
static deleteById(zoneId: number, id: number): Promise<void> {
|
||||
return this.post<void>(`/${zoneId}/domains/${id}/delete`);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { InternalAxiosRequestConfig } from 'axios';
|
||||
|
||||
import { apiConfig } from '../core/config';
|
||||
import { BaseService } from '../core/base.service';
|
||||
import type { ListUploadsResponse, Upload, UploadImageResponse } from './types';
|
||||
|
||||
@@ -23,6 +24,7 @@ export class UploadService extends BaseService {
|
||||
}
|
||||
|
||||
return this.post<Upload>('', formData, {
|
||||
timeout: apiConfig.uploadTimeout,
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
} as InternalAxiosRequestConfig);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user