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,26 +1,30 @@
import { client } from '../client'
import { API } from '../endpoints'
import type { ApiResponse } from '@/scripts/types/api'
export interface Module {
name: string
slug: string
description: string
version: string
enabled: boolean
installed: boolean
[key: string]: unknown
}
import type { Module } from '@/scripts/types/domain/module'
export interface ModuleCheckResponse {
error?: string
success?: boolean
authenticated?: boolean
premium?: boolean
}
export interface ModuleDetailMeta {
modules: Module[]
}
export interface ModuleInstallPayload {
module: string
slug: string
module_name: string
version: string
api_token?: string
checksum_sha256?: string | null
path?: string
}
export interface ModuleDetailResponse {
data: Module
meta: ModuleDetailMeta
}
export const moduleService = {
@@ -29,7 +33,7 @@ export const moduleService = {
return data
},
async get(module: string): Promise<Module> {
async get(module: string): Promise<ModuleDetailResponse> {
const { data } = await client.get(`${API.MODULES}/${module}`)
return data
},

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