The previous commit (GET-only permitAll) inadvertently broke upload by
requiring auth. The original design intentionally allows unauthenticated
upload — explicitly permit POST /api/file/upload to make this clear.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Upload endpoint (POST) was inadvertently matched by the method-less
requestMatchers("/api/file/*") rule. Making it GET-only makes the intent
explicit and ensures upload correctly requires a valid JWT.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Webhook notification body shows app display name (resolved from
tenant-service via internal API with in-memory cache) instead of appKey
- When re-uploading a package with the same versionCode, automatically
withdraw APPROVED store entries from the older entity before submitting
the new entity, preventing duplicate active submissions
- tenant-service /internal/sdk/apps/{appKey}/platform-info now includes
the app 'name' field
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Spring Security's default Http403ForbiddenEntryPoint was returning 403
for all auth failures. Frontend clients treat 403 as a permission error
(not an auth error), so silent loops occurred instead of proper re-login.
Adding a custom AuthenticationEntryPoint that returns 401 makes clients
handle auth failures correctly (show login page on 401).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The MI /devupload/dev/query API returns the currently-published app's status,
not the pending submission. appStatus=3 means the previous version is still
online, not that the new submission was accepted. Use updateVersion=true as
the approval signal; default updateVersion to false to avoid false positives.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- HONOR: use get-app-current-release endpoint (correct), auditResult field (0=review,1=approved,2=rejected)
- HONOR: assertHonorSuccess now accepts both "0" and "0000" success codes
- OPPO: add integer status mapping (111=approved, 444=rejected) from reference impl
- All stores: add full response body logging for diagnosing poll issues
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- StoreSubmissionService: skip UNDER_REVIEW/APPROVED stores in preflight loop to prevent duplicate submissions
- StoreSubmissionService: post-batch sweep marks any still-SUBMITTING stores in same batch as REJECTED
- StoreSubmissionService: @EventListener(ApplicationReadyEvent) clears orphaned SUBMITTING states on startup
- AppVersionRepository: add findAllWithSubmittingStores() native query for startup sweep
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- OpsController/OpsService: POST /api/ops/apps/{appKey}/transfer to move app between tenants
- StoreSubmissionService: read parallelStoreUpload from publish config; conditional parallel vs sequential submission
- AppStoreService: support DINGTALK/WECOM/FEISHU/CUSTOM notify formats in sendWebhook()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- LicenseDeviceService: update app_key on re-registration if blank,
fixing devices that registered before the app_key column was added
- FeatureServiceManager: send activation IM notification in afterCommit()
hook so the frontend refresh sees the committed DB state
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
RestTemplate drops large multipart payloads on the MI API server; switching to
ProcessBuilder curl with Expect:100-continue headers and a 130-minute timeout resolves
upload failures for large APKs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>