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:
ryan
2026-09-03 09:16:22 +08:00
1065 changed files with 271858 additions and 9600 deletions
+6
View File
@@ -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;
}
/**
+1 -1
View File
@@ -4,8 +4,8 @@ import type {
CreateUserRequest,
ListUsersRequest,
ListUsersResponse,
UpdateUserStatusRequest,
UpdateUserRequest,
UpdateUserStatusRequest,
} from './types';
export class AdminUserService extends BaseService {
+1 -1
View File
@@ -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();
}
+7 -7
View File
@@ -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];
+87 -19
View File
@@ -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`);
}
}
+197
View File
@@ -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);
}