Merge remote-tracking branch 'upstream/main' into feat/payment-system-v2

# Conflicts:
#	frontend/src/api/admin/settings.ts
#	frontend/src/stores/app.ts
#	frontend/src/types/index.ts
#	frontend/src/views/admin/SettingsView.vue
This commit is contained in:
erio
2026-04-11 18:24:49 +08:00
115 changed files with 3422 additions and 396 deletions

View File

@@ -38,6 +38,8 @@ export async function list(
search?: string
privacy_mode?: string
lite?: string
sort_by?: string
sort_order?: 'asc' | 'desc'
},
options?: {
signal?: AbortSignal
@@ -71,6 +73,8 @@ export async function listWithEtag(
search?: string
privacy_mode?: string
lite?: string
sort_by?: string
sort_order?: 'asc' | 'desc'
},
options?: {
signal?: AbortSignal
@@ -500,7 +504,11 @@ export async function exportData(options?: {
platform?: string
type?: string
status?: string
group?: string
privacy_mode?: string
search?: string
sort_by?: string
sort_order?: 'asc' | 'desc'
}
includeProxies?: boolean
}): Promise<AdminDataPayload> {
@@ -508,11 +516,15 @@ export async function exportData(options?: {
if (options?.ids && options.ids.length > 0) {
params.ids = options.ids.join(',')
} else if (options?.filters) {
const { platform, type, status, search } = options.filters
const { platform, type, status, group, privacy_mode, search, sort_by, sort_order } = options.filters
if (platform) params.platform = platform
if (type) params.type = type
if (status) params.status = status
if (group) params.group = group
if (privacy_mode) params.privacy_mode = privacy_mode
if (search) params.search = search
if (sort_by) params.sort_by = sort_by
if (sort_order) params.sort_order = sort_order
}
if (options?.includeProxies === false) {
params.include_proxies = 'false'

View File

@@ -17,10 +17,16 @@ export async function list(
filters?: {
status?: string
search?: string
sort_by?: string
sort_order?: 'asc' | 'desc'
},
options?: {
signal?: AbortSignal
}
): Promise<BasePaginationResponse<Announcement>> {
const { data } = await apiClient.get<BasePaginationResponse<Announcement>>('/admin/announcements', {
params: { page, page_size: pageSize, ...filters }
params: { page, page_size: pageSize, ...filters },
signal: options?.signal
})
return data
}
@@ -49,11 +55,21 @@ export async function getReadStatus(
id: number,
page: number = 1,
pageSize: number = 20,
search: string = ''
filters?: {
search?: string
sort_by?: string
sort_order?: 'asc' | 'desc'
},
options?: {
signal?: AbortSignal
}
): Promise<BasePaginationResponse<AnnouncementUserReadStatus>> {
const { data } = await apiClient.get<BasePaginationResponse<AnnouncementUserReadStatus>>(
`/admin/announcements/${id}/read-status`,
{ params: { page, page_size: pageSize, search } }
{
params: { page, page_size: pageSize, ...filters },
signal: options?.signal
}
)
return data
}
@@ -68,4 +84,3 @@ const announcementsAPI = {
}
export default announcementsAPI

View File

@@ -83,6 +83,8 @@ export async function list(
filters?: {
status?: string
search?: string
sort_by?: string
sort_order?: 'asc' | 'desc'
},
options?: { signal?: AbortSignal }
): Promise<PaginatedResponse<Channel>> {

View File

@@ -27,6 +27,8 @@ export async function list(
status?: 'active' | 'inactive'
is_exclusive?: boolean
search?: string
sort_by?: string
sort_order?: 'asc' | 'desc'
},
options?: {
signal?: AbortSignal

View File

@@ -17,10 +17,16 @@ export async function list(
filters?: {
status?: string
search?: string
sort_by?: string
sort_order?: 'asc' | 'desc'
},
options?: {
signal?: AbortSignal
}
): Promise<BasePaginationResponse<PromoCode>> {
const { data } = await apiClient.get<BasePaginationResponse<PromoCode>>('/admin/promo-codes', {
params: { page, page_size: pageSize, ...filters }
params: { page, page_size: pageSize, ...filters },
signal: options?.signal
})
return data
}

View File

@@ -29,6 +29,8 @@ export async function list(
protocol?: string
status?: 'active' | 'inactive'
search?: string
sort_by?: string
sort_order?: 'asc' | 'desc'
},
options?: {
signal?: AbortSignal
@@ -227,16 +229,20 @@ export async function exportData(options?: {
protocol?: string
status?: 'active' | 'inactive'
search?: string
sort_by?: string
sort_order?: 'asc' | 'desc'
}
}): Promise<AdminDataPayload> {
const params: Record<string, string> = {}
if (options?.ids && options.ids.length > 0) {
params.ids = options.ids.join(',')
} else if (options?.filters) {
const { protocol, status, search } = options.filters
const { protocol, status, search, sort_by, sort_order } = options.filters
if (protocol) params.protocol = protocol
if (status) params.status = status
if (search) params.search = search
if (sort_by) params.sort_by = sort_by
if (sort_order) params.sort_order = sort_order
}
const { data } = await apiClient.get<AdminDataPayload>('/admin/proxies/data', { params })
return data

View File

@@ -25,6 +25,8 @@ export async function list(
type?: RedeemCodeType
status?: 'active' | 'used' | 'expired' | 'unused'
search?: string
sort_by?: string
sort_order?: 'asc' | 'desc'
},
options?: {
signal?: AbortSignal
@@ -151,7 +153,10 @@ export async function getStats(): Promise<{
*/
export async function exportCodes(filters?: {
type?: RedeemCodeType
status?: 'active' | 'used' | 'expired'
status?: 'used' | 'expired' | 'unused'
search?: string
sort_by?: string
sort_order?: 'asc' | 'desc'
}): Promise<Blob> {
const response = await apiClient.get('/admin/redeem-codes/export', {
params: filters,

View File

@@ -38,6 +38,8 @@ export interface SystemSettings {
doc_url: string
home_content: string
hide_ccs_import_button: boolean
table_default_page_size: number
table_page_size_options: number[]
backend_mode_enabled: boolean
custom_menu_items: CustomMenuItem[]
custom_endpoints: CustomEndpoint[]
@@ -154,6 +156,8 @@ export interface UpdateSettingsRequest {
doc_url?: string
home_content?: string
hide_ccs_import_button?: boolean
table_default_page_size?: number
table_page_size_options?: number[]
backend_mode_enabled?: boolean
custom_menu_items?: CustomMenuItem[]
custom_endpoints?: CustomEndpoint[]

View File

@@ -81,6 +81,8 @@ export interface AdminUsageQueryParams extends UsageQueryParams {
user_id?: number
exact_total?: boolean
billing_mode?: string
sort_by?: string
sort_order?: 'asc' | 'desc'
}
// ==================== API Functions ====================

View File

@@ -24,6 +24,8 @@ export async function list(
group_name?: string // fuzzy filter by allowed group name
attributes?: Record<number, string> // attributeId -> value
include_subscriptions?: boolean
sort_by?: string
sort_order?: 'asc' | 'desc'
},
options?: {
signal?: AbortSignal
@@ -37,7 +39,9 @@ export async function list(
role: filters?.role,
search: filters?.search,
group_name: filters?.group_name,
include_subscriptions: filters?.include_subscriptions
include_subscriptions: filters?.include_subscriptions,
sort_by: filters?.sort_by,
sort_order: filters?.sort_order
}
// Add attribute filters as attr[id]=value

View File

@@ -17,7 +17,13 @@ import type { ApiKey, CreateApiKeyRequest, UpdateApiKeyRequest, PaginatedRespons
export async function list(
page: number = 1,
pageSize: number = 10,
filters?: { search?: string; status?: string; group_id?: number | string },
filters?: {
search?: string
status?: string
group_id?: number | string
sort_by?: string
sort_order?: 'asc' | 'desc'
},
options?: {
signal?: AbortSignal
}

View File

@@ -91,7 +91,7 @@ export async function list(
* @returns Paginated list of usage logs
*/
export async function query(
params: UsageQueryParams,
params: UsageQueryParams & { sort_by?: string; sort_order?: 'asc' | 'desc' },
config: { signal?: AbortSignal } = {}
): Promise<PaginatedResponse<UsageLog>> {
const { data } = await apiClient.get<PaginatedResponse<UsageLog>>('/usage', {