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:
@@ -20,6 +20,21 @@ describe('accountHealthService', () => {
|
||||
expect(health.state).toBe('unhealthy');
|
||||
});
|
||||
|
||||
it('does not reuse expired session-token health for proxy-only accounts', () => {
|
||||
const health = buildRuntimeHealthForAccount({
|
||||
accountStatus: 'expired',
|
||||
siteStatus: 'active',
|
||||
extraConfig: null,
|
||||
sessionCapable: false,
|
||||
});
|
||||
|
||||
expect(health).toMatchObject({
|
||||
state: 'unknown',
|
||||
source: 'none',
|
||||
});
|
||||
expect(health.reason).not.toContain('访问令牌已过期');
|
||||
});
|
||||
|
||||
it('returns stored runtime health from extra config when available', () => {
|
||||
const health = buildRuntimeHealthForAccount({
|
||||
accountStatus: 'active',
|
||||
|
||||
@@ -86,6 +86,7 @@ export function buildRuntimeHealthForAccount(input: {
|
||||
accountStatus?: string | null;
|
||||
siteStatus?: string | null;
|
||||
extraConfig?: string | null;
|
||||
sessionCapable?: boolean;
|
||||
}): RuntimeHealthInfo {
|
||||
const accountStatus = (input.accountStatus || 'active').toLowerCase();
|
||||
const siteStatus = (input.siteStatus || 'active').toLowerCase();
|
||||
@@ -99,7 +100,7 @@ export function buildRuntimeHealthForAccount(input: {
|
||||
};
|
||||
}
|
||||
|
||||
if (accountStatus === 'expired') {
|
||||
if (accountStatus === 'expired' && input.sessionCapable !== false) {
|
||||
return {
|
||||
state: 'unhealthy',
|
||||
reason: '访问令牌已过期',
|
||||
|
||||
@@ -13,6 +13,7 @@ describe('databaseMigrationService', () => {
|
||||
dialect: 'postgres',
|
||||
connectionString: 'postgres://user:pass@db.example.com:5432/metapi',
|
||||
overwrite: true,
|
||||
ssl: false,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,6 +25,7 @@ describe('databaseMigrationService', () => {
|
||||
|
||||
expect(normalized.dialect).toBe('mysql');
|
||||
expect(normalized.overwrite).toBe(true);
|
||||
expect(normalized.ssl).toBe(false);
|
||||
});
|
||||
|
||||
it('accepts sqlite file migration target path', () => {
|
||||
@@ -37,6 +39,7 @@ describe('databaseMigrationService', () => {
|
||||
dialect: 'sqlite',
|
||||
connectionString: './data/target.db',
|
||||
overwrite: false,
|
||||
ssl: false,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -58,5 +61,40 @@ describe('databaseMigrationService', () => {
|
||||
const masked = maskConnectionString('postgres://admin:super-secret@db.example.com:5432/metapi');
|
||||
expect(masked).toBe('postgres://admin:***@db.example.com:5432/metapi');
|
||||
});
|
||||
|
||||
it('normalizes ssl boolean from input', () => {
|
||||
const normalized = normalizeMigrationInput({
|
||||
dialect: 'mysql',
|
||||
connectionString: 'mysql://user:pass@tidb.example.com:4000/db',
|
||||
ssl: true,
|
||||
});
|
||||
expect(normalized.ssl).toBe(true);
|
||||
});
|
||||
|
||||
it('defaults ssl to false when not provided', () => {
|
||||
const normalized = normalizeMigrationInput({
|
||||
dialect: 'postgres',
|
||||
connectionString: 'postgres://user:pass@db.example.com:5432/metapi',
|
||||
});
|
||||
expect(normalized.ssl).toBe(false);
|
||||
});
|
||||
|
||||
it('parses ssl from string values', () => {
|
||||
const normalized = normalizeMigrationInput({
|
||||
dialect: 'mysql',
|
||||
connectionString: 'mysql://user:pass@host:3306/db',
|
||||
ssl: '1',
|
||||
});
|
||||
expect(normalized.ssl).toBe(true);
|
||||
});
|
||||
|
||||
it('parses ssl false from string "0"', () => {
|
||||
const normalized = normalizeMigrationInput({
|
||||
dialect: 'mysql',
|
||||
connectionString: 'mysql://user:pass@host:3306/db',
|
||||
ssl: '0',
|
||||
});
|
||||
expect(normalized.ssl).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -11,12 +11,14 @@ export interface DatabaseMigrationInput {
|
||||
dialect?: unknown;
|
||||
connectionString?: unknown;
|
||||
overwrite?: unknown;
|
||||
ssl?: unknown;
|
||||
}
|
||||
|
||||
export interface NormalizedDatabaseMigrationInput {
|
||||
dialect: MigrationDialect;
|
||||
connectionString: string;
|
||||
overwrite: boolean;
|
||||
ssl: boolean;
|
||||
}
|
||||
|
||||
type BackupSnapshot = {
|
||||
@@ -173,6 +175,7 @@ export function normalizeMigrationInput(input: DatabaseMigrationInput): Normaliz
|
||||
dialect,
|
||||
connectionString,
|
||||
overwrite: input.overwrite === undefined ? true : asBoolean(input.overwrite, true),
|
||||
ssl: asBoolean(input.ssl, false),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -224,8 +227,12 @@ async function toBackupSnapshot(): Promise<BackupSnapshot> {
|
||||
};
|
||||
}
|
||||
|
||||
async function createPostgresClient(connectionString: string): Promise<SqlClient> {
|
||||
const client = new pg.Client({ connectionString });
|
||||
async function createPostgresClient(connectionString: string, ssl: boolean): Promise<SqlClient> {
|
||||
const clientOptions: pg.ClientConfig = { connectionString };
|
||||
if (ssl) {
|
||||
clientOptions.ssl = { rejectUnauthorized: false };
|
||||
}
|
||||
const client = new pg.Client(clientOptions);
|
||||
await client.connect();
|
||||
|
||||
return {
|
||||
@@ -244,8 +251,12 @@ async function createPostgresClient(connectionString: string): Promise<SqlClient
|
||||
};
|
||||
}
|
||||
|
||||
async function createMySqlClient(connectionString: string): Promise<SqlClient> {
|
||||
const connection = await mysql.createConnection(connectionString);
|
||||
async function createMySqlClient(connectionString: string, ssl: boolean): Promise<SqlClient> {
|
||||
const connectionOptions: mysql.ConnectionOptions = { uri: connectionString };
|
||||
if (ssl) {
|
||||
connectionOptions.ssl = { rejectUnauthorized: false };
|
||||
}
|
||||
const connection = await mysql.createConnection(connectionOptions);
|
||||
|
||||
return {
|
||||
dialect: 'mysql',
|
||||
@@ -291,8 +302,8 @@ async function createSqliteClient(connectionString: string): Promise<SqlClient>
|
||||
}
|
||||
|
||||
async function createClient(input: NormalizedDatabaseMigrationInput): Promise<SqlClient> {
|
||||
if (input.dialect === 'postgres') return createPostgresClient(input.connectionString);
|
||||
if (input.dialect === 'mysql') return createMySqlClient(input.connectionString);
|
||||
if (input.dialect === 'postgres') return createPostgresClient(input.connectionString, input.ssl);
|
||||
if (input.dialect === 'mysql') return createMySqlClient(input.connectionString, input.ssl);
|
||||
return createSqliteClient(input.connectionString);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user