Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
227 lines
6.3 KiB
JavaScript
227 lines
6.3 KiB
JavaScript
import { get as secretGet, listKeys as secretListKeys } from 'alga:extension/secrets';
|
|
import { callRoute as uiProxyCallRoute } from 'alga:extension/ui-proxy';
|
|
|
|
if (typeof globalThis.TextEncoder === 'undefined') {
|
|
globalThis.TextEncoder = class {
|
|
encode(value) {
|
|
const text = String(value);
|
|
const bytes = new Uint8Array(text.length);
|
|
for (let i = 0; i < text.length; i += 1) {
|
|
bytes[i] = text.charCodeAt(i) & 0xff;
|
|
}
|
|
return bytes;
|
|
}
|
|
|
|
encodeInto(source, destination) {
|
|
const text = String(source);
|
|
const len = Math.min(text.length, destination.length);
|
|
for (let i = 0; i < len; i += 1) {
|
|
destination[i] = text.charCodeAt(i) & 0xff;
|
|
}
|
|
return { read: len, written: len };
|
|
}
|
|
};
|
|
}
|
|
|
|
if (typeof globalThis.TextDecoder === 'undefined') {
|
|
globalThis.TextDecoder = class {
|
|
decode(view) {
|
|
if (!view || typeof view.length !== 'number') {
|
|
return '';
|
|
}
|
|
let result = '';
|
|
for (let i = 0; i < view.length; i += 1) {
|
|
result += String.fromCharCode(view[i]);
|
|
}
|
|
return result;
|
|
}
|
|
};
|
|
}
|
|
|
|
if (typeof globalThis.process === 'undefined') {
|
|
globalThis.process = { env: {} };
|
|
}
|
|
|
|
if (globalThis.process && globalThis.process.env && !globalThis.process.env.JCO_DEBUG) {
|
|
globalThis.process.env.JCO_DEBUG = '1';
|
|
}
|
|
|
|
const __logBuffer = [];
|
|
|
|
if (typeof globalThis.console === 'undefined') {
|
|
globalThis.console = {};
|
|
}
|
|
|
|
const noop = () => {};
|
|
|
|
const appendLog = (level, args) => {
|
|
try {
|
|
const msg = `[${level}] ${args.map((value) => String(value)).join(' ')}`;
|
|
__logBuffer.push(msg);
|
|
} catch (_) {
|
|
__logBuffer.push(`[${level}] <unprintable>`);
|
|
}
|
|
};
|
|
|
|
globalThis.console.log = (...args) => appendLog('log', args);
|
|
globalThis.console.info = (...args) => appendLog('info', args);
|
|
globalThis.console.warn = (...args) => appendLog('warn', args);
|
|
globalThis.console.error = (...args) => appendLog('error', args);
|
|
|
|
let __nextTimeoutId = 1;
|
|
const __scheduledTimeouts = new Map();
|
|
|
|
if (typeof globalThis.setTimeout === 'undefined') {
|
|
globalThis.setTimeout = (fn) => {
|
|
if (typeof fn !== 'function') {
|
|
return 0;
|
|
}
|
|
const id = __nextTimeoutId++;
|
|
const run = () => {
|
|
if (!__scheduledTimeouts.has(id)) {
|
|
return;
|
|
}
|
|
__scheduledTimeouts.delete(id);
|
|
try {
|
|
fn();
|
|
} catch (err) {
|
|
console.error('setTimeout callback failed', err);
|
|
}
|
|
};
|
|
__scheduledTimeouts.set(id, run);
|
|
Promise.resolve().then(run);
|
|
return id;
|
|
};
|
|
}
|
|
|
|
if (typeof globalThis.clearTimeout === 'undefined') {
|
|
globalThis.clearTimeout = (id) => {
|
|
__scheduledTimeouts.delete(id);
|
|
};
|
|
}
|
|
|
|
const encoder = new TextEncoder();
|
|
|
|
function encodeJson(value) {
|
|
return encoder.encode(JSON.stringify(value));
|
|
}
|
|
|
|
export async function handler(request, host) {
|
|
const logInfo =
|
|
host && host.logging && typeof host.logging.info === 'function'
|
|
? (msg) => {
|
|
try {
|
|
return host.logging.info(String(msg));
|
|
} catch (_) {
|
|
return Promise.resolve();
|
|
}
|
|
}
|
|
: () => Promise.resolve();
|
|
const flushLogs = async () => {
|
|
while (__logBuffer.length > 0) {
|
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
const entry = __logBuffer.shift();
|
|
await logInfo(entry);
|
|
}
|
|
};
|
|
|
|
try {
|
|
console.log('js component handler invoked');
|
|
await flushLogs();
|
|
const ctx = request.context;
|
|
const httpUrl = request?.http?.url || request?.http?.path || '';
|
|
|
|
if (httpUrl.startsWith('/dynamic/secrets')) {
|
|
let secretValue = null;
|
|
let keys = [];
|
|
let secretError = null;
|
|
try {
|
|
keys = (await secretListKeys()) ?? [];
|
|
} catch (err) {
|
|
secretError = err instanceof Error ? err.message : String(err);
|
|
}
|
|
if (!secretError) {
|
|
try {
|
|
secretValue = await secretGet('ALGA_API_KEY');
|
|
} catch (err) {
|
|
secretError = err instanceof Error ? err.message : String(err);
|
|
}
|
|
}
|
|
return {
|
|
status: 200,
|
|
headers: [
|
|
{ name: 'content-type', value: 'application/json' },
|
|
{ name: 'x-generated-by', value: 'js-component' }
|
|
],
|
|
body: encodeJson({
|
|
ok: secretError === null,
|
|
method: request.http.method,
|
|
path: request.http.url,
|
|
tenantId: ctx?.tenantId ?? null,
|
|
extensionId: ctx?.extensionId ?? null,
|
|
secrets: {
|
|
value: secretValue,
|
|
keys,
|
|
error: secretError
|
|
}
|
|
})
|
|
};
|
|
}
|
|
|
|
if (httpUrl.startsWith('/dynamic/ui-proxy')) {
|
|
let proxyResponse = null;
|
|
let proxyError = null;
|
|
try {
|
|
const payload = request.http.body ? new Uint8Array(request.http.body) : new Uint8Array();
|
|
const bytes = await uiProxyCallRoute('/proxy/ping', payload.length ? payload : null);
|
|
proxyResponse = bytes ? Array.from(bytes) : null;
|
|
} catch (err) {
|
|
proxyError = err instanceof Error ? err.message : String(err);
|
|
}
|
|
return {
|
|
status: 200,
|
|
headers: [
|
|
{ name: 'content-type', value: 'application/json' },
|
|
{ name: 'x-generated-by', value: 'js-component' }
|
|
],
|
|
body: encodeJson({
|
|
ok: proxyError === null,
|
|
method: request.http.method,
|
|
path: request.http.url,
|
|
tenantId: ctx?.tenantId ?? null,
|
|
extensionId: ctx?.extensionId ?? null,
|
|
proxy: {
|
|
response: proxyResponse,
|
|
error: proxyError
|
|
}
|
|
})
|
|
};
|
|
}
|
|
|
|
return {
|
|
status: 200,
|
|
headers: [
|
|
{ name: 'content-type', value: 'application/json' },
|
|
{ name: 'x-generated-by', value: 'js-component' }
|
|
],
|
|
body: encodeJson({
|
|
ok: true,
|
|
method: request.http.method,
|
|
path: request.http.url,
|
|
tenantId: ctx?.tenantId ?? null,
|
|
extensionId: ctx?.extensionId ?? null,
|
|
echo: request.http.body ? Array.from(request.http.body) : null
|
|
})
|
|
};
|
|
} catch (err) {
|
|
console.error('js component error', err);
|
|
await flushLogs();
|
|
const message = err instanceof Error ? err.message : String(err);
|
|
return {
|
|
status: 500,
|
|
headers: [{ name: 'content-type', value: 'application/json' }],
|
|
body: encodeJson({ ok: false, error: message })
|
|
};
|
|
}
|
|
}
|