refactor(modules): marketplace install flow with checksum validation

Rewires module installation to use slug + version + checksum_sha256 instead of the opaque module identifier. ModuleInstaller splits marketplace token handling out of install() into helpers, adopts structured error responses, and validates the downloaded archive's SHA-256 against the marketplace manifest before unpacking.

ModuleResource is simplified to accept an already-loaded installed-module instance rather than fetching it from state, exposes access_tier and checksum fields, and drops the auto-disable-on-unpurchased side effect that was bleeding write logic into a read resource. UnzipUpdateRequest accepts a nullable module with a conditional module_name field so the same endpoint serves both app and module updates.

ModulesPolicy::manageModules now short-circuits for super-admins so administration flows (token validation, store state) are not blocked on a company-scoped ability. Two new feature tests cover both the authorization bypass and ModuleResource serialization.
This commit is contained in:
Darko Gjorgjijoski
2026-04-10 17:30:00 +02:00
parent 2af31d0e5f
commit 23d1476870
10 changed files with 332 additions and 186 deletions

View File

@@ -1,17 +1,13 @@
export interface ModuleAuthor {
name: string
avatar: string
}
export interface ModuleVersion {
module_version: string
invoiceshelf_version: string
invoiceshelf_version: string | null
created_at: string
}
export interface ModuleLink {
name: string
url: string
icon: string
label: string
link: string
}
export interface ModuleReview {
@@ -38,12 +34,15 @@ export interface Module {
cover: string | null
slug: string
module_name: string
access_tier: 'public' | 'premium'
faq: ModuleFaq[] | null
highlights: string[] | null
installed_module_version: string | null
installed_module_version_updated_at: string | null
latest_module_version: string
latest_module_version_updated_at: string
latest_min_invoiceshelf_version: string | null
latest_module_checksum_sha256: string | null
is_dev: boolean
license: string | null
long_description: string | null