mirror of
https://gh-proxy.org/https://github.com/klinsmaya/open-connector.git
synced 2026-11-08 01:16:52 +08:00
## Summary Add OOMOL cloud OAuth as a provider authorization source. Users can save an encrypted project API key, select a cloud provider configuration, authorize named connections, and run their actions and proxy requests through SaaS. Existing connections retain their source when the provider default changes, and third-party credentials remain in SaaS. - Implement project discovery, OAuth request synchronization, remote execution, and persistent cleanup of disconnected cloud accounts. Add SQLite, PostgreSQL, and D1 migrations and storage support, with instance reset and maintenance tooling. - Add Console project and authorization settings, localized errors, disconnect notifications, and styled OAuth completion pages that close after successful authorization. Align local development callback origins and proxy routes. - Improve safe request diagnostics and retry transient project health-check failures once. Execution requests are not automatically replayed. - Group storage implementations by database backend, share guarded HTTP requests with Marketplace, update Zod to 4.6.5, and load provider icons from the public catalog. - Document setup, maintenance, and the manual acceptance checklist. ## Validation - `npm run fix-check` passed. - `npm run build:web` passed. - Latest focused regression run: 346 tests passed across 33 files covering Console, SaaS project/client/routes, OAuth completion, and guarded HTTP requests. - Earlier implementation validation included the broader runtime/storage suites and a real PostgreSQL integration run. Full deployed action/proxy, cleanup recovery, and D1 maintenance acceptance remain documented manual checks; they are not claimed as completed by the unit tests. ## Deployment notes Apply migrations `0014_saas_project` and `0015_saas_cleanup_runtime`, configure encryption and an explicit `OOMOL_CONNECT_ORIGIN`, and enable the relevant cleanup scheduling. The remote SaaS deployment must support the project OAuth and remote execution APIs described in `docs/saas-oauth.md`.
53 lines
2.2 KiB
TypeScript
53 lines
2.2 KiB
TypeScript
import { execFile } from "node:child_process";
|
|
import { mkdtemp, rm } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import { join, resolve } from "node:path";
|
|
import { promisify } from "node:util";
|
|
import { afterEach, expect, it } from "vitest";
|
|
import { AesGcmSecretCodec } from "../src/server/secrets/secret-codec.ts";
|
|
import { SqliteRuntimeDatabase } from "../src/server/storage/sqlite/runtime-store.ts";
|
|
|
|
const directories: string[] = [];
|
|
afterEach(async () => {
|
|
await Promise.all(directories.splice(0).map((path) => rm(path, { recursive: true, force: true })));
|
|
});
|
|
it("runs the offline SQLite reset-instance command while retaining local encrypted data", async () => {
|
|
const directory = await mkdtemp(join(tmpdir(), "connect-reset-instance-"));
|
|
directories.push(directory);
|
|
const codec = new AesGcmSecretCodec("old-key");
|
|
let database = new SqliteRuntimeDatabase(join(directory, "connect.sqlite"), { secretCodec: codec });
|
|
const oldId = await database.saasProjectStore.getInstanceId();
|
|
await database.saasProjectStore.saveProject({
|
|
id: "managed",
|
|
projectId: "project",
|
|
baseUrl: "https://saas.example",
|
|
apiKey: "remote-secret",
|
|
});
|
|
const local = await database.connectionStore.set("local", "default", {
|
|
authType: "api_key",
|
|
apiKey: "local-secret",
|
|
values: {},
|
|
metadata: {},
|
|
profile: { accountId: "local", displayName: "Local", grantedScopes: [] },
|
|
});
|
|
database.close();
|
|
const { stdout } = await promisify(execFile)(
|
|
process.execPath,
|
|
[resolve("scripts/runtime-data.ts"), "reset-instance", "--yes", "--data-dir", directory],
|
|
{
|
|
env: { ...process.env, OOMOL_CONNECT_DATABASE_URL: "", OOMOL_CONNECT_ENCRYPTION_KEY: "old-key" },
|
|
},
|
|
);
|
|
expect(stdout).toContain(oldId);
|
|
expect(stdout).not.toContain("remote-secret");
|
|
expect(stdout).not.toContain("local-secret");
|
|
database = new SqliteRuntimeDatabase(join(directory, "connect.sqlite"), { secretCodec: codec });
|
|
try {
|
|
expect(await database.saasProjectStore.getInstanceId()).not.toBe(oldId);
|
|
expect(await database.saasProjectStore.getProject()).toBeUndefined();
|
|
expect(await database.connectionStore.get("local", "default")).toEqual(local);
|
|
} finally {
|
|
database.close();
|
|
}
|
|
});
|