Skip to content

Commit

Permalink
refactor: Adjust resource telemetry and resource db fomart
Browse files Browse the repository at this point in the history
  • Loading branch information
ci010 committed Oct 19, 2023
1 parent 22b703f commit a69dd76
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 41 deletions.
1 change: 1 addition & 0 deletions xmcl-runtime-api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export * from './src/services/UserService'
export * from './src/services/VersionService'
export * from './src/services/XUpdateService'
export * from './src/services/YggdrasilService'
export * from './src/services/ProjectMappingService'

export * from './src/apps'
export * from './src/bootstrap'
Expand Down
7 changes: 7 additions & 0 deletions xmcl-runtime-api/src/services/ProjectMappingService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ServiceKey } from './Service'
export interface ProjectMappingService {
lookupByModrinth(modrinth: string): Promise<number | undefined>
lookupByCurseforge(curseforge: string): Promise<string | undefined>
}

export const ProjectMappingServiceKey: ServiceKey<ProjectMappingService> = 'ProjectMappingService'
1 change: 1 addition & 0 deletions xmcl-runtime/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export * from './lib/services/ModpackService'
export * from './lib/services/ModrinthService'
export * from './lib/services/ResourcePackPreviewService'
export * from './lib/services/ResourceService'
export * from './lib/services/ProjectMappingService'
export * from './lib/services/ServerStatusService'
export * from './lib/services/UserService'
export * from './lib/services/VersionService'
Expand Down
90 changes: 73 additions & 17 deletions xmcl-runtime/lib/plugins/pluginTelemetry.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PartialResourceHash, Resource } from '@xmcl/runtime-api'
import { PartialResourceHash, Resource, ResourceDomain, ResourceMetadata } from '@xmcl/runtime-api'
import { Contracts } from 'applicationinsights'
import { randomUUID } from 'crypto'
import { LauncherAppPlugin } from '../app/LauncherApp'
Expand Down Expand Up @@ -138,28 +138,84 @@ export const pluginTelemetry: LauncherAppPlugin = async (app) => {
})
})

const getPayload = (sha1: string, metadata: ResourceMetadata, name?: string, domain?: ResourceDomain) => {
interface ResourceTracingPayload {
name?: string
sha1: string
domain?: ResourceDomain
forge?: {
modId: string
version: string
}
fabric?: {
modId: string
version: string
}[]
curseforge?: {
projectId: number
fileId: number
}
modrinth?: {
projectId: string
versionId: string
}
}
const trace: ResourceTracingPayload = {
name,
sha1,
domain,
}
if (metadata.curseforge) {
trace.curseforge = {
projectId: metadata.curseforge.projectId,
fileId: metadata.curseforge.fileId,
}
}
if (metadata.modrinth) {
trace.modrinth = {
projectId: metadata.modrinth.projectId,
versionId: metadata.modrinth.versionId,
}
}
if (metadata.forge) {
trace.forge = {
modId: metadata.forge.modid,
version: metadata.forge.version,
}
}
if (metadata.fabric) {
if (metadata.fabric instanceof Array) {
trace.fabric = metadata.fabric.map(f => ({
modId: f.id,
version: f.version,
}))
} else {
trace.fabric = [{
modId: metadata.fabric.id,
version: metadata.fabric.version,
}]
}
}

return trace
}

app.registry.get(ResourceService).then((resourceService) => {
resourceService.on('resourceAdd', (res: Resource) => {
if (settings.disableTelemetry) return
appInsight.defaultClient.trackEvent({
name: 'resource-metadata',
properties: {
fileName: res.fileName,
domain: res.domain,
sha1: res.hash,
metadata: res.metadata,
},
name: 'resource-metadata-v2',
properties: getPayload(res.hash, res.metadata, res.name, res.domain),
})
}).on('resourceUpdate', (res: PartialResourceHash) => {
})
resourceService.on('resourceUpdate', (res: PartialResourceHash) => {
if (settings.disableTelemetry) return
appInsight.defaultClient.trackEvent({
name: 'resource-metadata',
properties: {
name: res.name,
sha1: res.hash,
metadata: res.metadata,
},
})
if (res.metadata) {
appInsight.defaultClient.trackEvent({
name: 'resource-metadata-v2',
properties: getPayload(res.hash, res.metadata, res.name),
})
}
})
})

Expand Down
24 changes: 0 additions & 24 deletions xmcl-runtime/lib/resources/migrateResources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,30 +78,6 @@ async function up(db: Kysely<Database>): Promise<void> {
.addColumn('sha1', 'char(40)', (col) => col.notNull())
.execute()

await db.schema
.createTable('modProject')
.addColumn('modid', 'varchar', (col) => col.notNull())
.addColumn('modloader', 'varchar')
.addColumn('modrinth', 'varchar')
.addColumn('modloader', 'varchar')
.addColumn('curseforge', 'varchar')
.addColumn('mcwiki', 'varchar')
.addPrimaryKeyConstraint('modloader_modid', ['modloader', 'modid'])
.execute()

await db.schema
.createTable('modFile')
.addColumn('modid', 'varchar')
.addColumn('version', 'varchar')
.addColumn('modloader', 'varchar')
.addColumn('modrinthProjectId', 'varchar')
.addColumn('modrinthProjectVersionId', 'varchar')
.addColumn('curseforgeProjectId', 'varchar')
.addColumn('curseforgeProjectFileId', 'varchar')
.addColumn('url', 'json')
.addPrimaryKeyConstraint('modloader_modid_version', ['modloader', 'modid', 'version'])
.execute()

await db.schema
.createIndex('snapshots_ino_index')
.on('snapshots')
Expand Down
85 changes: 85 additions & 0 deletions xmcl-runtime/lib/services/ProjectMappingService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import SQLite from 'better-sqlite3'
import { Kysely, SqliteDialect } from 'kysely'
import { LauncherApp } from '../app/LauncherApp'
import { LauncherAppKey } from '../app/utils'
import { Inject } from '../util/objectRegistry'
import { AbstractService, ExposeServiceKey } from './Service'
import { PathResolver, kGameDataPath } from '../entities/gameDataPath'
import { missing } from '../util/fs'
import { download } from '@xmcl/file-transfer'
import { request } from 'undici'
import { readFile, writeFile } from 'fs/promises'
import { ProjectMappingServiceKey, ProjectMappingService as IProjectMappingService } from '@xmcl/runtime-api'

interface Database {
project: {
modrinth: string
curseforge: string
}
}

@ExposeServiceKey(ProjectMappingServiceKey)
export class ProjectMappingService extends AbstractService implements IProjectMappingService {
#db: Kysely<Database>

constructor(
@Inject(LauncherAppKey) app: LauncherApp,
@Inject(kGameDataPath) getPath: PathResolver,
) {
super(app, async () => {
const filePath = getPath('project-mapping.sqlite')
const url = 'https://xmcl.blob.core.windows.net/project-mapping/latest.sqlite'
if (await missing(filePath)) {
await download({
url,
destination: filePath,
})
await writeFile(getPath('project-mapping.last-modified'), new Date().toUTCString())
} else {
const lastModified = await readFile(getPath('project-mapping.last-modified'), 'utf-8')
const response = await request(url, {
method: 'HEAD',
headers: {
'If-Modified-Since': lastModified,
},
})
if (response.statusCode === 200) {
await download({
url,
destination: filePath,
})
await writeFile(getPath('project-mapping.last-modified'), new Date().toUTCString())
}
}
})

this.#db = new Kysely<Database>({
dialect: new SqliteDialect({
database: new SQLite(getPath('project-mapping.sqlite'), {}),
}),
log: (e) => {
if (e.level === 'error') {
this.warn(e.query.sql + '\n[' + e.query.parameters.join(', ') + ']')
}
},
})
}

async lookupByModrinth(modrinth: string) {
await this.initialize()
const result = await this.#db.selectFrom('project')
.where('modrinth', '=', modrinth)
.select('curseforge')
.executeTakeFirst()
if (result) return Number(result.curseforge)
}

async lookupByCurseforge(curseforge: string) {
await this.initialize()
const result = await this.#db.selectFrom('project')
.where('curseforge', '=', curseforge)
.select('modrinth')
.executeTakeFirst()
if (result) return result.modrinth
}
}

0 comments on commit a69dd76

Please sign in to comment.