You've already forked AstralRinth
Merge tag 'v0.10.20' into beta
This commit is contained in:
22
apps/frontend/src/composables/affiliates.ts
Normal file
22
apps/frontend/src/composables/affiliates.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
export const useAffiliates = () => {
|
||||
const affiliateCookie = useCookie('mrs_afl', {
|
||||
maxAge: 60 * 60 * 24 * 7, // 7 days
|
||||
sameSite: 'lax',
|
||||
secure: true,
|
||||
httpOnly: false,
|
||||
path: '/',
|
||||
})
|
||||
|
||||
const setAffiliateCode = (code: string) => {
|
||||
affiliateCookie.value = code
|
||||
}
|
||||
|
||||
const getAffiliateCode = (): string | undefined => {
|
||||
return affiliateCookie.value || undefined
|
||||
}
|
||||
|
||||
return {
|
||||
setAffiliateCode,
|
||||
getAffiliateCode,
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,39 @@
|
||||
import { useGeneratedState } from '@/composables/generated.ts'
|
||||
import { useRequestHeaders, useState } from '#imports'
|
||||
|
||||
export const useCountries = () => {
|
||||
const generated = useGeneratedState()
|
||||
return computed(() => generated.value.countries ?? [])
|
||||
}
|
||||
|
||||
export const useFormattedCountries = () => {
|
||||
const countries = useCountries()
|
||||
|
||||
return computed(() =>
|
||||
countries.value.map((country) => {
|
||||
let label = country.nameShort
|
||||
|
||||
if (country.alpha2 === 'TW') {
|
||||
label = 'Taiwan'
|
||||
} else if (country.nameShort.length > 30) {
|
||||
label = `${country.nameShort} (${country.alpha2})`
|
||||
}
|
||||
|
||||
return {
|
||||
value: country.alpha2,
|
||||
label,
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
export const useSubdivisions = (countryCode: ComputedRef<string> | Ref<string> | string) => {
|
||||
const generated = useGeneratedState()
|
||||
const code = isRef(countryCode) ? countryCode : ref(countryCode)
|
||||
|
||||
return computed(() => generated.value.subdivisions?.[unref(code)] ?? [])
|
||||
}
|
||||
|
||||
export const useUserCountry = () => {
|
||||
const country = useState<string>('userCountry', () => 'US')
|
||||
const fromServer = useState<boolean>('userCountryFromServer', () => false)
|
||||
|
||||
@@ -19,6 +19,7 @@ const validateValues = <K extends PropertyKey>(flags: Record<K, FlagValue>) => f
|
||||
export const DEFAULT_FEATURE_FLAGS = validateValues({
|
||||
// Developer flags
|
||||
developerMode: false,
|
||||
demoMode: false,
|
||||
showVersionFilesInTable: false,
|
||||
// showAdsWithPlus: false,
|
||||
alwaysShowChecklistAsPopup: true,
|
||||
|
||||
122
apps/frontend/src/composables/generated.ts
Normal file
122
apps/frontend/src/composables/generated.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import type { ISO3166, Labrinth } from '@modrinth/api-client'
|
||||
|
||||
import generatedState from '~/generated/state.json'
|
||||
|
||||
export interface ProjectType {
|
||||
actual: string
|
||||
id: string
|
||||
display: string
|
||||
}
|
||||
|
||||
export interface LoaderData {
|
||||
pluginLoaders: string[]
|
||||
pluginPlatformLoaders: string[]
|
||||
allPluginLoaders: string[]
|
||||
dataPackLoaders: string[]
|
||||
modLoaders: string[]
|
||||
hiddenModLoaders: string[]
|
||||
}
|
||||
|
||||
// Re-export types from api-client for convenience
|
||||
export type Country = ISO3166.Country
|
||||
export type Subdivision = ISO3166.Subdivision
|
||||
|
||||
export interface GeneratedState extends Labrinth.State.GeneratedState {
|
||||
// Additional runtime-defined fields not from the API
|
||||
projectTypes: ProjectType[]
|
||||
loaderData: LoaderData
|
||||
projectViewModes: string[]
|
||||
approvedStatuses: string[]
|
||||
rejectedStatuses: string[]
|
||||
staffRoles: string[]
|
||||
|
||||
// Metadata
|
||||
lastGenerated?: string
|
||||
apiUrl?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Composable for accessing the complete generated state.
|
||||
* This includes both fetched data and runtime-defined constants.
|
||||
*/
|
||||
export const useGeneratedState = () =>
|
||||
useState<GeneratedState>('generatedState', () => ({
|
||||
// Cast JSON data to typed API responses
|
||||
categories: (generatedState.categories ?? []) as Labrinth.Tags.v2.Category[],
|
||||
loaders: (generatedState.loaders ?? []) as Labrinth.Tags.v2.Loader[],
|
||||
gameVersions: (generatedState.gameVersions ?? []) as Labrinth.Tags.v2.GameVersion[],
|
||||
donationPlatforms: (generatedState.donationPlatforms ??
|
||||
[]) as Labrinth.Tags.v2.DonationPlatform[],
|
||||
reportTypes: (generatedState.reportTypes ?? []) as string[],
|
||||
muralBankDetails: generatedState.muralBankDetails as
|
||||
| Record<string, { bankNames: string[] }>
|
||||
| undefined,
|
||||
countries: (generatedState.countries ?? []) as ISO3166.Country[],
|
||||
subdivisions: (generatedState.subdivisions ?? {}) as Record<string, ISO3166.Subdivision[]>,
|
||||
|
||||
projectTypes: [
|
||||
{
|
||||
actual: 'mod',
|
||||
id: 'mod',
|
||||
display: 'mod',
|
||||
},
|
||||
{
|
||||
actual: 'mod',
|
||||
id: 'plugin',
|
||||
display: 'plugin',
|
||||
},
|
||||
{
|
||||
actual: 'mod',
|
||||
id: 'datapack',
|
||||
display: 'data pack',
|
||||
},
|
||||
{
|
||||
actual: 'shader',
|
||||
id: 'shader',
|
||||
display: 'shader',
|
||||
},
|
||||
{
|
||||
actual: 'resourcepack',
|
||||
id: 'resourcepack',
|
||||
display: 'resource pack',
|
||||
},
|
||||
{
|
||||
actual: 'modpack',
|
||||
id: 'modpack',
|
||||
display: 'modpack',
|
||||
},
|
||||
],
|
||||
loaderData: {
|
||||
pluginLoaders: ['bukkit', 'spigot', 'paper', 'purpur', 'sponge', 'folia'],
|
||||
pluginPlatformLoaders: ['bungeecord', 'waterfall', 'velocity'],
|
||||
allPluginLoaders: [
|
||||
'bukkit',
|
||||
'spigot',
|
||||
'paper',
|
||||
'purpur',
|
||||
'sponge',
|
||||
'bungeecord',
|
||||
'waterfall',
|
||||
'velocity',
|
||||
'folia',
|
||||
],
|
||||
dataPackLoaders: ['datapack'],
|
||||
modLoaders: ['forge', 'fabric', 'quilt', 'liteloader', 'modloader', 'rift', 'neoforge'],
|
||||
hiddenModLoaders: ['liteloader', 'modloader', 'rift'],
|
||||
},
|
||||
projectViewModes: ['list', 'grid', 'gallery'],
|
||||
approvedStatuses: ['approved', 'archived', 'unlisted', 'private'],
|
||||
rejectedStatuses: ['rejected', 'withheld'],
|
||||
staffRoles: ['moderator', 'admin'],
|
||||
|
||||
homePageProjects: generatedState.homePageProjects as unknown as
|
||||
| Labrinth.Projects.v2.Project[]
|
||||
| undefined,
|
||||
homePageSearch: generatedState.homePageSearch as Labrinth.Search.v2.SearchResults | undefined,
|
||||
homePageNotifs: generatedState.homePageNotifs as Labrinth.Search.v2.SearchResults | undefined,
|
||||
products: generatedState.products as Labrinth.Billing.Internal.Product[] | undefined,
|
||||
|
||||
lastGenerated: generatedState.lastGenerated,
|
||||
apiUrl: generatedState.apiUrl,
|
||||
errors: generatedState.errors,
|
||||
}))
|
||||
@@ -106,87 +106,4 @@ export class BackupsModule extends ServerModule {
|
||||
async getAutoBackup(): Promise<AutoBackupSettings> {
|
||||
return await useServersFetch(`servers/${this.serverId}/autobackup`)
|
||||
}
|
||||
|
||||
downloadBackup(
|
||||
backupId: string,
|
||||
backupName: string,
|
||||
): {
|
||||
promise: Promise<void>
|
||||
onProgress: (cb: (p: { loaded: number; total: number; progress: number }) => void) => void
|
||||
cancel: () => void
|
||||
} {
|
||||
const progressSubject = new EventTarget()
|
||||
const abortController = new AbortController()
|
||||
|
||||
const downloadPromise = new Promise<void>((resolve, reject) => {
|
||||
const auth = this.server.general?.node
|
||||
if (!auth?.instance || !auth?.token) {
|
||||
reject(new Error('Missing authentication credentials'))
|
||||
return
|
||||
}
|
||||
|
||||
const xhr = new XMLHttpRequest()
|
||||
|
||||
xhr.addEventListener('progress', (e) => {
|
||||
if (e.lengthComputable) {
|
||||
const progress = e.loaded / e.total
|
||||
progressSubject.dispatchEvent(
|
||||
new CustomEvent('progress', {
|
||||
detail: { loaded: e.loaded, total: e.total, progress },
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
// progress = -1 to indicate indeterminate size
|
||||
progressSubject.dispatchEvent(
|
||||
new CustomEvent('progress', {
|
||||
detail: { loaded: e.loaded, total: 0, progress: -1 },
|
||||
}),
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
xhr.onload = () => {
|
||||
if (xhr.status >= 200 && xhr.status < 300) {
|
||||
try {
|
||||
const blob = xhr.response
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.download = `${backupName}.zip`
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
document.body.removeChild(a)
|
||||
window.URL.revokeObjectURL(url)
|
||||
resolve()
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
} else {
|
||||
reject(new Error(`Download failed with status ${xhr.status}`))
|
||||
}
|
||||
}
|
||||
|
||||
xhr.onerror = () => reject(new Error('Download failed'))
|
||||
xhr.onabort = () => reject(new Error('Download cancelled'))
|
||||
|
||||
xhr.open(
|
||||
'GET',
|
||||
`https://${auth.instance}/modrinth/v0/backups/${backupId}/download?auth=${auth.token}`,
|
||||
)
|
||||
xhr.responseType = 'blob'
|
||||
xhr.send()
|
||||
|
||||
abortController.signal.addEventListener('abort', () => xhr.abort())
|
||||
})
|
||||
|
||||
return {
|
||||
promise: downloadPromise,
|
||||
onProgress: (cb: (p: { loaded: number; total: number; progress: number }) => void) => {
|
||||
progressSubject.addEventListener('progress', ((e: CustomEvent) => {
|
||||
cb(e.detail)
|
||||
}) as EventListener)
|
||||
},
|
||||
cancel: () => abortController.abort(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
import tags from '~/generated/state.json'
|
||||
|
||||
export const useTags = () =>
|
||||
useState('tags', () => ({
|
||||
categories: tags.categories,
|
||||
loaders: tags.loaders,
|
||||
gameVersions: tags.gameVersions,
|
||||
donationPlatforms: tags.donationPlatforms,
|
||||
reportTypes: tags.reportTypes,
|
||||
projectTypes: [
|
||||
{
|
||||
actual: 'mod',
|
||||
id: 'mod',
|
||||
display: 'mod',
|
||||
},
|
||||
{
|
||||
actual: 'mod',
|
||||
id: 'plugin',
|
||||
display: 'plugin',
|
||||
},
|
||||
{
|
||||
actual: 'mod',
|
||||
id: 'datapack',
|
||||
display: 'data pack',
|
||||
},
|
||||
{
|
||||
actual: 'shader',
|
||||
id: 'shader',
|
||||
display: 'shader',
|
||||
},
|
||||
{
|
||||
actual: 'resourcepack',
|
||||
id: 'resourcepack',
|
||||
display: 'resource pack',
|
||||
},
|
||||
{
|
||||
actual: 'modpack',
|
||||
id: 'modpack',
|
||||
display: 'modpack',
|
||||
},
|
||||
],
|
||||
loaderData: {
|
||||
pluginLoaders: ['bukkit', 'spigot', 'paper', 'purpur', 'sponge', 'folia'],
|
||||
pluginPlatformLoaders: ['bungeecord', 'waterfall', 'velocity'],
|
||||
allPluginLoaders: [
|
||||
'bukkit',
|
||||
'spigot',
|
||||
'paper',
|
||||
'purpur',
|
||||
'sponge',
|
||||
'bungeecord',
|
||||
'waterfall',
|
||||
'velocity',
|
||||
'folia',
|
||||
],
|
||||
dataPackLoaders: ['datapack'],
|
||||
modLoaders: ['forge', 'fabric', 'quilt', 'liteloader', 'modloader', 'rift', 'neoforge'],
|
||||
hiddenModLoaders: ['liteloader', 'modloader', 'rift'],
|
||||
},
|
||||
projectViewModes: ['list', 'grid', 'gallery'],
|
||||
approvedStatuses: ['approved', 'archived', 'unlisted', 'private'],
|
||||
rejectedStatuses: ['rejected', 'withheld'],
|
||||
staffRoles: ['moderator', 'admin'],
|
||||
}))
|
||||
Reference in New Issue
Block a user