1
0
forked from boos/BoosAPI

feat: implement macOS notarization and ad-hoc signing in afterSign script

feat: add run-server script to manage server execution and compatibility checks

feat: create main application logic for desktop with window management, server handling, and auto-updater

test: add unit tests for desktop runtime helpers

feat: implement desktop server environment configuration and health check

test: add tests for server configuration and desktop routes

feat: add native module guard for better-sqlite3 compatibility

test: add tests for native module guard functionality

test: add comprehensive tests for account verification feedback handling

feat: implement account verification feedback helpers

chore: add TypeScript configuration for desktop builds
This commit is contained in:
cita
2026-03-06 16:24:41 +08:00
parent acdc846b13
commit 2a810facb8
45 changed files with 6712 additions and 405 deletions
+46
View File
@@ -0,0 +1,46 @@
import { notarize } from '@electron/notarize';
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
const execFileAsync = promisify(execFile);
async function hasSignature(appPath) {
try {
await execFileAsync('codesign', ['--verify', '--deep', '--strict', appPath]);
return true;
} catch {
return false;
}
}
async function adHocSign(appPath) {
await execFileAsync('codesign', ['--force', '--deep', '--sign', '-', appPath]);
}
export default async function afterSign(context) {
if (context.electronPlatformName !== 'darwin') return;
const appPath = `${context.appOutDir}/${context.packager.appInfo.productFilename}.app`;
const appleId = process.env.APPLE_ID || process.env.APPLEID;
const appleIdPassword = process.env.APPLE_APP_SPECIFIC_PASSWORD || process.env.APPLEIDPASS;
const teamId = process.env.APPLE_TEAM_ID;
const signed = await hasSignature(appPath);
if (!signed) {
console.log('[metapi-desktop] No macOS signing identity detected, applying ad-hoc signature.');
await adHocSign(appPath);
}
if (!appleId || !appleIdPassword || !teamId) {
console.log('[metapi-desktop] Skipping notarization: APPLE_ID / APPLE_APP_SPECIFIC_PASSWORD / APPLE_TEAM_ID not fully configured.');
return;
}
console.log('[metapi-desktop] Notarizing macOS build...');
await notarize({
appPath,
appleId,
appleIdPassword,
teamId,
});
}
+82
View File
@@ -0,0 +1,82 @@
import { spawn } from 'node:child_process';
import { join } from 'node:path';
import {
buildBetterSqliteRebuildCommand,
isNodeModuleVersionMismatch,
} from '../../src/server/nativeModuleGuard.js';
function run(command: string, args: string[]) {
return new Promise<void>((resolve, reject) => {
const child = spawn(command, args, {
cwd: process.cwd(),
stdio: 'inherit',
shell: false,
env: process.env,
});
child.once('exit', (code, signal) => {
if (code === 0) {
resolve();
return;
}
reject(new Error(`Command failed: ${command} ${args.join(' ')} (code=${code ?? 'null'}, signal=${signal ?? 'null'})`));
});
child.once('error', reject);
});
}
async function ensureBetterSqliteCompatible() {
try {
await import('better-sqlite3');
return;
} catch (error) {
if (!isNodeModuleVersionMismatch(error)) {
throw error;
}
}
const rebuild = buildBetterSqliteRebuildCommand();
console.log('[metapi] Detected better-sqlite3 ABI mismatch. Rebuilding for the current Node.js runtime...');
await run(rebuild.command, rebuild.args);
try {
await import('better-sqlite3');
} catch (error) {
throw new Error(
`better-sqlite3 is still incompatible after rebuild: ${error instanceof Error ? error.message : String(error)}`,
);
}
}
async function main() {
const isWatchMode = process.argv.includes('--watch');
await ensureBetterSqliteCompatible();
if (isWatchMode) {
const tsxCli = join(process.cwd(), 'node_modules', 'tsx', 'dist', 'cli.mjs');
const child = spawn(process.execPath, [tsxCli, 'watch', 'src/server/index.ts'], {
cwd: process.cwd(),
stdio: 'inherit',
env: process.env,
});
child.once('exit', (code) => {
process.exit(code ?? 0);
});
return;
}
const child = spawn(process.execPath, ['dist/server/index.js'], {
cwd: process.cwd(),
stdio: 'inherit',
env: process.env,
});
child.once('exit', (code) => {
process.exit(code ?? 0);
});
}
main().catch((error) => {
console.error(error);
process.exit(1);
});
-31
View File
@@ -1,31 +0,0 @@
@echo off
setlocal
rem Check whether better-sqlite3 matches the current Node.js ABI.
node -e "try{require('better-sqlite3');process.exit(0);}catch(err){if(err&&err.code==='ERR_DLOPEN_FAILED'&&String(err.message||'').includes('NODE_MODULE_VERSION')){process.exit(42);}console.error(err);process.exit(1);}"
set "NATIVE_CHECK_CODE=%errorlevel%"
if %NATIVE_CHECK_CODE% EQU 42 (
echo [metapi] Detected Node.js/native module ABI mismatch. Rebuilding better-sqlite3...
call npm rebuild better-sqlite3 --no-audit --fund=false
if errorlevel 1 (
echo [metapi] npm rebuild failed. Running npm ci --omit=dev as fallback...
call npm ci --omit=dev --no-audit --fund=false
if errorlevel 1 exit /b %errorlevel%
)
node -e "try{require('better-sqlite3');process.exit(0);}catch(err){console.error(err);process.exit(1);}"
if errorlevel 1 (
echo [metapi] better-sqlite3 is still incompatible with current Node.js.
echo [metapi] Recommended: use Node.js 22 LTS or run Docker image 1467078763/metapi:latest.
exit /b %errorlevel%
)
) else if NOT %NATIVE_CHECK_CODE% EQU 0 (
exit /b %NATIVE_CHECK_CODE%
)
node dist/server/db/migrate.js
if errorlevel 1 exit /b %errorlevel%
node dist/server/index.js
exit /b %errorlevel%
-23
View File
@@ -1,23 +0,0 @@
#!/usr/bin/env sh
set -eu
# Check whether better-sqlite3 matches the current Node.js ABI.
set +e
node -e "try{require('better-sqlite3');process.exit(0);}catch(err){if(err&&err.code==='ERR_DLOPEN_FAILED'&&String(err.message||'').includes('NODE_MODULE_VERSION')){process.exit(42);}console.error(err);process.exit(1);}"
native_check_code=$?
set -e
if [ "$native_check_code" -eq 42 ]; then
echo "[metapi] Detected Node.js/native module ABI mismatch. Rebuilding better-sqlite3..."
if ! npm rebuild better-sqlite3 --no-audit --fund=false; then
echo "[metapi] npm rebuild failed. Running npm ci --omit=dev as fallback..."
npm ci --omit=dev --no-audit --fund=false
fi
node -e "try{require('better-sqlite3');process.exit(0);}catch(err){console.error(err);process.exit(1);}"
elif [ "$native_check_code" -ne 0 ]; then
exit "$native_check_code"
fi
node dist/server/db/migrate.js
exec node dist/server/index.js