chore: import upstream snapshot with attribution
Lint and Format Check / lint-and-format (push) Failing after 0s
Check Migrations / Check for duplicate migration numbers (push) Failing after 1s
CI Pre-merge Check / CI Pre-merge Check (push) Failing after 2m17s

This commit is contained in:
wehub-resource-sync
2026-07-13 12:23:40 +08:00
commit 3a28426bf4
1399 changed files with 257375 additions and 0 deletions
+25
View File
@@ -0,0 +1,25 @@
{
"compilerOptions": {
"lib": ["deno.window", "deno.worker"],
"strict": true
},
"lint": {
"files": {
"include": ["./**/*.ts", "./**/*.js"]
},
"rules": {
"tags": ["recommended"]
}
},
"fmt": {
"files": {
"include": ["./**/*.ts", "./**/*.js"]
},
"options": {
"useTabs": false,
"lineWidth": 100,
"indentWidth": 2,
"singleQuote": true
}
}
}
+31
View File
@@ -0,0 +1,31 @@
# InsForge Edge Function Examples
This folder contains **example serverless (edge) functions** you can deploy to InsForge.
## Files
- `demo-hello-world.js`: public function (GET/POST) with CORS + secret example (`HELLO_PREFIX`)
- `demo-whoami.js`: authenticated function (GET) that returns the current user
## Deploy
Use the InsForge MCP tools:
- `create-function` with `slug` matching the function name you want (e.g. `demo-hello-world`)
- `update-function` to redeploy after edits
## Invoke from a client app (SDK)
```js
// GET
await insforge.functions.invoke('demo-hello-world', { method: 'GET' })
// POST
await insforge.functions.invoke('demo-hello-world', {
body: { name: 'Gary' }
})
// Authenticated GET (SDK auto-includes user token if logged in)
await insforge.functions.invoke('demo-whoami', { method: 'GET' })
```
+62
View File
@@ -0,0 +1,62 @@
/**
* Demo: Public "hello world" function
*
* - Supports CORS (OPTIONS)
* - GET: reads from query string (?name=...)
* - POST: reads JSON body { name }
* - Demonstrates reading secrets via Deno.env.get('KEY')
*
* Notes:
* - `createClient` and `Deno` are injected by InsForge's worker template.
* - Keep exports exactly: module.exports = async function(request) { ... }
*/
module.exports = async function (request) {
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
};
if (request.method === 'OPTIONS') {
return new Response(null, { status: 204, headers: corsHeaders });
}
const url = new URL(request.url);
let body = null;
if (request.method !== 'GET') {
try {
body = await request.json();
} catch {
body = null;
}
}
const name =
(typeof body?.name === 'string' && body.name.trim()) ||
(url.searchParams.get('name') || '').trim() ||
'World';
// Example secret (configure in InsForge -> Functions -> Secrets):
// - HELLO_PREFIX="Hello"
const helloPrefix = Deno.env.get('HELLO_PREFIX') || 'Hello';
return new Response(
JSON.stringify(
{
message: `${helloPrefix}, ${name}!`,
timestamp: new Date().toISOString(),
method: request.method,
path: url.pathname,
},
null,
2
),
{
status: 200,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
}
);
};
+70
View File
@@ -0,0 +1,70 @@
/**
* Demo: Authenticated "whoami" function
*
* - Requires Authorization: Bearer <user_jwt>
* - Uses injected createClient() + edgeFunctionToken to call auth.getCurrentUser()
* - Returns 401 if no valid user
*/
module.exports = async function (request) {
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
};
if (request.method === 'OPTIONS') {
return new Response(null, { status: 204, headers: corsHeaders });
}
if (request.method !== 'GET') {
return new Response(JSON.stringify({ error: 'Method not allowed' }), {
status: 405,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}
const authHeader = request.headers.get('Authorization') || '';
const userToken = authHeader.startsWith('Bearer ')
? authHeader.slice('Bearer '.length).trim()
: null;
if (!userToken) {
return new Response(JSON.stringify({ error: 'Missing bearer token' }), {
status: 401,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}
const client = createClient({
baseUrl: Deno.env.get('INSFORGE_INTERNAL_URL') || 'http://insforge:7130',
edgeFunctionToken: userToken,
});
const { data, error } = await client.auth.getCurrentUser();
if (error || !data?.user?.id) {
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
status: 401,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
}
return new Response(
JSON.stringify(
{
user: {
id: data.user.id,
email: data.user.email,
created_at: data.user.created_at,
},
},
null,
2
),
{
status: 200,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
}
);
};
+342
View File
@@ -0,0 +1,342 @@
import { Client } from 'https://deno.land/x/postgres@v0.17.0/mod.ts';
import { join, dirname, fromFileUrl } from 'https://deno.land/std@0.224.0/path/mod.ts';
/* eslint-disable no-console */
const rawPort = Deno.env.get('PORT');
const hasValidNumericPort = rawPort !== undefined && /^\d+$/.test(rawPort);
const parsedPort = hasValidNumericPort ? Number(rawPort) : Number.NaN;
const port =
Number.isInteger(parsedPort) && parsedPort >= 1 && parsedPort <= 65535 ? parsedPort : 7133;
const hostname = Deno.env.get('HOST') ?? '::';
if (rawPort !== undefined && port === 7133 && rawPort !== '7133') {
console.warn(`Invalid PORT value "${rawPort}", falling back to 7133`);
}
console.log(`Deno serverless runtime running on ${hostname}:${port}`);
// Configuration
const WORKER_TIMEOUT_MS = parseInt(Deno.env.get('WORKER_TIMEOUT_MS') ?? '60000');
// Worker template code - loaded on first use
let workerTemplateCode: string | null = null;
async function getWorkerTemplateCode(): Promise<string> {
if (!workerTemplateCode) {
const currentDir = dirname(fromFileUrl(import.meta.url));
workerTemplateCode = await Deno.readTextFile(join(currentDir, 'worker-template.js'));
}
return workerTemplateCode;
}
// Decrypt function for Deno (compatible with Node.js encryption)
async function decryptSecret(ciphertext: string, key: string): Promise<string> {
try {
const parts = ciphertext.split(':');
if (parts.length !== 3) {
throw new Error('Invalid ciphertext format');
}
// Get the encryption key by hashing the JWT secret
const keyData = new TextEncoder().encode(key);
const hashBuffer = await crypto.subtle.digest('SHA-256', keyData);
const cryptoKey = await crypto.subtle.importKey('raw', hashBuffer, { name: 'AES-GCM' }, false, [
'decrypt',
]);
// Extract IV, auth tag, and encrypted data
const iv = Uint8Array.from(parts[0].match(/.{2}/g)!.map((byte) => parseInt(byte, 16)));
const authTag = Uint8Array.from(parts[1].match(/.{2}/g)!.map((byte) => parseInt(byte, 16)));
const encrypted = Uint8Array.from(parts[2].match(/.{2}/g)!.map((byte) => parseInt(byte, 16)));
// Combine encrypted data and auth tag (GCM expects them together)
const cipherData = new Uint8Array(encrypted.length + authTag.length);
cipherData.set(encrypted);
cipherData.set(authTag, encrypted.length);
// Decrypt
const decryptedBuffer = await crypto.subtle.decrypt(
{ name: 'AES-GCM', iv },
cryptoKey,
cipherData
);
return new TextDecoder().decode(decryptedBuffer);
} catch (error) {
console.error('Failed to decrypt secret:', error);
throw error;
}
}
// Database connection
const dbConfig = {
user: Deno.env.get('POSTGRES_USER') || 'postgres',
password: Deno.env.get('POSTGRES_PASSWORD') || 'postgres',
database: Deno.env.get('POSTGRES_DB') || 'insforge',
hostname: Deno.env.get('POSTGRES_HOST') || 'postgres',
port: parseInt(Deno.env.get('POSTGRES_PORT') || '5432', 10),
};
// Get function code from database
async function getFunctionCode(slug: string): Promise<string | null> {
const client = new Client(dbConfig);
try {
await client.connect();
const result = await client.queryObject<{ code: string }>`
SELECT code FROM functions.definitions
WHERE slug = ${slug} AND status = 'active'
`;
if (!result.rows.length) {
return null;
}
return result.rows[0].code;
} catch (error) {
console.error(`Error fetching function ${slug}:`, error);
return null;
} finally {
await client.end();
}
}
// Get all secrets from main secrets table and decrypt them
async function getFunctionSecrets(): Promise<Record<string, string>> {
const client = new Client(dbConfig);
try {
await client.connect();
// Get the encryption key from environment
const encryptionKey = Deno.env.get('ENCRYPTION_KEY') || Deno.env.get('JWT_SECRET');
if (!encryptionKey) {
console.error('No encryption key available for decrypting secrets');
return {};
}
// Fetch all active secrets from system.secrets table
const result = await client.queryObject<{
key: string;
value_ciphertext: string;
}>`
SELECT key, value_ciphertext
FROM system.secrets
WHERE is_active = true
AND (expires_at IS NULL OR expires_at > NOW())
`;
const secrets: Record<string, string> = {};
// Decrypt each secret
for (const row of result.rows) {
try {
secrets[row.key] = await decryptSecret(row.value_ciphertext, encryptionKey);
} catch (error) {
console.error(`Failed to decrypt secret ${row.key}:`, error);
// Skip this secret if decryption fails
}
}
return secrets;
} catch (error) {
console.error('Error fetching secrets:', error);
return {};
} finally {
await client.end();
}
}
// Execute function in isolated worker
async function executeInWorker(code: string, request: Request): Promise<Response> {
// Get worker template
const template = await getWorkerTemplateCode();
// Fetch all function secrets
const secrets = await getFunctionSecrets();
// Create blob for worker
const workerBlob = new Blob([template], { type: 'application/javascript' });
const workerUrl = URL.createObjectURL(workerBlob);
return new Promise(async (resolve) => {
const worker = new Worker(workerUrl, {
type: 'module',
deno: {
permissions: {
// RESTRICTED: Native environment access is disabled to shield host secrets.
// Secrets are passed explicitly via message payload.
env: false,
// SDK/External REQUIREMENT: Allow network access for API connectivity and external integrations.
net: true,
read: false,
write: false,
run: false,
ffi: false,
sys: false,
import: false,
hrtime: false,
},
},
});
// Set timeout for worker execution
const timeout = setTimeout(() => {
worker.terminate();
URL.revokeObjectURL(workerUrl);
resolve(
new Response(JSON.stringify({ error: 'Function timeout' }), {
status: 504,
headers: { 'Content-Type': 'application/json' },
})
);
}, WORKER_TIMEOUT_MS);
// Handle worker response
worker.onmessage = (e) => {
clearTimeout(timeout);
worker.terminate();
URL.revokeObjectURL(workerUrl);
if (e.data.success) {
const { response } = e.data;
// The worker now properly sends null for bodyless responses
resolve(
new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: response.headers,
})
);
} else {
resolve(
new Response(JSON.stringify({ error: e.data.error }), {
status: e.data.status || 500,
headers: { 'Content-Type': 'application/json' },
})
);
}
};
// Handle worker errors
worker.onerror = (error) => {
clearTimeout(timeout);
worker.terminate();
URL.revokeObjectURL(workerUrl);
console.error('Worker error:', error);
resolve(
new Response(JSON.stringify({ error: 'Worker execution error' }), {
status: 500,
headers: { 'Content-Type': 'application/json' },
})
);
};
// Prepare request data
const body = request.body ? await request.text() : null;
const requestData = {
url: request.url,
method: request.method,
headers: Object.fromEntries(request.headers),
body,
};
// Send message with code, request data, and secrets
worker.postMessage({ code, requestData, secrets });
});
}
Deno.serve({ hostname, port }, async (req: Request) => {
const url = new URL(req.url);
const pathname = url.pathname;
// Health check
if (pathname === '/health') {
return new Response(
JSON.stringify({
status: 'ok',
runtime: 'deno',
version: Deno.version.deno,
typescript: Deno.version.typescript,
v8: Deno.version.v8,
}),
{
headers: { 'Content-Type': 'application/json' },
}
);
}
// Function execution - match ONLY exact slug, no subpaths
const slugMatch = pathname.match(/^\/([a-zA-Z0-9_-]+)$/);
if (slugMatch) {
const slug = slugMatch[1];
const startTime = Date.now();
// Get function code from database
const code = await getFunctionCode(slug);
if (!code) {
return new Response(JSON.stringify({ error: 'Function not found or not active' }), {
status: 404,
headers: { 'Content-Type': 'application/json' },
});
}
// Execute in worker with original request
try {
const response = await executeInWorker(code, req);
const duration = Date.now() - startTime;
// Log completed invocations only
console.log(
JSON.stringify({
timestamp: new Date().toISOString(),
level: 'info',
slug,
method: req.method,
status: response.status,
duration: `${duration}ms`,
})
);
return response;
} catch (error) {
const duration = Date.now() - startTime;
console.error(
JSON.stringify({
timestamp: new Date().toISOString(),
level: 'error',
slug,
error: error instanceof Error ? error.message : String(error),
duration: `${duration}ms`,
})
);
return new Response(JSON.stringify({ error: 'Function execution failed' }), {
status: 500,
headers: { 'Content-Type': 'application/json' },
});
}
}
// Runtime info
if (pathname === '/info') {
return new Response(
JSON.stringify({
runtime: 'deno',
version: Deno.version,
env: Deno.env.get('DENO_ENV') || 'production',
database: {
host: dbConfig.hostname,
database: dbConfig.database,
},
}),
{
headers: { 'Content-Type': 'application/json' },
}
);
}
// 404
return new Response('Not Found', { status: 404 });
});
+222
View File
@@ -0,0 +1,222 @@
/**
* Worker Template for Serverless Functions
*
* This code runs inside a Web Worker environment created by Deno.
* Each worker is created fresh for a single request, executes once, and terminates.
*/
/* eslint-env worker */
/* global self, Request, Deno */
// --- SECURITY BLACKOUT (Top-level) ---
// We polyfill Deno.env and process.env BEFORE any imports using Top-Level Await.
// This prevents libraries (like 'debug') from triggering 'NotCapable' errors
// when using a strict native whitelist (env: false).
const sterileEnv = {
NODE_ENV: 'production',
};
try {
// Shadow Deno.env with a pure JS implementation
const mockDenoEnv = {
get: (key) => sterileEnv[key] ?? undefined,
set: () => {
throw new Error('Deno.env.set is disabled');
},
delete: () => {
throw new Error('Deno.env.delete is disabled');
},
toObject: () => ({ ...sterileEnv }),
has: (key) => key in sterileEnv,
};
// Replace global Deno.env
Object.defineProperty(globalThis, 'Deno', {
value: Object.freeze({ env: mockDenoEnv }),
configurable: false, // Lock down permanently (Audit Finding)
writable: false,
});
// Shadow process.env (Node compatibility)
if (!globalThis.process) globalThis.process = {};
globalThis.process.env = { ...sterileEnv };
} catch (e) {
// FATAL: Security setup failed. Terminate immediately to prevent leakage.
console.error('Security shadow application failed:', e);
self.postMessage({
success: false,
error: 'Security Initialization Error',
status: 500,
});
self.close();
throw new Error('Security initialization failed - halting worker');
}
// ----------------------------
// ----------------------------
// EARLY MESSAGE BUFFERING (Race Fix)
// ----------------------------
// The runtime (server.ts -> executeInWorker) calls worker.postMessage()
// immediately after `new Worker()`. Web Workers do NOT queue messages that
// arrive before an `onmessage` handler is registered, so if we only attached
// the handler AFTER the top-level `await import(...)` below (cold import can
// take >200ms), the request message was silently dropped -> 504 timeout.
//
// Fix: register a synchronous handler BEFORE any top-level await. It buffers
// any early message(s). Once imports finish, we swap `self.onmessage` to the
// real handler and drain the buffer through it. Single-threaded execution
// guarantees no message can slip between the swap and the drain.
const __earlyMessages = [];
self.onmessage = (e) => {
__earlyMessages.push(e);
};
// ----------------------------
// LATE IMPORTS (Pre-emptive Mocking)
// ----------------------------
// We use dynamic imports AFTER the environment is shadowed.
//
// If an import fails (e.g. npm registry unreachable under restricted egress),
// the worker can never produce a real handler. Answer the request with an
// explicit 500 (same {success,error,status} shape server.ts onmessage expects)
// instead of hanging to the runtime's 60s timeout.
//
// The request message may NOT have been delivered yet when the import rejects:
// literal dynamic imports are prefetched with the module graph, so a failed
// import rejects before the first event-loop turn. Closing the worker at that
// point would drop the undelivered request and reproduce the silent 504, so we
// only close() after a message has been answered (with a deadline as backstop).
let createClient, encodeBase64, decodeBase64;
let __importFailed = false;
try {
({ createClient } = await import('npm:@insforge/sdk'));
({ encodeBase64, decodeBase64 } =
await import('https://deno.land/std@0.224.0/encoding/base64.ts'));
} catch (importError) {
__importFailed = true;
const failMsg = 'SDK import failed: ' + (importError?.message || importError);
console.error(failMsg);
const answerAndClose = () => {
self.postMessage({ success: false, error: failMsg, status: 500 });
self.close();
};
if (__earlyMessages.splice(0).length > 0) {
answerAndClose();
} else {
self.onmessage = answerAndClose;
setTimeout(() => self.close(), 10000);
}
}
// Handle the single message with code, request data, and secrets
const handleMessage = async (e) => {
const { code, requestData, secrets = {} } = e.data;
try {
/**
* MOCK DENO OBJECT:
* Providing safe secrets access even under strict native lock-down (env: false).
* This fake 'Deno' object is injected into the user function's scope, ensuring
* they only see the secrets we explicitly allow, while the native Deno runtime
* remains blindfolded at the C++ layer.
*/
const mockDeno = {
// Mock only the required Deno.env API for secret retrieval
env: {
get: (key) => secrets[key] ?? undefined,
// (toObject removed for security to prevent secret enumeration)
},
// Explicitly block all subprocess APIs as a secondary defense tier
run: () => {
throw new Error('Deno.run is natively disabled');
},
spawn: () => {
throw new Error('Deno.spawn is natively disabled');
},
Command: function () {
throw new Error('Deno.Command is natively disabled');
},
};
/**
* FUNCTION WRAPPING:
* Injecting mocks into the user function execution scope.
* We pass mockDeno instead of the real Deno global.
*/
const wrapper = new Function(
'exports',
'module',
'createClient',
'Deno',
'encodeBase64',
'decodeBase64',
code
);
const exports = {};
const module = { exports };
// Execute the wrapper, passing mockDeno as the Deno global
wrapper(exports, module, createClient, mockDeno, encodeBase64, decodeBase64);
// Get the exported function
const functionHandler = module.exports || exports.default || exports;
if (typeof functionHandler !== 'function') {
throw new Error(
'No function exported. Expected: module.exports = async function(req) { ... }'
);
}
// Create Request object from data
const request = new Request(requestData.url, {
method: requestData.method,
headers: requestData.headers,
body: requestData.body,
});
// Execute the function
const response = await functionHandler(request);
// Serialize and send response
let body = null;
if (![204, 205, 304].includes(response.status)) {
body = await response.text();
}
const responseData = {
status: response.status,
statusText: response.statusText,
headers: Object.fromEntries(response.headers),
body: body,
};
self.postMessage({ success: true, response: responseData });
} catch (error) {
if (error instanceof Response) {
let body = null;
if (![204, 205, 304].includes(error.status)) {
body = await error.text();
}
const responseData = {
status: error.status,
statusText: error.statusText,
headers: Object.fromEntries(error.headers),
body: body,
};
self.postMessage({ success: true, response: responseData });
} else {
self.postMessage({
success: false,
error: error.message || 'Unknown error',
status: error.status || 500,
});
}
}
};
// Activate the real handler and drain any messages that arrived during import.
if (!__importFailed) {
self.onmessage = handleMessage;
for (const e of __earlyMessages.splice(0)) {
await handleMessage(e);
}
}