/** * POST /api/settings/mobile-app/publish — publish a new LogShip Mobile build into the server-side * release channel (data/mobile-releases/) WITHOUT an ERP deployment. * * Multipart form: `apk` (file) + either `version` (JSON file/text: the android/update-files/version.json) * or the plain fields versionName, versionCode, releaseNotes, releaseDate, minRequiredVersion; `force=1` * allows re-publishing the same/lower versionCode. * Auth: a client-administrator session (verified against iDempiere ad_role) OR the service token * (`Authorization: Bearer `, used by scripts/publish-mobile-update.sh). */ import { publishRelease, manifestWithSignedUrl, isServiceTokenRequest, isAdminVerified } from '../../../utils/mobileReleases' export default defineEventHandler(async (event) => { setHeader(event, 'Cache-Control', 'no-store') let allowed = isServiceTokenRequest(event) if (!allowed) { const token = await getTokenHelper(event) if (!token) { setResponseStatus(event, 401); return { status: 401, message: 'Not authenticated' } } allowed = await isAdminVerified(event, token) } if (!allowed) { setResponseStatus(event, 403); return { status: 403, message: 'Client administrator required' } } const parts = await readMultipartFormData(event) if (!parts) { setResponseStatus(event, 400); return { status: 400, message: 'Multipart form data required' } } let apk: Buffer | null = null const fields: Record = {} for (const p of parts) { if (p.name === 'apk') apk = p.data else if (p.name === 'version') { try { Object.assign(fields, JSON.parse(p.data.toString('utf8'))) } catch { setResponseStatus(event, 400); return { status: 400, message: 'version is not valid JSON' } } } else if (p.name) fields[p.name] = p.data.toString('utf8') } if (!apk) { setResponseStatus(event, 400); return { status: 400, message: 'apk file missing' } } try { const { manifest, archived } = await publishRelease({ apk, versionName: String(fields.versionName || ''), versionCode: Number(fields.versionCode), releaseNotes: fields.releaseNotes, releaseDate: fields.releaseDate, minRequiredVersion: fields.minRequiredVersion, force: ['1', 'true', 'yes'].includes(String(fields.force || '').toLowerCase()) }) return { status: 200, message: `Published ${manifest.versionName} (code ${manifest.versionCode})`, manifest: manifestWithSignedUrl(manifest), archived } } catch (err: any) { console.error('[mobile-app/publish]', err?.message || err) setResponseStatus(event, 400) return { status: 400, message: err?.message || 'Publish failed' } } })