Add a mod manifest endpoint

This commit is contained in:
Lncvrt committed 2026-03-24 14:48:40 -07:00
1 parent 3d8153069d
commit aa296ab53d
4 files changed
+244 -3

No files matched your search

+19
View File
@@ -13,6 +13,7 @@ import { handler as getVerifyCodeHandler } from './routes/get-verify-code'
import { handler as canLoadClientHandler } from './routes/can-load-client'
import { handler as launcherModsGetHandler } from './routes/launcher/mods/get'
import { handler as launcherVersionsHandler } from './routes/launcher/versions/get'
import { handler as launcherLatestHandler } from './routes/launcher/latest/get'
import { handler as launcherDownloadHandler } from './routes/launcher/download/get'
@@ -955,6 +956,24 @@ app.get('/can-load-client', context => canLoadClientHandler(context), {
)
})
})
app.get('/launcher/mods', context => launcherModsGetHandler(context), {
detail: {
description: 'The endpoint for getting mods for a version.',
tags: ['Launcher']
},
query: t.Object({
platform: t.String({ examples: ['windows', 'macos', 'linux'] }),
arch: t.String({ examples: ['x86', 'x64', 'arm64'] }),
version: t.String()
}),
headers: t.Object({
'x-forwarded-for': t.Optional(
t.String({
hide: true
})
)
})
})
app.get('/launcher/versions', context => launcherVersionsHandler(context), {
detail: {
description:
+21
View File
@@ -103,6 +103,27 @@ export const cdnFiles = mysqlTable('cdnfiles', {
mimeType: varchar('mimeType', { length: 255 })
})
export const mods = mysqlTable('mods', {
id: bigint('id', { mode: 'number' }).primaryKey().autoincrement().notNull(),
name: text('name').notNull(),
description: text('description').notNull(),
downloadCount: bigint('downloadCount', { mode: 'number' })
.notNull()
.default(0),
hidden: boolean('hidden').default(false).notNull()
})
export const modVersions = mysqlTable('modversions', {
id: text('id').primaryKey().notNull(),
downloads: text('downloads').default('{}').notNull(),
gameVersion: varchar('gameVersion', { length: 24 })
.notNull()
.references(() => launcherVersionManifest.id),
releaseDate: bigint('releaseDate', { mode: 'number' }).notNull(),
changelog: text('changelog'),
hidden: boolean('hidden').default(false).notNull()
})
// berrydashdatabase
export const berryDashUserData = mysqlTable('userdata', {
+147
View File
@@ -0,0 +1,147 @@
import {
games,
launcherVersionManifest,
mods,
modVersions
} from '../../../lib/tables'
import { and, asc, desc, eq } from 'drizzle-orm'
import { getDatabaseConnection, jsonResponse } from '../../../lib/util'
import { Context } from 'elysia'
export const handler = async (context: Context) => {
const dbResult = getDatabaseConnection(0)
if (!dbResult)
return jsonResponse({ error: 'Failed to connect to database' }, 500)
const { connection, db } = dbResult
const platform = context.query.platform
const arch = context.query.arch
const version = context.query.version
let platformsToShow: string[] | null = null
if (platform == 'windows') {
if (arch == 'x86') platformsToShow = ['windows-x86']
else if (arch == 'x86_64' || arch == 'x64')
platformsToShow = ['windows-x64', 'windows-x86']
else if (arch == 'aarch64' || arch == 'arm64')
platformsToShow = ['windows-arm64']
else {
connection.end()
return jsonResponse(
{
message: 'Unsupported architecture for Windows',
versions: null,
games: null
},
400
)
}
} else if (platform == 'linux') {
if (arch == 'x86') platformsToShow = ['linux-x64']
else if (arch == 'x86_64' || arch == 'x64')
platformsToShow = ['linux-x64', 'linux-x86', 'windows-x64', 'windows-x86']
else if (arch == 'aarch64' || arch == 'arm64')
platformsToShow = ['linux-arm64']
else {
connection.end()
return jsonResponse(
{
message: 'Unsupported architecture for Linux',
versions: null,
games: null
},
400
)
}
} else if (platform == 'macos') {
if (arch == 'x86_64' || arch == 'x64')
platformsToShow = ['macos-x64', 'macos-universal']
else if (arch == 'aarch64' || arch == 'arm64')
platformsToShow = ['macos-arm64', 'macos-universal', 'macos-x64']
else {
connection.end()
return jsonResponse(
{
message: 'Unsupported architecture for macOS',
versions: null,
games: null
},
400
)
}
} else if (platform == 'android') platformsToShow = ['android']
else if (platform == 'ios') platformsToShow = ['ios']
else {
connection.end()
return jsonResponse(
{ message: 'Unsupported platform', versions: null, games: null },
400
)
}
const modsRaw = await db
.select({
id: mods.id,
name: mods.name,
description: mods.description,
downloadCount: mods.downloadCount
})
.from(mods)
.where(eq(mods.hidden, false))
.orderBy(desc(mods.id))
.execute()
const modsFinal = (
await Promise.all(
modsRaw.map(async v => {
const vi = await db
.select({
id: modVersions.id,
downloads: modVersions.downloads,
gameVersion: modVersions.gameVersion,
releaseDate: modVersions.releaseDate,
changelog: modVersions.changelog
})
.from(modVersions)
.where(
and(
eq(modVersions.hidden, false),
eq(modVersions.gameVersion, version)
)
)
.orderBy(desc(modVersions.releaseDate))
.execute()
if (!vi || vi.length <= 1) return
const downloads: Record<string, number> = JSON.parse(vi[0].downloads)
let download: string | undefined = undefined
for (const key of platformsToShow) {
if (key in downloads) {
download = key
break
}
}
if (!download || !downloads[download]) return
return {
id: v.id,
name: v.name,
description: v.description,
downloads: v.downloadCount,
released: vi[vi.length - 1].releaseDate,
updated: vi[0].releaseDate,
latestVersion: vi[0].id.substring(String(v.id).length + 1),
latestDownload: downloads[download],
changelog: vi[0].changelog
}
})
)
).filter(v => v != null)
connection.end()
return jsonResponse(modsFinal)
}