You've already forked AstralRinth
forked from didirus/AstralRinth
feat: start of cross platform page system (#4731)
* feat: abstract api-client DI into ui package * feat: cross platform page system * feat: tanstack as cross platform useAsyncData * feat: archon servers routes + labrinth billing routes * fix: dont use partial * feat: migrate server list page to tanstack + api-client + re-enabled broken features! * feat: migrate servers manage page to api-client before page system * feat: migrate manage page to page system * fix: type issues * fix: upgrade wrapper bugs * refactor: move state types into api-client * feat: disable financial stuff on app frontend * feat: finalize cross platform page system for now * fix: lint * fix: build issues * feat: remove papaparse * fix: lint * fix: interface error --------- Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com>
This commit is contained in:
3
packages/api-client/src/modules/archon/index.ts
Normal file
3
packages/api-client/src/modules/archon/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './servers/types'
|
||||
export * from './servers/v0'
|
||||
export * from './servers/v1'
|
||||
57
packages/api-client/src/modules/archon/servers/v0.ts
Normal file
57
packages/api-client/src/modules/archon/servers/v0.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { AbstractModule } from '../../../core/abstract-module'
|
||||
import type { Archon } from '../types'
|
||||
|
||||
export class ArchonServersV0Module extends AbstractModule {
|
||||
public getModuleID(): string {
|
||||
return 'archon_servers_v0'
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of servers for the authenticated user
|
||||
* GET /modrinth/v0/servers
|
||||
*/
|
||||
public async list(
|
||||
options?: Archon.Servers.v0.GetServersOptions,
|
||||
): Promise<Archon.Servers.v0.ServerGetResponse> {
|
||||
const params = new URLSearchParams()
|
||||
if (options?.limit) params.set('limit', options.limit.toString())
|
||||
if (options?.offset) params.set('offset', options.offset.toString())
|
||||
|
||||
const query = params.toString() ? `?${params.toString()}` : ''
|
||||
|
||||
return this.client.request<Archon.Servers.v0.ServerGetResponse>(`servers${query}`, {
|
||||
api: 'archon',
|
||||
method: 'GET',
|
||||
version: 'modrinth/v0',
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Check stock availability for a region
|
||||
* POST /modrinth/v0/stock
|
||||
*/
|
||||
public async checkStock(
|
||||
region: string,
|
||||
request: Archon.Servers.v0.StockRequest,
|
||||
): Promise<Archon.Servers.v0.StockResponse> {
|
||||
return this.client.request<Archon.Servers.v0.StockResponse>(`/stock?region=${region}`, {
|
||||
api: 'archon',
|
||||
version: 'modrinth/v0',
|
||||
method: 'POST',
|
||||
body: request,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get filesystem authentication credentials for a server
|
||||
* Returns URL and JWT token for accessing the server's filesystem via Kyros
|
||||
* GET /modrinth/v0/servers/:id/fs
|
||||
*/
|
||||
public async getFilesystemAuth(serverId: string): Promise<Archon.Servers.v0.JWTAuth> {
|
||||
return this.client.request<Archon.Servers.v0.JWTAuth>(`/servers/${serverId}/fs`, {
|
||||
api: 'archon',
|
||||
version: 'modrinth/v0',
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
}
|
||||
20
packages/api-client/src/modules/archon/servers/v1.ts
Normal file
20
packages/api-client/src/modules/archon/servers/v1.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { AbstractModule } from '../../../core/abstract-module'
|
||||
import type { Archon } from '../types'
|
||||
|
||||
export class ArchonServersV1Module extends AbstractModule {
|
||||
public getModuleID(): string {
|
||||
return 'archon_servers_v1'
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available regions
|
||||
* GET /v1/regions
|
||||
*/
|
||||
public async getRegions(): Promise<Archon.Servers.v1.Region[]> {
|
||||
return this.client.request<Archon.Servers.v1.Region[]>('/regions', {
|
||||
api: 'archon',
|
||||
version: 1,
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
}
|
||||
128
packages/api-client/src/modules/archon/types.ts
Normal file
128
packages/api-client/src/modules/archon/types.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
export namespace Archon {
|
||||
export namespace Servers {
|
||||
export namespace v0 {
|
||||
export type ServerGetResponse = {
|
||||
servers: Server[]
|
||||
pagination: Pagination
|
||||
}
|
||||
|
||||
export type Pagination = {
|
||||
current_page: number
|
||||
page_size: number
|
||||
total_pages: number
|
||||
total_items: number
|
||||
}
|
||||
|
||||
export type Status = 'installing' | 'broken' | 'available' | 'suspended'
|
||||
|
||||
export type SuspensionReason =
|
||||
| 'moderated'
|
||||
| 'paymentfailed'
|
||||
| 'cancelled'
|
||||
| 'upgrading'
|
||||
| 'other'
|
||||
|
||||
export type Loader =
|
||||
| 'Forge'
|
||||
| 'NeoForge'
|
||||
| 'Fabric'
|
||||
| 'Quilt'
|
||||
| 'Purpur'
|
||||
| 'Spigot'
|
||||
| 'Vanilla'
|
||||
| 'Paper'
|
||||
|
||||
export type Game = 'Minecraft'
|
||||
|
||||
export type UpstreamKind = 'modpack' | 'none'
|
||||
|
||||
export type Server = {
|
||||
server_id: string
|
||||
name: string
|
||||
owner_id: string
|
||||
net: Net
|
||||
game: Game
|
||||
backup_quota: number
|
||||
used_backup_quota: number
|
||||
status: Status
|
||||
suspension_reason: SuspensionReason | null
|
||||
loader: Loader | null
|
||||
loader_version: string | null
|
||||
mc_version: string | null
|
||||
upstream: Upstream | null
|
||||
sftp_username: string
|
||||
sftp_password: string
|
||||
sftp_host: string
|
||||
datacenter: string
|
||||
notices: Notice[]
|
||||
node: NodeInfo | null
|
||||
flows: Flows
|
||||
is_medal: boolean
|
||||
|
||||
medal_expires?: string
|
||||
}
|
||||
|
||||
export type Net = {
|
||||
ip: string
|
||||
port: number
|
||||
domain: string
|
||||
}
|
||||
|
||||
export type Upstream = {
|
||||
kind: UpstreamKind
|
||||
version_id: string
|
||||
project_id: string
|
||||
}
|
||||
|
||||
export type Notice = {
|
||||
id: number
|
||||
dismissable: boolean
|
||||
title: string
|
||||
message: string
|
||||
level: string
|
||||
announced: string
|
||||
}
|
||||
|
||||
export type NodeInfo = {
|
||||
token: string
|
||||
instance: string
|
||||
}
|
||||
|
||||
export type Flows = {
|
||||
intro: boolean
|
||||
}
|
||||
|
||||
export type GetServersOptions = {
|
||||
limit?: number
|
||||
offset?: number
|
||||
}
|
||||
|
||||
export type StockRequest = {
|
||||
cpu?: number
|
||||
memory_mb?: number
|
||||
swap_mb?: number
|
||||
storage_mb?: number
|
||||
}
|
||||
|
||||
export type StockResponse = {
|
||||
available: number
|
||||
}
|
||||
|
||||
export type JWTAuth = {
|
||||
url: string // e.g., "node-xyz.modrinth.com/modrinth/v0/fs"
|
||||
token: string // JWT token for filesystem access
|
||||
}
|
||||
}
|
||||
|
||||
export namespace v1 {
|
||||
export type Region = {
|
||||
shortcode: string
|
||||
country_code: string
|
||||
display_name: string
|
||||
lat: number
|
||||
lon: number
|
||||
zone: string
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,13 @@
|
||||
import type { AbstractModrinthClient } from '../core/abstract-client'
|
||||
import type { AbstractModule } from '../core/abstract-module'
|
||||
import { ArchonServersV0Module } from './archon/servers/v0'
|
||||
import { ArchonServersV1Module } from './archon/servers/v1'
|
||||
import { ISO3166Module } from './iso3166'
|
||||
import { KyrosFilesV0Module } from './kyros/files/v0'
|
||||
import { LabrinthBillingInternalModule } from './labrinth/billing/internal'
|
||||
import { LabrinthProjectsV2Module } from './labrinth/projects/v2'
|
||||
import { LabrinthProjectsV3Module } from './labrinth/projects/v3'
|
||||
import { LabrinthStateModule } from './labrinth/state'
|
||||
|
||||
type ModuleConstructor = new (client: AbstractModrinthClient) => AbstractModule
|
||||
|
||||
@@ -15,8 +21,14 @@ type ModuleConstructor = new (client: AbstractModrinthClient) => AbstractModule
|
||||
* TODO: Better way? Probably not
|
||||
*/
|
||||
export const MODULE_REGISTRY = {
|
||||
archon_servers_v0: ArchonServersV0Module,
|
||||
archon_servers_v1: ArchonServersV1Module,
|
||||
iso3166_data: ISO3166Module,
|
||||
kyros_files_v0: KyrosFilesV0Module,
|
||||
labrinth_billing_internal: LabrinthBillingInternalModule,
|
||||
labrinth_projects_v2: LabrinthProjectsV2Module,
|
||||
labrinth_projects_v3: LabrinthProjectsV3Module,
|
||||
labrinth_state: LabrinthStateModule,
|
||||
} as const satisfies Record<string, ModuleConstructor>
|
||||
|
||||
export type ModuleID = keyof typeof MODULE_REGISTRY
|
||||
|
||||
121
packages/api-client/src/modules/iso3166/index.ts
Normal file
121
packages/api-client/src/modules/iso3166/index.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import { $fetch } from 'ofetch'
|
||||
|
||||
import { AbstractModule } from '../../core/abstract-module'
|
||||
import type { ISO3166 } from './types'
|
||||
|
||||
export type { ISO3166 } from './types'
|
||||
|
||||
const ISO3166_REPO = 'https://raw.githubusercontent.com/ipregistry/iso3166/master'
|
||||
|
||||
/**
|
||||
* Parse CSV string into array of objects
|
||||
* @param csv - CSV string to parse
|
||||
* @returns Array of objects with header keys mapped to row values
|
||||
*/
|
||||
function parseCSV(csv: string): Record<string, string>[] {
|
||||
const lines = csv
|
||||
.trim()
|
||||
.split('\n')
|
||||
.filter((line) => line.trim() !== '')
|
||||
|
||||
if (lines.length === 0) return []
|
||||
|
||||
const headerLine = lines[0]
|
||||
const headers = (headerLine.startsWith('#') ? headerLine.slice(1) : headerLine).split(',')
|
||||
|
||||
return lines.slice(1).map((line) => {
|
||||
const values = line.split(',')
|
||||
const row: Record<string, string> = {}
|
||||
|
||||
headers.forEach((header, index) => {
|
||||
row[header] = values[index] || ''
|
||||
})
|
||||
|
||||
return row
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Module for fetching ISO 3166 country and subdivision data
|
||||
* Data from https://github.com/ipregistry/iso3166 (Licensed under CC BY-SA 4.0)
|
||||
* @platform Not for use in Tauri or Nuxt environments, only node.
|
||||
*/
|
||||
export class ISO3166Module extends AbstractModule {
|
||||
public getModuleID(): string {
|
||||
return 'iso3166_data'
|
||||
}
|
||||
|
||||
/**
|
||||
* Build ISO 3166 country and subdivision data from the ipregistry repository
|
||||
*
|
||||
* @returns Promise resolving to countries and subdivisions data
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const data = await client.iso3166.data.build()
|
||||
* console.log(data.countries) // Array of country data
|
||||
* console.log(data.subdivisions['US']) // Array of US state data
|
||||
* ```
|
||||
*/
|
||||
public async build(): Promise<ISO3166.State> {
|
||||
try {
|
||||
const [countriesCSV, subdivisionsCSV] = await Promise.all([
|
||||
$fetch<string>(`${ISO3166_REPO}/countries.csv`, {
|
||||
// @ts-expect-error supports text
|
||||
responseType: 'text',
|
||||
}),
|
||||
$fetch<string>(`${ISO3166_REPO}/subdivisions.csv`, {
|
||||
// @ts-expect-error supports text
|
||||
responseType: 'text',
|
||||
}),
|
||||
])
|
||||
|
||||
const countriesData = parseCSV(countriesCSV)
|
||||
const subdivisionsData = parseCSV(subdivisionsCSV)
|
||||
|
||||
const countries: ISO3166.Country[] = countriesData.map((c) => ({
|
||||
alpha2: c.country_code_alpha2,
|
||||
alpha3: c.country_code_alpha3,
|
||||
numeric: c.numeric_code,
|
||||
nameShort: c.name_short,
|
||||
nameLong: c.name_long,
|
||||
}))
|
||||
|
||||
// Group subdivisions by country code
|
||||
const subdivisions: Record<string, ISO3166.Subdivision[]> = subdivisionsData.reduce(
|
||||
(acc, sub) => {
|
||||
const countryCode = sub.country_code_alpha2
|
||||
|
||||
if (!countryCode || typeof countryCode !== 'string' || countryCode.trim() === '') {
|
||||
return acc
|
||||
}
|
||||
|
||||
if (!acc[countryCode]) acc[countryCode] = []
|
||||
|
||||
acc[countryCode].push({
|
||||
code: sub['subdivision_code_iso3166-2'],
|
||||
name: sub.subdivision_name,
|
||||
localVariant: sub.localVariant || null,
|
||||
category: sub.category,
|
||||
parent: sub.parent_subdivision || null,
|
||||
language: sub.language_code,
|
||||
})
|
||||
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, ISO3166.Subdivision[]>,
|
||||
)
|
||||
|
||||
return {
|
||||
countries,
|
||||
subdivisions,
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error fetching ISO3166 data:', err)
|
||||
return {
|
||||
countries: [],
|
||||
subdivisions: {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
packages/api-client/src/modules/iso3166/types.ts
Normal file
23
packages/api-client/src/modules/iso3166/types.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
export namespace ISO3166 {
|
||||
export interface Country {
|
||||
alpha2: string
|
||||
alpha3: string
|
||||
numeric: string
|
||||
nameShort: string
|
||||
nameLong: string
|
||||
}
|
||||
|
||||
export interface Subdivision {
|
||||
code: string // Full ISO 3166-2 code (e.g., "US-NY")
|
||||
name: string // Official name in local language
|
||||
localVariant: string | null // English variant if different
|
||||
category: string // STATE, PROVINCE, REGION, etc.
|
||||
parent: string | null // Parent subdivision code
|
||||
language: string // Language code
|
||||
}
|
||||
|
||||
export interface State {
|
||||
countries: Country[]
|
||||
subdivisions: Record<string, Subdivision[]>
|
||||
}
|
||||
}
|
||||
52
packages/api-client/src/modules/kyros/files/v0.ts
Normal file
52
packages/api-client/src/modules/kyros/files/v0.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { AbstractModule } from '../../../core/abstract-module'
|
||||
|
||||
export class KyrosFilesV0Module extends AbstractModule {
|
||||
public getModuleID(): string {
|
||||
return 'kyros_files_v0'
|
||||
}
|
||||
|
||||
/**
|
||||
* Download a file from a server's filesystem
|
||||
*
|
||||
* @param nodeInstance - Node instance URL (e.g., "node-xyz.modrinth.com/modrinth/v0/fs")
|
||||
* @param nodeToken - JWT token from getFilesystemAuth
|
||||
* @param path - File path (e.g., "/server-icon-original.png")
|
||||
* @returns Promise resolving to file Blob
|
||||
*/
|
||||
public async downloadFile(nodeInstance: string, nodeToken: string, path: string): Promise<Blob> {
|
||||
return this.client.request<Blob>(`/fs/download`, {
|
||||
api: `https://${nodeInstance.replace('v0/fs', '')}`,
|
||||
method: 'GET',
|
||||
version: 'v0',
|
||||
params: { path },
|
||||
headers: { Authorization: `Bearer ${nodeToken}` },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload a file to a server's filesystem
|
||||
*
|
||||
* @param nodeInstance - Node instance URL
|
||||
* @param nodeToken - JWT token from getFilesystemAuth
|
||||
* @param path - Destination path (e.g., "/server-icon.png")
|
||||
* @param file - File to upload
|
||||
*/
|
||||
public async uploadFile(
|
||||
nodeInstance: string,
|
||||
nodeToken: string,
|
||||
path: string,
|
||||
file: File,
|
||||
): Promise<void> {
|
||||
return this.client.request<void>(`/fs/create`, {
|
||||
api: `https://${nodeInstance.replace('v0/fs', '')}`,
|
||||
method: 'POST',
|
||||
version: 'v0',
|
||||
params: { path, type: 'file' },
|
||||
headers: {
|
||||
Authorization: `Bearer ${nodeToken}`,
|
||||
'Content-Type': 'application/octet-stream',
|
||||
},
|
||||
body: file,
|
||||
})
|
||||
}
|
||||
}
|
||||
7
packages/api-client/src/modules/kyros/types.ts
Normal file
7
packages/api-client/src/modules/kyros/types.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export namespace Kyros {
|
||||
export namespace Files {
|
||||
export namespace v0 {
|
||||
// Empty for now
|
||||
}
|
||||
}
|
||||
}
|
||||
189
packages/api-client/src/modules/labrinth/billing/internal.ts
Normal file
189
packages/api-client/src/modules/labrinth/billing/internal.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
import { AbstractModule } from '../../../core/abstract-module'
|
||||
import type { Labrinth } from '../types'
|
||||
|
||||
export class LabrinthBillingInternalModule extends AbstractModule {
|
||||
public getModuleID(): string {
|
||||
return 'labrinth_billing_internal'
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user's subscriptions
|
||||
* GET /_internal/billing/subscriptions
|
||||
*/
|
||||
public async getSubscriptions(
|
||||
userId?: string,
|
||||
): Promise<Labrinth.Billing.Internal.UserSubscription[]> {
|
||||
const params = userId ? `?user_id=${userId}` : ''
|
||||
|
||||
return this.client.request<Labrinth.Billing.Internal.UserSubscription[]>(
|
||||
`/billing/subscriptions${params}`,
|
||||
{
|
||||
api: 'labrinth',
|
||||
version: 'internal',
|
||||
method: 'GET',
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available products for purchase
|
||||
* GET /_internal/billing/products
|
||||
*/
|
||||
public async getProducts(): Promise<Labrinth.Billing.Internal.Product[]> {
|
||||
return this.client.request<Labrinth.Billing.Internal.Product[]>('/billing/products', {
|
||||
api: 'labrinth',
|
||||
version: 'internal',
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Stripe customer information
|
||||
* GET /_internal/billing/customer
|
||||
*/
|
||||
public async getCustomer(): Promise<unknown> {
|
||||
return this.client.request<unknown>('/billing/customer', {
|
||||
api: 'labrinth',
|
||||
version: 'internal',
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a subscription (change product, interval, cancel, etc.)
|
||||
* PATCH /_internal/billing/subscription/{id}
|
||||
*/
|
||||
public async editSubscription(
|
||||
id: string,
|
||||
edit: Labrinth.Billing.Internal.EditSubscriptionRequest,
|
||||
dry?: boolean,
|
||||
): Promise<Labrinth.Billing.Internal.EditSubscriptionResponse | void> {
|
||||
const params = dry ? '?dry=true' : ''
|
||||
|
||||
return this.client.request<Labrinth.Billing.Internal.EditSubscriptionResponse | void>(
|
||||
`/billing/subscription/${id}${params}`,
|
||||
{
|
||||
api: 'labrinth',
|
||||
version: 'internal',
|
||||
method: 'PATCH',
|
||||
body: edit,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user's payment methods
|
||||
* GET /_internal/billing/payment_methods
|
||||
*/
|
||||
public async getPaymentMethods(): Promise<unknown[]> {
|
||||
return this.client.request<unknown[]>('/billing/payment_methods', {
|
||||
api: 'labrinth',
|
||||
version: 'internal',
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate flow to add a new payment method
|
||||
* POST /_internal/billing/payment_method
|
||||
*/
|
||||
public async addPaymentMethodFlow(): Promise<Labrinth.Billing.Internal.AddPaymentMethodFlowResponse> {
|
||||
return this.client.request<Labrinth.Billing.Internal.AddPaymentMethodFlowResponse>(
|
||||
'/billing/payment_method',
|
||||
{
|
||||
api: 'labrinth',
|
||||
version: 'internal',
|
||||
method: 'POST',
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a payment method (set as primary)
|
||||
* PATCH /_internal/billing/payment_method/{id}
|
||||
*/
|
||||
public async editPaymentMethod(
|
||||
id: string,
|
||||
body: Labrinth.Billing.Internal.EditPaymentMethodRequest,
|
||||
): Promise<void> {
|
||||
return this.client.request<void>(`/billing/payment_method/${id}`, {
|
||||
api: 'labrinth',
|
||||
version: 'internal',
|
||||
method: 'PATCH',
|
||||
body,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a payment method
|
||||
* DELETE /_internal/billing/payment_method/{id}
|
||||
*/
|
||||
public async removePaymentMethod(id: string): Promise<void> {
|
||||
return this.client.request<void>(`/billing/payment_method/${id}`, {
|
||||
api: 'labrinth',
|
||||
version: 'internal',
|
||||
method: 'DELETE',
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get payment history (charges)
|
||||
* GET /_internal/billing/payments
|
||||
*/
|
||||
public async getPayments(userId?: string): Promise<Labrinth.Billing.Internal.Charge[]> {
|
||||
const params = userId ? `?user_id=${userId}` : ''
|
||||
|
||||
return this.client.request<Labrinth.Billing.Internal.Charge[]>(`/billing/payments${params}`, {
|
||||
api: 'labrinth',
|
||||
version: 'internal',
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate a payment
|
||||
* POST /_internal/billing/payment
|
||||
*/
|
||||
public async initiatePayment(
|
||||
request: Labrinth.Billing.Internal.InitiatePaymentRequest,
|
||||
): Promise<Labrinth.Billing.Internal.InitiatePaymentResponse> {
|
||||
return this.client.request<Labrinth.Billing.Internal.InitiatePaymentResponse>(
|
||||
'/billing/payment',
|
||||
{
|
||||
api: 'labrinth',
|
||||
version: 'internal',
|
||||
method: 'POST',
|
||||
body: request,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Refund a charge (Admin only)
|
||||
* POST /_internal/billing/charge/{id}/refund
|
||||
*/
|
||||
public async refundCharge(
|
||||
id: string,
|
||||
refund: Labrinth.Billing.Internal.RefundChargeRequest,
|
||||
): Promise<void> {
|
||||
return this.client.request<void>(`/billing/charge/${id}/refund`, {
|
||||
api: 'labrinth',
|
||||
version: 'internal',
|
||||
method: 'POST',
|
||||
body: refund,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Credit subscriptions (Admin only)
|
||||
* POST /_internal/billing/credit
|
||||
*/
|
||||
public async credit(request: Labrinth.Billing.Internal.CreditRequest): Promise<void> {
|
||||
return this.client.request<void>('/billing/credit', {
|
||||
api: 'labrinth',
|
||||
version: 'internal',
|
||||
method: 'POST',
|
||||
body: request,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,4 @@
|
||||
export * from './billing/internal'
|
||||
export * from './projects/v2'
|
||||
export * from './projects/v3'
|
||||
export * from './state'
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
export type Environment = 'required' | 'optional' | 'unsupported' | 'unknown'
|
||||
|
||||
export type ProjectStatus =
|
||||
| 'approved'
|
||||
| 'archived'
|
||||
| 'rejected'
|
||||
| 'draft'
|
||||
| 'unlisted'
|
||||
| 'processing'
|
||||
| 'withheld'
|
||||
| 'scheduled'
|
||||
| 'private'
|
||||
| 'unknown'
|
||||
|
||||
export type MonetizationStatus = 'monetized' | 'demonetized' | 'force-demonetized'
|
||||
|
||||
export type ProjectType = 'mod' | 'modpack' | 'resourcepack' | 'shader' | 'plugin' | 'datapack'
|
||||
|
||||
export type GalleryImageV2 = {
|
||||
url: string
|
||||
featured: boolean
|
||||
title?: string
|
||||
description?: string
|
||||
created: string
|
||||
ordering: number
|
||||
}
|
||||
|
||||
export type DonationLinkV2 = {
|
||||
id: string
|
||||
platform: string
|
||||
url: string
|
||||
}
|
||||
|
||||
export type ProjectV2 = {
|
||||
id: string
|
||||
slug: string
|
||||
project_type: ProjectType
|
||||
team: string
|
||||
title: string
|
||||
description: string
|
||||
body: string
|
||||
published: string
|
||||
updated: string
|
||||
approved?: string
|
||||
queued?: string
|
||||
status: ProjectStatus
|
||||
requested_status?: ProjectStatus
|
||||
moderator_message?: {
|
||||
message: string
|
||||
body?: string
|
||||
}
|
||||
license: {
|
||||
id: string
|
||||
name: string
|
||||
url?: string
|
||||
}
|
||||
client_side: Environment
|
||||
server_side: Environment
|
||||
downloads: number
|
||||
followers: number
|
||||
categories: string[]
|
||||
additional_categories: string[]
|
||||
game_versions: string[]
|
||||
loaders: string[]
|
||||
versions: string[]
|
||||
icon_url?: string
|
||||
issues_url?: string
|
||||
source_url?: string
|
||||
wiki_url?: string
|
||||
discord_url?: string
|
||||
donation_urls?: DonationLinkV2[]
|
||||
gallery?: GalleryImageV2[]
|
||||
color?: number
|
||||
thread_id: string
|
||||
monetization_status: MonetizationStatus
|
||||
}
|
||||
|
||||
export type SearchResultHit = {
|
||||
project_id: string
|
||||
project_type: ProjectType
|
||||
slug: string
|
||||
author: string
|
||||
title: string
|
||||
description: string
|
||||
categories: string[]
|
||||
display_categories: string[]
|
||||
versions: string[]
|
||||
downloads: number
|
||||
follows: number
|
||||
icon_url: string
|
||||
date_created: string
|
||||
date_modified: string
|
||||
latest_version?: string
|
||||
license: string
|
||||
client_side: Environment
|
||||
server_side: Environment
|
||||
gallery: string[]
|
||||
color?: number
|
||||
}
|
||||
|
||||
export type SearchResult = {
|
||||
hits: SearchResultHit[]
|
||||
offset: number
|
||||
limit: number
|
||||
total_hits: number
|
||||
}
|
||||
|
||||
export type ProjectSearchParams = {
|
||||
query?: string
|
||||
facets?: string[][]
|
||||
filters?: string
|
||||
index?: 'relevance' | 'downloads' | 'follows' | 'newest' | 'updated'
|
||||
offset?: number
|
||||
limit?: number
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
import type { MonetizationStatus, ProjectStatus } from './v2'
|
||||
|
||||
export type GalleryItemV3 = {
|
||||
url: string
|
||||
raw_url: string
|
||||
featured: boolean
|
||||
name?: string
|
||||
description?: string
|
||||
created: string
|
||||
ordering: number
|
||||
}
|
||||
|
||||
export type LinkV3 = {
|
||||
platform: string
|
||||
donation: boolean
|
||||
url: string
|
||||
}
|
||||
|
||||
export type ProjectV3 = {
|
||||
id: string
|
||||
slug?: string
|
||||
project_types: string[]
|
||||
games: string[]
|
||||
team_id: string
|
||||
organization?: string
|
||||
name: string
|
||||
summary: string
|
||||
description: string
|
||||
published: string
|
||||
updated: string
|
||||
approved?: string
|
||||
queued?: string
|
||||
status: ProjectStatus
|
||||
requested_status?: ProjectStatus
|
||||
license: {
|
||||
id: string
|
||||
name: string
|
||||
url?: string
|
||||
}
|
||||
downloads: number
|
||||
followers: number
|
||||
categories: string[]
|
||||
additional_categories: string[]
|
||||
loaders: string[]
|
||||
versions: string[]
|
||||
icon_url?: string
|
||||
link_urls: Record<string, LinkV3>
|
||||
gallery: GalleryItemV3[]
|
||||
color?: number
|
||||
thread_id: string
|
||||
monetization_status: MonetizationStatus
|
||||
side_types_migration_review_status: 'reviewed' | 'pending'
|
||||
[key: string]: unknown
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AbstractModule } from '../../../core/abstract-module'
|
||||
import type { ProjectSearchParams, ProjectV2, SearchResult } from './types/v2'
|
||||
import type { Labrinth } from '../types'
|
||||
|
||||
export class LabrinthProjectsV2Module extends AbstractModule {
|
||||
public getModuleID(): string {
|
||||
@@ -18,8 +18,8 @@ export class LabrinthProjectsV2Module extends AbstractModule {
|
||||
* console.log(project.title) // "Sodium"
|
||||
* ```
|
||||
*/
|
||||
public async get(id: string): Promise<ProjectV2> {
|
||||
return this.client.request<ProjectV2>(`/project/${id}`, {
|
||||
public async get(id: string): Promise<Labrinth.Projects.v2.Project> {
|
||||
return this.client.request<Labrinth.Projects.v2.Project>(`/project/${id}`, {
|
||||
api: 'labrinth',
|
||||
version: 2,
|
||||
method: 'GET',
|
||||
@@ -37,8 +37,8 @@ export class LabrinthProjectsV2Module extends AbstractModule {
|
||||
* const projects = await client.labrinth.projects_v2.getMultiple(['sodium', 'lithium', 'phosphor'])
|
||||
* ```
|
||||
*/
|
||||
public async getMultiple(ids: string[]): Promise<ProjectV2[]> {
|
||||
return this.client.request<ProjectV2[]>(`/projects`, {
|
||||
public async getMultiple(ids: string[]): Promise<Labrinth.Projects.v2.Project[]> {
|
||||
return this.client.request<Labrinth.Projects.v2.Project[]>(`/projects`, {
|
||||
api: 'labrinth',
|
||||
version: 2,
|
||||
method: 'GET',
|
||||
@@ -61,8 +61,10 @@ export class LabrinthProjectsV2Module extends AbstractModule {
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
public async search(params: ProjectSearchParams): Promise<SearchResult> {
|
||||
return this.client.request<SearchResult>(`/search`, {
|
||||
public async search(
|
||||
params: Labrinth.Projects.v2.ProjectSearchParams,
|
||||
): Promise<Labrinth.Projects.v2.SearchResult> {
|
||||
return this.client.request<Labrinth.Projects.v2.SearchResult>(`/search`, {
|
||||
api: 'labrinth',
|
||||
version: 2,
|
||||
method: 'GET',
|
||||
@@ -83,7 +85,7 @@ export class LabrinthProjectsV2Module extends AbstractModule {
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
public async edit(id: string, data: Partial<ProjectV2>): Promise<void> {
|
||||
public async edit(id: string, data: Partial<Labrinth.Projects.v2.Project>): Promise<void> {
|
||||
return this.client.request(`/project/${id}`, {
|
||||
api: 'labrinth',
|
||||
version: 2,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AbstractModule } from '../../../core/abstract-module'
|
||||
import type { ProjectV3 } from './types/v3'
|
||||
import type { Labrinth } from '../types'
|
||||
|
||||
export class LabrinthProjectsV3Module extends AbstractModule {
|
||||
public getModuleID(): string {
|
||||
@@ -18,8 +18,8 @@ export class LabrinthProjectsV3Module extends AbstractModule {
|
||||
* console.log(project.project_types) // v3 field
|
||||
* ```
|
||||
*/
|
||||
public async get(id: string): Promise<ProjectV3> {
|
||||
return this.client.request<ProjectV3>(`/project/${id}`, {
|
||||
public async get(id: string): Promise<Labrinth.Projects.v3.Project> {
|
||||
return this.client.request<Labrinth.Projects.v3.Project>(`/project/${id}`, {
|
||||
api: 'labrinth',
|
||||
version: 3,
|
||||
method: 'GET',
|
||||
@@ -37,8 +37,8 @@ export class LabrinthProjectsV3Module extends AbstractModule {
|
||||
* const projects = await client.labrinth.projects_v3.getMultiple(['sodium', 'lithium'])
|
||||
* ```
|
||||
*/
|
||||
public async getMultiple(ids: string[]): Promise<ProjectV3[]> {
|
||||
return this.client.request<ProjectV3[]>(`/projects`, {
|
||||
public async getMultiple(ids: string[]): Promise<Labrinth.Projects.v3.Project[]> {
|
||||
return this.client.request<Labrinth.Projects.v3.Project[]>(`/projects`, {
|
||||
api: 'labrinth',
|
||||
version: 3,
|
||||
method: 'GET',
|
||||
@@ -59,7 +59,7 @@ export class LabrinthProjectsV3Module extends AbstractModule {
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
public async edit(id: string, data: Partial<ProjectV3>): Promise<void> {
|
||||
public async edit(id: string, data: Labrinth.Projects.v3.EditProjectRequest): Promise<void> {
|
||||
return this.client.request(`/project/${id}`, {
|
||||
api: 'labrinth',
|
||||
version: 3,
|
||||
|
||||
135
packages/api-client/src/modules/labrinth/state/index.ts
Normal file
135
packages/api-client/src/modules/labrinth/state/index.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import { AbstractModule } from '../../../core/abstract-module'
|
||||
import type { Labrinth } from '../types'
|
||||
|
||||
export class LabrinthStateModule extends AbstractModule {
|
||||
public getModuleID(): string {
|
||||
return 'labrinth_state'
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the complete generated state by fetching from multiple endpoints
|
||||
*
|
||||
* @returns Promise resolving to the generated state containing categories, loaders, products, etc.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const state = await client.labrinth.state.build()
|
||||
* console.log(state.products) // Available billing products
|
||||
* ```
|
||||
*/
|
||||
public async build(): Promise<Labrinth.State.GeneratedState> {
|
||||
const errors: unknown[] = []
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const handleError = (err: any, defaultValue: any) => {
|
||||
console.error('Error fetching state data:', err)
|
||||
errors.push(err)
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// TODO: as we add new modules, move these raw requests to actual
|
||||
// abstractions
|
||||
const [
|
||||
categories,
|
||||
loaders,
|
||||
gameVersions,
|
||||
donationPlatforms,
|
||||
reportTypes,
|
||||
homePageProjects,
|
||||
homePageSearch,
|
||||
homePageNotifs,
|
||||
products,
|
||||
muralBankDetails,
|
||||
iso3166Data,
|
||||
] = await Promise.all([
|
||||
// Tag endpoints
|
||||
this.client
|
||||
.request<Labrinth.Tags.v2.Category[]>('/tag/category', {
|
||||
api: 'labrinth',
|
||||
version: 2,
|
||||
method: 'GET',
|
||||
})
|
||||
.catch((err) => handleError(err, [])),
|
||||
this.client
|
||||
.request<Labrinth.Tags.v2.Loader[]>('/tag/loader', {
|
||||
api: 'labrinth',
|
||||
version: 2,
|
||||
method: 'GET',
|
||||
})
|
||||
.catch((err) => handleError(err, [])),
|
||||
this.client
|
||||
.request<Labrinth.Tags.v2.GameVersion[]>('/tag/game_version', {
|
||||
api: 'labrinth',
|
||||
version: 2,
|
||||
method: 'GET',
|
||||
})
|
||||
.catch((err) => handleError(err, [])),
|
||||
this.client
|
||||
.request<Labrinth.Tags.v2.DonationPlatform[]>('/tag/donation_platform', {
|
||||
api: 'labrinth',
|
||||
version: 2,
|
||||
method: 'GET',
|
||||
})
|
||||
.catch((err) => handleError(err, [])),
|
||||
this.client
|
||||
.request<string[]>('/tag/report_type', { api: 'labrinth', version: 2, method: 'GET' })
|
||||
.catch((err) => handleError(err, [])),
|
||||
|
||||
// Homepage data
|
||||
this.client
|
||||
.request<Labrinth.Projects.v2.Project[]>('/projects_random', {
|
||||
api: 'labrinth',
|
||||
version: 2,
|
||||
method: 'GET',
|
||||
params: { count: '60' },
|
||||
})
|
||||
.catch((err) => handleError(err, [])),
|
||||
this.client
|
||||
.request<Labrinth.Search.v2.SearchResults>('/search', {
|
||||
api: 'labrinth',
|
||||
version: 2,
|
||||
method: 'GET',
|
||||
params: { limit: '3', query: 'leave', index: 'relevance' },
|
||||
})
|
||||
.catch((err) => handleError(err, {} as Labrinth.Search.v2.SearchResults)),
|
||||
this.client
|
||||
.request<Labrinth.Search.v2.SearchResults>('/search', {
|
||||
api: 'labrinth',
|
||||
version: 2,
|
||||
method: 'GET',
|
||||
params: { limit: '3', query: '', index: 'updated' },
|
||||
})
|
||||
.catch((err) => handleError(err, {} as Labrinth.Search.v2.SearchResults)),
|
||||
|
||||
// Internal billing/mural endpoints
|
||||
this.client.labrinth.billing_internal.getProducts().catch((err) => handleError(err, [])),
|
||||
this.client
|
||||
.request<{ bankDetails: Record<string, { bankNames: string[] }> }>('/mural/bank-details', {
|
||||
api: 'labrinth',
|
||||
version: 'internal',
|
||||
method: 'GET',
|
||||
})
|
||||
.catch((err) => handleError(err, null)),
|
||||
|
||||
// ISO3166 country and subdivision data
|
||||
this.client.iso3166.data
|
||||
.build()
|
||||
.catch((err) => handleError(err, { countries: [], subdivisions: {} })),
|
||||
])
|
||||
|
||||
return {
|
||||
categories,
|
||||
loaders,
|
||||
gameVersions,
|
||||
donationPlatforms,
|
||||
reportTypes,
|
||||
homePageProjects,
|
||||
homePageSearch,
|
||||
homePageNotifs,
|
||||
products,
|
||||
muralBankDetails: muralBankDetails?.bankDetails,
|
||||
countries: iso3166Data.countries,
|
||||
subdivisions: iso3166Data.subdivisions,
|
||||
errors,
|
||||
}
|
||||
}
|
||||
}
|
||||
453
packages/api-client/src/modules/labrinth/types.ts
Normal file
453
packages/api-client/src/modules/labrinth/types.ts
Normal file
@@ -0,0 +1,453 @@
|
||||
import type { ISO3166 } from '../iso3166/types'
|
||||
|
||||
export namespace Labrinth {
|
||||
export namespace Billing {
|
||||
export namespace Internal {
|
||||
export type PriceDuration = 'five-days' | 'monthly' | 'quarterly' | 'yearly'
|
||||
|
||||
export type SubscriptionStatus = 'provisioned' | 'unprovisioned'
|
||||
|
||||
export type UserSubscription = {
|
||||
id: string
|
||||
user_id: string
|
||||
price_id: string
|
||||
interval: PriceDuration
|
||||
status: SubscriptionStatus
|
||||
created: string // ISO datetime string
|
||||
metadata?: SubscriptionMetadata
|
||||
}
|
||||
|
||||
export type SubscriptionMetadata =
|
||||
| { type: 'pyro'; id: string; region?: string }
|
||||
| { type: 'medal'; id: string }
|
||||
|
||||
export type ChargeStatus =
|
||||
| 'open'
|
||||
| 'processing'
|
||||
| 'succeeded'
|
||||
| 'failed'
|
||||
| 'cancelled'
|
||||
| 'expiring'
|
||||
|
||||
export type ChargeType = 'one-time' | 'subscription' | 'proration' | 'refund'
|
||||
|
||||
export type PaymentPlatform = 'Stripe' | 'None'
|
||||
|
||||
export type Charge = {
|
||||
id: string
|
||||
user_id: string
|
||||
price_id: string
|
||||
amount: number
|
||||
currency_code: string
|
||||
status: ChargeStatus
|
||||
due: string // ISO datetime string
|
||||
last_attempt: string | null // ISO datetime string
|
||||
type: ChargeType
|
||||
subscription_id: string | null
|
||||
subscription_interval: PriceDuration | null
|
||||
platform: PaymentPlatform
|
||||
parent_charge_id: string | null
|
||||
net: number | null
|
||||
}
|
||||
|
||||
export type ProductMetadata =
|
||||
| { type: 'midas' }
|
||||
| {
|
||||
type: 'pyro'
|
||||
cpu: number
|
||||
ram: number
|
||||
swap: number
|
||||
storage: number
|
||||
}
|
||||
| {
|
||||
type: 'medal'
|
||||
cpu: number
|
||||
ram: number
|
||||
swap: number
|
||||
storage: number
|
||||
region: string
|
||||
}
|
||||
|
||||
export type Price =
|
||||
| { type: 'one-time'; price: number }
|
||||
| { type: 'recurring'; intervals: Record<PriceDuration, number> }
|
||||
|
||||
export type ProductPrice = {
|
||||
id: string
|
||||
product_id: string
|
||||
prices: Price
|
||||
currency_code: string
|
||||
}
|
||||
|
||||
export type Product = {
|
||||
id: string
|
||||
metadata: ProductMetadata
|
||||
prices: ProductPrice[]
|
||||
unitary: boolean
|
||||
}
|
||||
|
||||
export type EditSubscriptionRequest = {
|
||||
interval?: PriceDuration
|
||||
payment_method?: string
|
||||
cancelled?: boolean
|
||||
region?: string
|
||||
product?: string
|
||||
}
|
||||
|
||||
export type EditSubscriptionResponse = {
|
||||
payment_intent_id: string
|
||||
client_secret: string
|
||||
tax: number
|
||||
total: number
|
||||
}
|
||||
|
||||
export type AddPaymentMethodFlowResponse = {
|
||||
client_secret: string
|
||||
}
|
||||
|
||||
export type EditPaymentMethodRequest = {
|
||||
primary: boolean
|
||||
}
|
||||
|
||||
export type InitiatePaymentRequest = {
|
||||
type: 'payment_method' | 'confirmation_token'
|
||||
id?: string
|
||||
token?: string
|
||||
charge:
|
||||
| { type: 'existing'; id: string }
|
||||
| { type: 'new'; product_id: string; interval?: PriceDuration }
|
||||
existing_payment_intent?: string
|
||||
metadata?: {
|
||||
type: 'pyro'
|
||||
server_name?: string
|
||||
server_region?: string
|
||||
source: unknown
|
||||
}
|
||||
}
|
||||
|
||||
export type InitiatePaymentResponse = {
|
||||
payment_intent_id?: string
|
||||
client_secret?: string
|
||||
price_id: string
|
||||
tax: number
|
||||
total: number
|
||||
payment_method?: string
|
||||
}
|
||||
|
||||
export type RefundChargeRequest = {
|
||||
type: 'full' | 'partial' | 'none'
|
||||
amount?: number
|
||||
unprovision?: boolean
|
||||
}
|
||||
|
||||
export type CreditRequest =
|
||||
| { subscription_ids: string[]; days: number; send_email: boolean; message: string }
|
||||
| { nodes: string[]; days: number; send_email: boolean; message: string }
|
||||
| { region: string; days: number; send_email: boolean; message: string }
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Projects {
|
||||
export namespace v2 {
|
||||
export type Environment = 'required' | 'optional' | 'unsupported' | 'unknown'
|
||||
|
||||
export type ProjectStatus =
|
||||
| 'approved'
|
||||
| 'archived'
|
||||
| 'rejected'
|
||||
| 'draft'
|
||||
| 'unlisted'
|
||||
| 'processing'
|
||||
| 'withheld'
|
||||
| 'scheduled'
|
||||
| 'private'
|
||||
| 'unknown'
|
||||
|
||||
export type MonetizationStatus = 'monetized' | 'demonetized' | 'force-demonetized'
|
||||
|
||||
export type ProjectType =
|
||||
| 'mod'
|
||||
| 'modpack'
|
||||
| 'resourcepack'
|
||||
| 'shader'
|
||||
| 'plugin'
|
||||
| 'datapack'
|
||||
|
||||
export type GalleryImage = {
|
||||
url: string
|
||||
featured: boolean
|
||||
title?: string
|
||||
description?: string
|
||||
created: string
|
||||
ordering: number
|
||||
}
|
||||
|
||||
export type DonationLink = {
|
||||
id: string
|
||||
platform: string
|
||||
url: string
|
||||
}
|
||||
|
||||
export type Project = {
|
||||
id: string
|
||||
slug: string
|
||||
project_type: ProjectType
|
||||
team: string
|
||||
title: string
|
||||
description: string
|
||||
body: string
|
||||
published: string
|
||||
updated: string
|
||||
approved?: string
|
||||
queued?: string
|
||||
status: ProjectStatus
|
||||
requested_status?: ProjectStatus
|
||||
moderator_message?: {
|
||||
message: string
|
||||
body?: string
|
||||
}
|
||||
license: {
|
||||
id: string
|
||||
name: string
|
||||
url?: string
|
||||
}
|
||||
client_side: Environment
|
||||
server_side: Environment
|
||||
downloads: number
|
||||
followers: number
|
||||
categories: string[]
|
||||
additional_categories: string[]
|
||||
game_versions: string[]
|
||||
loaders: string[]
|
||||
versions: string[]
|
||||
icon_url?: string
|
||||
issues_url?: string
|
||||
source_url?: string
|
||||
wiki_url?: string
|
||||
discord_url?: string
|
||||
donation_urls?: DonationLink[]
|
||||
gallery?: GalleryImage[]
|
||||
color?: number
|
||||
thread_id: string
|
||||
monetization_status: MonetizationStatus
|
||||
}
|
||||
|
||||
export type SearchResultHit = {
|
||||
project_id: string
|
||||
project_type: ProjectType
|
||||
slug: string
|
||||
author: string
|
||||
title: string
|
||||
description: string
|
||||
categories: string[]
|
||||
display_categories: string[]
|
||||
versions: string[]
|
||||
downloads: number
|
||||
follows: number
|
||||
icon_url: string
|
||||
date_created: string
|
||||
date_modified: string
|
||||
latest_version?: string
|
||||
license: string
|
||||
client_side: Environment
|
||||
server_side: Environment
|
||||
gallery: string[]
|
||||
color?: number
|
||||
}
|
||||
|
||||
export type SearchResult = {
|
||||
hits: SearchResultHit[]
|
||||
offset: number
|
||||
limit: number
|
||||
total_hits: number
|
||||
}
|
||||
|
||||
export type ProjectSearchParams = {
|
||||
query?: string
|
||||
facets?: string[][]
|
||||
filters?: string
|
||||
index?: 'relevance' | 'downloads' | 'follows' | 'newest' | 'updated'
|
||||
offset?: number
|
||||
limit?: number
|
||||
}
|
||||
}
|
||||
|
||||
export namespace v3 {
|
||||
export type Environment =
|
||||
| 'client_and_server'
|
||||
| 'client_only'
|
||||
| 'client_only_server_optional'
|
||||
| 'singleplayer_only'
|
||||
| 'server_only'
|
||||
| 'server_only_client_optional'
|
||||
| 'dedicated_server_only'
|
||||
| 'client_or_server'
|
||||
| 'client_or_server_prefers_both'
|
||||
| 'unknown'
|
||||
|
||||
export type GalleryItem = {
|
||||
url: string
|
||||
raw_url: string
|
||||
featured: boolean
|
||||
name?: string
|
||||
description?: string
|
||||
created: string
|
||||
ordering: number
|
||||
}
|
||||
|
||||
export type Link = {
|
||||
platform: string
|
||||
donation: boolean
|
||||
url: string
|
||||
}
|
||||
|
||||
export type Project = {
|
||||
id: string
|
||||
slug?: string
|
||||
project_types: string[]
|
||||
games: string[]
|
||||
team_id: string
|
||||
organization?: string
|
||||
name: string
|
||||
summary: string
|
||||
description: string
|
||||
published: string
|
||||
updated: string
|
||||
approved?: string
|
||||
queued?: string
|
||||
status: v2.ProjectStatus
|
||||
requested_status?: v2.ProjectStatus
|
||||
license: {
|
||||
id: string
|
||||
name: string
|
||||
url?: string
|
||||
}
|
||||
downloads: number
|
||||
followers: number
|
||||
categories: string[]
|
||||
additional_categories: string[]
|
||||
loaders: string[]
|
||||
versions: string[]
|
||||
icon_url?: string
|
||||
link_urls: Record<string, Link>
|
||||
gallery: GalleryItem[]
|
||||
color?: number
|
||||
thread_id: string
|
||||
monetization_status: v2.MonetizationStatus
|
||||
side_types_migration_review_status: 'reviewed' | 'pending'
|
||||
environment?: Environment[]
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export type EditProjectRequest = {
|
||||
name?: string
|
||||
summary?: string
|
||||
description?: string
|
||||
categories?: string[]
|
||||
additional_categories?: string[]
|
||||
license_url?: string | null
|
||||
link_urls?: Record<string, string | null>
|
||||
license_id?: string
|
||||
slug?: string
|
||||
status?: v2.ProjectStatus
|
||||
requested_status?: v2.ProjectStatus | null
|
||||
moderation_message?: string | null
|
||||
moderation_message_body?: string | null
|
||||
monetization_status?: v2.MonetizationStatus
|
||||
side_types_migration_review_status?: 'reviewed' | 'pending'
|
||||
environment?: Environment
|
||||
[key: string]: unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Tags {
|
||||
export namespace v2 {
|
||||
export interface Category {
|
||||
icon: string
|
||||
name: string
|
||||
project_type: string
|
||||
header: string
|
||||
}
|
||||
|
||||
export interface Loader {
|
||||
icon: string
|
||||
name: string
|
||||
supported_project_types: string[]
|
||||
}
|
||||
|
||||
export interface GameVersion {
|
||||
version: string
|
||||
version_type: string
|
||||
date: string // RFC 3339 DateTime
|
||||
major: boolean
|
||||
}
|
||||
|
||||
export interface DonationPlatform {
|
||||
short: string
|
||||
name: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Search {
|
||||
export namespace v2 {
|
||||
export interface ResultSearchProject {
|
||||
project_id: string
|
||||
project_type: string
|
||||
slug: string | null
|
||||
author: string
|
||||
title: string
|
||||
description: string
|
||||
categories: string[]
|
||||
display_categories: string[]
|
||||
versions: string[]
|
||||
downloads: number
|
||||
follows: number
|
||||
icon_url: string
|
||||
date_created: string
|
||||
date_modified: string
|
||||
latest_version: string
|
||||
license: string
|
||||
client_side: string
|
||||
server_side: string
|
||||
gallery: string[]
|
||||
featured_gallery: string | null
|
||||
color: number | null
|
||||
}
|
||||
|
||||
export interface SearchResults {
|
||||
hits: ResultSearchProject[]
|
||||
offset: number
|
||||
limit: number
|
||||
total_hits: number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export namespace State {
|
||||
export interface GeneratedState {
|
||||
categories: Tags.v2.Category[]
|
||||
loaders: Tags.v2.Loader[]
|
||||
gameVersions: Tags.v2.GameVersion[]
|
||||
donationPlatforms: Tags.v2.DonationPlatform[]
|
||||
reportTypes: string[]
|
||||
muralBankDetails?: Record<
|
||||
string,
|
||||
{
|
||||
bankNames: string[]
|
||||
}
|
||||
>
|
||||
|
||||
homePageProjects?: Projects.v2.Project[]
|
||||
homePageSearch?: Search.v2.SearchResults
|
||||
homePageNotifs?: Search.v2.SearchResults
|
||||
products?: Billing.Internal.Product[]
|
||||
|
||||
countries: ISO3166.Country[]
|
||||
subdivisions: Record<string, ISO3166.Subdivision[]>
|
||||
|
||||
errors: unknown[]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,4 @@
|
||||
export * from './labrinth/projects/types/v2'
|
||||
export * from './labrinth/projects/types/v3'
|
||||
export * from './archon/types'
|
||||
export * from './iso3166/types'
|
||||
export * from './kyros/types'
|
||||
export * from './labrinth/types'
|
||||
|
||||
Reference in New Issue
Block a user