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
},