feat: base api-client impl (#4694)

* feat: base api-client impl

* fix: doc

* feat: start work on module stuff

* feat: migrate v2/v3 projects into module system

* fix: lint & README.md contributing

* refactor: remove utils old api client prototype

* fix: lint

* fix: api url issues

* fix: baseurl in error.vue

* fix: readme

* fix typo in readme

* Update apps/frontend/src/providers/api-client.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Calum H. <hendersoncal117@gmail.com>

* Update packages/api-client/src/features/verbose-logging.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Calum H. <hendersoncal117@gmail.com>

* Update packages/api-client/src/features/retry.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Calum H. <hendersoncal117@gmail.com>

---------

Signed-off-by: Calum H. <hendersoncal117@gmail.com>
Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Calum H.
2025-11-12 20:29:12 +00:00
committed by GitHub
parent 590ba3ce55
commit 70e2138248
45 changed files with 3155 additions and 123 deletions

View File

@@ -7,30 +7,21 @@
</template>
<script setup lang="ts">
import { NotificationPanel, provideNotificationManager } from '@modrinth/ui'
import { provideApi } from '@modrinth/ui/src/providers/api.ts'
import { RestModrinthApi } from '@modrinth/utils'
import ModrinthLoadingIndicator from '~/components/ui/modrinth-loading-indicator.ts'
import { createModrinthClient, provideModrinthClient } from './providers/api-client.ts'
import { FrontendNotificationManager } from './providers/frontend-notifications.ts'
const auth = await useAuth()
const config = useRuntimeConfig()
provideNotificationManager(new FrontendNotificationManager())
provideApi(
new RestModrinthApi((url: string, options?: object) => {
const match = url.match(/^\/v(\d+)\/(.+)$/)
if (match) {
const apiVersion = Number(match[1])
const path = match[2]
return useBaseFetch(path, {
...options,
apiVersion,
}) as Promise<Response>
} else {
throw new Error('Invalid format')
}
}),
)
const client = createModrinthClient(auth.value, {
apiBaseUrl: config.public.apiBaseUrl.replace('/v2/', '/'),
archonBaseUrl: config.public.pyroBaseUrl.replace('/v2/', '/'),
rateLimitKey: config.rateLimitKey,
})
provideModrinthClient(client)
</script>

View File

@@ -59,9 +59,21 @@ import { IntlFormatted } from '@vintl/vintl/components'
import Logo404 from '~/assets/images/404.svg'
import ModrinthLoadingIndicator from './components/ui/modrinth-loading-indicator.ts'
import { createModrinthClient, provideModrinthClient } from './providers/api-client.ts'
import { FrontendNotificationManager } from './providers/frontend-notifications.ts'
const auth = await useAuth()
const config = useRuntimeConfig()
provideNotificationManager(new FrontendNotificationManager())
const client = createModrinthClient(auth.value, {
apiBaseUrl: config.public.apiBaseUrl.replace('/v2/', '/'),
archonBaseUrl: config.public.pyroBaseUrl.replace('/v2/', '/'),
rateLimitKey: config.rateLimitKey,
})
provideModrinthClient(client)
const { formatMessage } = useVIntl()
const props = defineProps({

View File

@@ -9,14 +9,15 @@ import {
UnsavedChangesPopup,
useSavable,
} from '@modrinth/ui'
import { injectApi } from '@modrinth/ui/src/providers/api.ts'
import { defineMessages, useVIntl } from '@vintl/vintl'
import { injectModrinthClient } from '~/providers/api-client.ts'
const { formatMessage } = useVIntl()
const { currentMember, projectV2, projectV3, refreshProject } = injectProjectPageContext()
const { handleError } = injectNotificationManager()
const api = injectApi()
const client = injectModrinthClient()
const saving = ref(false)
@@ -49,8 +50,8 @@ const { saved, current, reset, save } = useSavable(
({ environment, side_types_migration_review_status }) => {
saving.value = true
side_types_migration_review_status = 'reviewed'
api.projects
.editV3(projectV2.value.id, { environment, side_types_migration_review_status })
client.labrinth.projects_v3
.edit(projectV2.value.id, { environment, side_types_migration_review_status })
.then(() => refreshProject().then(reset))
.catch(handleError)
.finally(() => (saving.value = false))

View File

@@ -7,14 +7,15 @@ import {
UnsavedChangesPopup,
useSavable,
} from '@modrinth/ui'
import { injectApi } from '@modrinth/ui/src/providers/api.ts'
import { defineMessages, type MessageDescriptor, useVIntl } from '@vintl/vintl'
import { injectModrinthClient } from '~/providers/api-client.ts'
const { formatMessage } = useVIntl()
const { projectV2: project, refreshProject } = injectProjectPageContext()
const { handleError } = injectNotificationManager()
const api = injectApi()
const client = injectModrinthClient()
const saving = ref(false)
@@ -34,7 +35,7 @@ const { saved, current, reset, save } = useSavable(
if (data) {
saving.value = true
api.projects
client.labrinth.projects_v2
.edit(project.value.id, { title, description: tagline, slug: url })
.then(() => refreshProject().then(reset))
.catch(handleError)

View File

@@ -1044,7 +1044,7 @@ const nodeUnavailableDetails = computed(() => [
{
label: 'Node',
value:
server.moduleErrors?.general?.error.responseData?.hostname ??
(server.moduleErrors?.general?.error.responseData as any)?.hostname ??
server.general?.datacenter ??
'Unknown',
type: 'inline' as const,

View File

@@ -0,0 +1,42 @@
import type { AbstractFeature, AuthConfig, NuxtClientConfig } from '@modrinth/api-client'
import {
AuthFeature,
CircuitBreakerFeature,
NuxtCircuitBreakerStorage,
NuxtModrinthClient,
VerboseLoggingFeature,
} from '@modrinth/api-client'
import { createContext } from '@modrinth/ui'
export function createModrinthClient(
auth: { token: string | undefined },
config: { apiBaseUrl: string; archonBaseUrl: string; rateLimitKey?: string },
): NuxtModrinthClient {
const optionalFeatures = [
import.meta.dev ? (new VerboseLoggingFeature() as AbstractFeature) : undefined,
].filter(Boolean) as AbstractFeature[]
const clientConfig: NuxtClientConfig = {
labrinthBaseUrl: config.apiBaseUrl,
archonBaseUrl: config.archonBaseUrl,
rateLimitKey: config.rateLimitKey,
features: [
new AuthFeature({
token: async () => auth.token,
} as AuthConfig),
new CircuitBreakerFeature({
storage: new NuxtCircuitBreakerStorage(),
maxFailures: 3,
resetTimeout: 30000,
}),
...optionalFeatures,
],
}
return new NuxtModrinthClient(clientConfig)
}
export const [injectModrinthClient, provideModrinthClient] = createContext<NuxtModrinthClient>(
'root',
'modrinthClient',
)