feat: Phase 1 mobile tablet — Jenkins APK, PC tab, in-app update

Capacitor 확장으로 git push 시 Mobile Jenkins 빌드, PC 탭 Android/iPad 다운로드,
앱 내 APK 업데이트, iOS TestFlight API 및 Capacitor ios 프로젝트를 추가한다.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Macbook
2026-06-14 16:31:55 +09:00
parent 36c06b55ea
commit f34104b721
38 changed files with 1468 additions and 15 deletions
+91
View File
@@ -0,0 +1,91 @@
/**
* Android 앱 내 업데이트 — /api/mobile-app/info 버전 비교 후 APK 다운로드
*/
import { App } from '@capacitor/app';
import { Capacitor } from '@capacitor/core';
import {
isRemoteVersionNewer,
loadMobileAppReleaseInfo,
type MobileAppReleaseInfoDto,
} from '../lib/shared';
export interface AppUpdateCheckResult {
updateAvailable: boolean;
currentVersion: string;
remoteVersion?: string;
release?: MobileAppReleaseInfoDto | null;
}
export async function getCurrentAppVersion(): Promise<string> {
if (!Capacitor.isNativePlatform()) {
return import.meta.env.VITE_APP_VERSION ?? '0.0.0';
}
try {
const info = await App.getInfo();
return info.version ?? '0.0.0';
} catch {
return '0.0.0';
}
}
export async function checkForAppUpdate(): Promise<AppUpdateCheckResult> {
const currentVersion = await getCurrentAppVersion();
const release = await loadMobileAppReleaseInfo();
const remoteVersion = release?.version?.trim();
const updateAvailable = Boolean(
release?.available
&& remoteVersion
&& isRemoteVersionNewer(currentVersion, remoteVersion),
);
return { updateAvailable, currentVersion, remoteVersion, release };
}
export async function openApkDownload(release?: MobileAppReleaseInfoDto | null): Promise<boolean> {
const info = release ?? await loadMobileAppReleaseInfo();
const url = info?.downloadUrl?.trim();
if (!url) return false;
window.location.href = url;
return true;
}
export async function promptAppUpdateIfNeeded(silent = false): Promise<boolean> {
if (!Capacitor.isNativePlatform() || Capacitor.getPlatform() !== 'android') {
return false;
}
const check = await checkForAppUpdate();
if (!check.updateAvailable || !check.remoteVersion) {
return false;
}
if (silent) {
return false;
}
const ok = window.confirm(
`새 버전(v${check.remoteVersion})이 있습니다.\n`
+ `현재: v${check.currentVersion}\n\n`
+ 'APK를 다운로드하여 설치하시겠습니까?',
);
if (!ok) return false;
await openApkDownload(check.release);
return true;
}
/** 설정 화면 등 — 수동 업데이트 확인 */
export async function checkAndPromptAppUpdate(): Promise<AppUpdateCheckResult> {
const result = await checkForAppUpdate();
if (!result.updateAvailable) {
window.alert(`최신 버전입니다 (v${result.currentVersion}).`);
return result;
}
const ok = window.confirm(
`새 버전 v${result.remoteVersion} 사용 가능.\nAPK를 다운로드하시겠습니까?`,
);
if (ok) {
await openApkDownload(result.release);
}
return result;
}