Finish reworking launcher manifest stuff

This commit is contained in:
Lncvrt committed 2026-02-26 22:47:15 -07:00
1 parent 02bad685da
commit fcdee21ab5
10 files changed
+251 -229

No files matched your search

+13 -10
View File
@@ -12,11 +12,11 @@ import { handler as getVerifyCodeHandler } from './routes/get-verify-code'
import { handler as canLoadClientHandler } from './routes/can-load-client'
import { handler as launcherVersionsHandler } from './routes/launcher/versions'
import { handler as launcherLatestHandler } from './routes/launcher/latest'
import { handler as launcherDownloadHandler } from './routes/launcher/download'
import { handler as launcherLoaderLatestHandler } from './routes/launcher/loader/latest'
import { handler as launcherLoaderUpdateDataHandler } from './routes/launcher/loader/update-data'
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'
import { handler as launcherLoaderLatestHandler } from './routes/launcher/loader/latest/get'
import { handler as launcherLoaderUpdateDataHandler } from './routes/launcher/loader/update-data/get'
import { handler as accountLoginPostHandler } from './routes/account/login/post'
import { handler as accountRegisterPostHandler } from './routes/account/register/post'
@@ -576,7 +576,12 @@ app.get('/launcher/versions', context => launcherVersionsHandler(context), {
},
query: t.Object({
platform: t.Optional(t.String({ examples: ['windows', 'macos', 'linux'] })),
arch: t.Optional(t.String({ examples: ['x86_64', 'aarch64'] }))
arch: t.Optional(t.String({ examples: ['x86', 'x64', 'arm64'] })),
game: t.Optional(
t.String({
description: "Adding this will make it only show a Game ID's versions"
})
)
}),
headers: t.Object({
'x-forwarded-for': t.Optional(
@@ -600,11 +605,9 @@ app.get('/launcher/latest', launcherLatestHandler, {
})
})
app.get('/launcher/download', context => launcherDownloadHandler(context), {
detail: {
hide: true
},
query: t.Object({
id: t.String()
versionId: t.Optional(t.String({ hide: true })),
downloadId: t.String()
}),
headers: t.Object({
'x-forwarded-for': t.Optional(
+14 -7
View File
@@ -1,6 +1,7 @@
import {
bigint,
boolean,
char,
int,
longtext,
mysqlTable,
@@ -53,23 +54,21 @@ export const games = mysqlTable('games', {
export const launcherVersionManifest = mysqlTable('launcherversionmanifest', {
id: varchar('id', { length: 24 }).primaryKey().notNull(),
displayName: text('displayName').notNull(),
versionName: text('versionName').notNull(),
downloads: text('downloads').default('[]').notNull(),
releaseDate: bigint('releaseDate', { mode: 'number' }).notNull(),
downloadUrlVersion: text('downloadUrlVersion').notNull(),
downloadUrls: text('downloadUrls').notNull(),
platforms: text('platforms').notNull(),
executables: text('executables').notNull(),
hidden: boolean('hidden').default(false).notNull(),
game: bigint('game', { mode: 'number' })
.default(0)
.references(() => games.id)
.notNull(),
place: bigint('place', { mode: 'number' }).default(0).notNull(),
sha512sums: text('sha512sums').default('[]').notNull(),
sizes: text('sizes').default('[]').notNull(),
changelog: text('changelog'),
category: int('category').notNull().default(-1),
lastRevision: bigint('lastRevision', { mode: 'number' }).notNull().default(0),
downloads: bigint('downloads', { mode: 'number' }).notNull().default(0)
downloadCount: bigint('downloadCount', { mode: 'number' })
.notNull()
.default(0)
})
export const verifyCodes = mysqlTable('verifycodes', {
@@ -94,6 +93,14 @@ export const resetCodes = mysqlTable('resetcodes', {
type: int('type').notNull()
})
export const cdnFiles = mysqlTable('cdnfiles', {
id: bigint('id', { mode: 'number' }).primaryKey().autoincrement().notNull(),
path: varchar('path', { length: 4096 }).notNull(),
hash: char('hash', { length: 128 }).notNull(),
size: bigint('size', { mode: 'number' }).notNull(),
mimeType: varchar('mimeType', { length: 255 })
})
// berrydashdatabase
export const berryDashUserData = mysqlTable('userdata', {
-21
View File
@@ -1,21 +0,0 @@
import { Context } from 'elysia'
import { launcherVersionManifest } from '../../lib/tables'
import { getDatabaseConnection } from '../../lib/util'
import { eq, sql } from 'drizzle-orm'
export const handler = async (context: Context) => {
const dbResult = getDatabaseConnection(0)
if (!dbResult) return null
const { connection, db } = dbResult
await db
.update(launcherVersionManifest)
.set({
downloads: sql`${launcherVersionManifest.downloads} + 1`
})
.where(eq(launcherVersionManifest.id, context.query.id))
connection.end()
return null
}
+40
View File
@@ -0,0 +1,40 @@
import { Context } from 'elysia'
import { cdnFiles, launcherVersionManifest } from '../../../lib/tables'
import { getDatabaseConnection, jsonResponse } from '../../../lib/util'
import { eq, sql } from 'drizzle-orm'
export const handler = async (context: Context) => {
const dbResult = getDatabaseConnection(0)
if (!dbResult) return jsonResponse({ success: false, data: null }, 400)
const { connection, db } = dbResult
const gameId = context.query.gameId
const downloadId = Number(context.query.downloadId)
const result = await db
.select({ path: cdnFiles.path, hash: cdnFiles.hash })
.from(cdnFiles)
.where(eq(cdnFiles.id, downloadId))
.execute()
if (result.length != 1)
return jsonResponse({ success: false, data: null }, 400)
if (gameId)
await db
.update(launcherVersionManifest)
.set({
downloads: sql`${launcherVersionManifest.downloads} + 1`
})
.where(eq(launcherVersionManifest.id, gameId))
connection.end()
return jsonResponse({
success: true,
data: {
url: 'https://games-r2.lncvrt.xyz/' + result[0].path,
hash: result[0].hash
}
})
}
@@ -1,6 +1,6 @@
import { launcherUpdates } from '../../lib/tables'
import { launcherUpdates } from '../../../lib/tables'
import { desc, eq } from 'drizzle-orm'
import { getDatabaseConnection, jsonResponse } from '../../lib/util'
import { getDatabaseConnection, jsonResponse } from '../../../lib/util'
export const handler = async () => {
const dbResult = getDatabaseConnection(0)
@@ -1,6 +1,6 @@
import { loaderUpdates } from '../../../lib/tables'
import { loaderUpdates } from '../../../../lib/tables'
import { desc, eq } from 'drizzle-orm'
import { getDatabaseConnection, jsonResponse } from '../../../lib/util'
import { getDatabaseConnection, jsonResponse } from '../../../../lib/util'
export const handler = async () => {
const dbResult = getDatabaseConnection(0)
@@ -1,6 +1,6 @@
import { launcherUpdates } from '../../../lib/tables'
import { launcherUpdates } from '../../../../lib/tables'
import { desc, eq } from 'drizzle-orm'
import { getDatabaseConnection, jsonResponse } from '../../../lib/util'
import { getDatabaseConnection, jsonResponse } from '../../../../lib/util'
import { Context } from 'elysia'
export const handler = async (context: Context) => {
-177
View File
@@ -1,177 +0,0 @@
import { games, launcherVersionManifest } from '../../lib/tables'
import { 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 as string | undefined
const arch = context.query.arch as string | undefined
let showAll = false
if (!platform || !arch) {
showAll = true
}
let platString = null
if (!showAll) {
if (platform == 'windows') {
if (arch == 'x86_64' || arch == 'x64') platString = 'windows'
else if (arch == 'aarch64' || arch == 'arm64')
platString = 'windows-arm64'
else {
connection.end()
return jsonResponse(
{
message: 'Unsupported architecture for Windows',
versions: null,
games: null
},
400
)
}
} else if (platform == 'linux') {
if (arch == 'x86_64' || arch == 'x64') platString = 'linux'
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') platString = 'macos'
else if (arch == 'aarch64' || arch == 'arm64') platString = 'macos'
else {
connection.end()
return jsonResponse(
{
message: 'Unsupported architecture for macOS',
versions: null,
games: null
},
400
)
}
} else if (platform == 'android') platString = 'android'
else if (platform == 'ios') platString = 'ios'
else {
connection.end()
return jsonResponse(
{ message: 'Unsupported platform', versions: null, games: null },
400
)
}
}
// const versionsRaw = await db
// .select({
// id: launcherVersionManifest.id,
// displayName: launcherVersionManifest.displayName,
// releaseDate: launcherVersionManifest.releaseDate,
// game: launcherVersionManifest.game,
// downloadUrlVersion: launcherVersionManifest.downloadUrlVersion,
// downloadUrls: launcherVersionManifest.downloadUrls,
// platforms: launcherVersionManifest.platforms,
// executables: launcherVersionManifest.executables,
// sha512sums: launcherVersionManifest.sha512sums,
// sizes: launcherVersionManifest.sizes,
// place: launcherVersionManifest.place,
// changelog: launcherVersionManifest.changelog,
// category: launcherVersionManifest.category,
// lastRevision: launcherVersionManifest.lastRevision
// })
// .from(launcherVersionManifest)
// .where(eq(launcherVersionManifest.hidden, false))
// .orderBy(
// asc(launcherVersionManifest.game),
// desc(launcherVersionManifest.place)
// )
// .execute()
// const versions = versionsRaw
// .map(v => ({
// ...v,
// downloadUrls: JSON.parse(
// v.downloadUrls.replaceAll('%version%', v.downloadUrlVersion)
// ),
// platforms: JSON.parse(v.platforms),
// executables: JSON.parse(v.executables),
// sha512sums: JSON.parse(v.sha512sums),
// sizes: JSON.parse(v.sizes),
// downloadUrlVersion: undefined,
// downloadUrl: undefined as string | undefined,
// executable: undefined as string | undefined,
// sha512sum: undefined as string | undefined,
// size: undefined as number | undefined,
// wine: undefined as boolean | undefined
// }))
// .filter(v => {
// delete v.downloadUrlVersion
// if (showAll || !platString) {
// delete v.downloadUrl
// delete v.executable
// delete v.sha512sum
// delete v.size
// delete v.wine
// return true
// }
// let i = v.platforms.indexOf(platString)
// if (i !== -1) {
// v.downloadUrl = v.downloadUrls[i]
// v.executable = v.executables[i]
// v.sha512sum = v.sha512sums[i]
// v.size = v.sizes[i]
// delete v.downloadUrls
// delete v.platforms
// delete v.executables
// delete v.sha512sums
// delete v.sizes
// delete v.wine
// return true
// } else if (platString == 'linux') {
// i = v.platforms.indexOf('windows')
// if (i !== -1) {
// v.downloadUrl = v.downloadUrls[i]
// v.executable = v.executables[i]
// v.sha512sum = v.sha512sums[i]
// v.size = v.sizes[i]
// v.wine = true
// delete v.downloadUrls
// delete v.platforms
// delete v.executables
// delete v.sha512sums
// delete v.sizes
// return true
// }
// }
// return false
// })
// const gamesListRaw = await db.select().from(games).execute()
// const gamesList = gamesListRaw.map(v => ({
// ...v,
// categoryNames: JSON.parse(v.categoryNames)
// }))
connection.end()
// const seenGames = new Set<number>()
// const finalVersions = versions.filter(v => {
// if (platString !== 'android' && platString !== 'ios') return true
// if (seenGames.has(v.game)) return false
// seenGames.add(v.game)
// return true
// })
return jsonResponse({ versions: [], games: [] })
}
+148
View File
@@ -0,0 +1,148 @@
import { games, launcherVersionManifest } 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 game = context.query.game
let showAll = false
if (!platform || !arch) showAll = true
let platformsToShow: string[] | null = null
if (!showAll) {
if (platform == 'windows') {
if (arch == 'x86') platformsToShow = ['windows-x86']
else if (arch == 'x86_64' || arch == 'x64')
platformsToShow = ['windows-x86', 'windows-x64']
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-x86', 'linux-x64']
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']
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 versionsRaw = await db
.select({
id: launcherVersionManifest.id,
displayName: launcherVersionManifest.displayName,
releaseDate: launcherVersionManifest.releaseDate,
game: launcherVersionManifest.game,
versionName: launcherVersionManifest.versionName,
downloads: launcherVersionManifest.downloads,
place: launcherVersionManifest.place,
changelog: launcherVersionManifest.changelog,
category: launcherVersionManifest.category,
lastRevision: launcherVersionManifest.lastRevision
})
.from(launcherVersionManifest)
.where(
game != null
? and(
eq(launcherVersionManifest.game, Number(game)),
eq(launcherVersionManifest.hidden, false)
)
: eq(launcherVersionManifest.hidden, false)
)
.orderBy(
asc(launcherVersionManifest.game),
desc(launcherVersionManifest.place)
)
.execute()
const versions = versionsRaw
.map(v => {
const downloads: Record<string, number> = JSON.parse(v.downloads)
let download: string | undefined = undefined
if (platformsToShow) {
for (const key of platformsToShow) {
if (key in downloads) download = key
}
}
const { versionName, ...rest } = v
return {
...rest,
downloads: platformsToShow ? undefined : downloads,
download: platformsToShow
? download
? downloads[download]
: undefined
: undefined
}
})
.filter(v => {
if (!platformsToShow) return true
return v.download !== undefined
})
if (!game) {
const gamesListRaw = await db.select().from(games).execute()
const gamesList = gamesListRaw.map(v => ({
...v,
categoryNames: JSON.parse(v.categoryNames)
}))
connection.end()
return jsonResponse({ versions: versions, games: gamesList })
} else return jsonResponse(versions)
}