Skip to content

Commit

Permalink
fix: Should not discard the mod info if the import failed
Browse files Browse the repository at this point in the history
  • Loading branch information
ci010 committed Oct 28, 2023
1 parent c005197 commit c86bdd7
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 32 deletions.
9 changes: 8 additions & 1 deletion xmcl-runtime/lib/resources/tryPersistResource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { dirname, extname, join } from 'path'
import { linkOrCopy } from '../util/fs'
import { getResourceEntry } from './getResourceEntry'
import { ResourceContext } from './ResourceContext'
import { isSystemError } from '../util/error'

export async function tryPersistResource(resource: { fileName: string; domain: ResourceDomain; hash: string; path: string }, root: string, context: ResourceContext) {
const backup = [
Expand Down Expand Up @@ -55,11 +56,17 @@ export async function tryPersistResource(resource: { fileName: string; domain: R
existedEntry = await context.db.selectFrom('snapshots').where('domainedPath', '=', `${resource.domain}/${fileName}`).selectAll().executeTakeFirst()
}

const fstat = await stat(filePath).catch(e => undefined)
const fstat = await stat(filePath).catch(e => {
if (isSystemError(e) && e.code === 'ENOENT') {
return undefined
}
throw e
})

if (fstat) {
// existed but not in database
// this is a broken resource
context.logger.warn(`Resource ${filePath} is broken. Try to fix it.`)
const localEntry = await getResourceEntry(filePath, context, true)
if (localEntry.sha1 === resource.hash) {
// The file is already imported...
Expand Down
4 changes: 2 additions & 2 deletions xmcl-runtime/lib/services/InstanceModsService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export class InstanceModsService extends AbstractService implements IInstanceMod

const fileArgs = files.filter((file) => !shouldIgnoreFile(file)).map((file) => join(dir, file))

const resources = await this.resourceService.importResources(fileArgs.map(f => ({ path: f, domain: ResourceDomain.Mods })))
const resources = await this.resourceService.importResources(fileArgs.map(f => ({ path: f, domain: ResourceDomain.Mods })), true)
return resources.map((r, i) => ({ ...r, path: fileArgs[i] }))
}

Expand All @@ -68,7 +68,7 @@ export class InstanceModsService extends AbstractService implements IInstanceMod
const watcher = watch(basePath, async (event, filePath) => {
if (shouldIgnoreFile(filePath)) return
if (event === 'update') {
const [resource] = await this.resourceService.importResources([{ path: filePath, domain: ResourceDomain.Mods }])
const [resource] = await this.resourceService.importResources([{ path: filePath, domain: ResourceDomain.Mods }], true)
if (isModResource(resource)) {
this.log(`Instance mod add ${filePath}`)
} else {
Expand Down
66 changes: 37 additions & 29 deletions xmcl-runtime/lib/services/ResourceService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,44 +273,52 @@ export class ResourceService extends AbstractService implements IResourceService
return result
}

importResources(resources: [ImportResourceOptions]): Promise<[Resource]>
importResources(resources: [ImportResourceOptions, ImportResourceOptions]): Promise<[Resource, Resource]>
importResources(resources: ImportResourceOptions[]): Promise<Resource[]>
async importResources(options: ImportResourceOptions[]): Promise<Resource[]> {
importResources(resources: [ImportResourceOptions], loose?: boolean): Promise<[Resource]>
importResources(resources: [ImportResourceOptions, ImportResourceOptions], loose?: boolean): Promise<[Resource, Resource]>
importResources(resources: ImportResourceOptions[], loose?: boolean): Promise<Resource[]>
async importResources(options: ImportResourceOptions[], loose?: boolean): Promise<Resource[]> {
// We need to resolve the domain of the resource
const resolved = await this.resolveResources(options)
const result = await Promise.all(resolved.map(async (resolved, index) => {
if (resolved.fileType === 'directory') {
// Will not persist the dictionary as it cannot calculate hash
return resolved
}
if (resolved.storedPath) {
return resolved
}
try {
if (resolved.fileType === 'directory') {
// Will not persist the dictionary as it cannot calculate hash
return resolved
}
if (resolved.storedPath) {
return resolved
}

if (resolved.domain === ResourceDomain.Unclassified) {
resolved.domain = resolveDomain(resolved.metadata)
}
if (resolved.domain === ResourceDomain.Unclassified) {
resolved.domain = resolveDomain(resolved.metadata)
}

const option = options[index]
const option = options[index]

if (option.metadata || option.uris || option.icons) {
const data = await upsertMetadata(option.metadata ?? {}, option.uris ?? [], option.icons ?? [], resolved.fileName, resolved.hash, this.context)
resolved.icons = resolved.icons ? [...resolved.icons, ...data.icons] : data.icons
resolved.uris = resolved.uris ? [...resolved.uris, ...data.uris] : data.uris
resolved.metadata = data
}
if (option.metadata || option.uris || option.icons) {
const data = await upsertMetadata(option.metadata ?? {}, option.uris ?? [], option.icons ?? [], resolved.fileName, resolved.hash, this.context)
resolved.icons = resolved.icons ? [...resolved.icons, ...data.icons] : data.icons
resolved.uris = resolved.uris ? [...resolved.uris, ...data.uris] : data.uris
resolved.metadata = data
}

const storedPath = await tryPersistResource(resolved, this.getPath(), this.context)
const resource = { ...resolved, storedPath }
const storedPathOrErr = await tryPersistResource(resolved, this.getPath(), this.context).catch(e => e)
if (typeof storedPathOrErr === 'string') {
const resource = { ...resolved, storedPath: storedPathOrErr }

this.log(`Persist new resource ${resource.path} -> ${storedPath}`)
this.log(`Persist new resource ${resource.path} -> ${storedPathOrErr}`)

return resource
}).map(r => r.catch(e => {
this.error(e)
return undefined
})))
return resource
}
if (loose) {
return resolved
}
throw storedPathOrErr
} catch (e) {
this.error(e as Error)
return undefined
}
}))

return result.filter(r => r) as Resource[]
}
Expand Down

0 comments on commit c86bdd7

Please sign in to comment.