@surf-ai/sdk 1.0.0-alpha.0 → 1.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/db/index.cjs +154 -3
- package/dist/db/index.d.cts +35 -1
- package/dist/db/index.d.ts +35 -1
- package/dist/db/index.js +148 -2
- package/dist/server/index.cjs +171 -186
- package/dist/server/index.js +252 -134
- package/package.json +1 -1
- package/dist/chunk-4NA3GKVD.js +0 -100
- package/dist/client-Z45B2GYT.js +0 -8
package/dist/server/index.js
CHANGED
|
@@ -1,16 +1,244 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
7
|
|
|
8
8
|
// src/server/runtime.ts
|
|
9
9
|
import express from "express";
|
|
10
10
|
import cors from "cors";
|
|
11
|
-
import
|
|
11
|
+
import fs2 from "fs";
|
|
12
12
|
import path from "path";
|
|
13
13
|
import { Cron } from "croner";
|
|
14
|
+
|
|
15
|
+
// src/core/config.ts
|
|
16
|
+
var DEFAULT_API_BASE_URL = "https://api.ask.surf/gateway/v1";
|
|
17
|
+
function trimTrailingSlashes(value) {
|
|
18
|
+
return String(value || "").replace(/\/+$/, "");
|
|
19
|
+
}
|
|
20
|
+
function readSurfApiConfig() {
|
|
21
|
+
return {
|
|
22
|
+
baseUrl: trimTrailingSlashes(process.env.SURF_API_BASE_URL || DEFAULT_API_BASE_URL),
|
|
23
|
+
apiKey: process.env.SURF_API_KEY
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function requireSurfApiConfig() {
|
|
27
|
+
const config = readSurfApiConfig();
|
|
28
|
+
if (!config.apiKey) {
|
|
29
|
+
throw new Error("SURF_API_KEY is required");
|
|
30
|
+
}
|
|
31
|
+
return { baseUrl: config.baseUrl, apiKey: config.apiKey };
|
|
32
|
+
}
|
|
33
|
+
function readAdminApiKey() {
|
|
34
|
+
return process.env.SURF_API_KEY;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// src/db/schema-sync.ts
|
|
38
|
+
import fs from "fs";
|
|
39
|
+
|
|
40
|
+
// src/core/transport.ts
|
|
41
|
+
function sleep(ms) {
|
|
42
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
43
|
+
}
|
|
44
|
+
function normalizePath(path2) {
|
|
45
|
+
return String(path2 || "").replace(/^\/+/, "");
|
|
46
|
+
}
|
|
47
|
+
function buildUrl(path2, params) {
|
|
48
|
+
const { baseUrl } = requireSurfApiConfig();
|
|
49
|
+
const url = new URL(`${baseUrl}/${normalizePath(path2)}`);
|
|
50
|
+
if (params) {
|
|
51
|
+
for (const [key, value] of Object.entries(params)) {
|
|
52
|
+
if (value != null) {
|
|
53
|
+
url.searchParams.set(key, String(value));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return url.toString();
|
|
58
|
+
}
|
|
59
|
+
function buildHeaders(extra) {
|
|
60
|
+
const { apiKey } = requireSurfApiConfig();
|
|
61
|
+
const headers = new Headers(extra);
|
|
62
|
+
headers.set("Authorization", `Bearer ${apiKey}`);
|
|
63
|
+
return headers;
|
|
64
|
+
}
|
|
65
|
+
async function fetchJson(url, init, retries = 1) {
|
|
66
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
67
|
+
const res = await fetch(url, init);
|
|
68
|
+
if (!res.ok) {
|
|
69
|
+
const text2 = await res.text();
|
|
70
|
+
throw new Error(`API error ${res.status}: ${text2.slice(0, 200)}`);
|
|
71
|
+
}
|
|
72
|
+
const text = await res.text();
|
|
73
|
+
if (text) {
|
|
74
|
+
return JSON.parse(text);
|
|
75
|
+
}
|
|
76
|
+
if (attempt < retries) {
|
|
77
|
+
await sleep(1e3);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
throw new Error(`Empty response from ${url}`);
|
|
81
|
+
}
|
|
82
|
+
async function getJson(path2, params) {
|
|
83
|
+
return fetchJson(buildUrl(path2, params), {
|
|
84
|
+
headers: buildHeaders()
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
async function postJson(path2, body) {
|
|
88
|
+
return fetchJson(buildUrl(path2), {
|
|
89
|
+
method: "POST",
|
|
90
|
+
headers: buildHeaders({
|
|
91
|
+
"Content-Type": "application/json"
|
|
92
|
+
}),
|
|
93
|
+
body: body ? JSON.stringify(body) : void 0
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/data/client.ts
|
|
98
|
+
async function get(path2, params) {
|
|
99
|
+
return getJson(path2, params);
|
|
100
|
+
}
|
|
101
|
+
async function post(path2, body) {
|
|
102
|
+
return postJson(path2, body);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/db/schema-sync.ts
|
|
106
|
+
var syncing = false;
|
|
107
|
+
async function syncSchema(options) {
|
|
108
|
+
const { schemaPath, retries = 3, retryDelay = 2e3 } = options;
|
|
109
|
+
for (let i = 0; i < retries; i++) {
|
|
110
|
+
try {
|
|
111
|
+
await doSyncSchema(schemaPath);
|
|
112
|
+
return;
|
|
113
|
+
} catch (err) {
|
|
114
|
+
console.error(`DB schema sync attempt ${i + 1}/${retries} failed: ${err.message}`);
|
|
115
|
+
if (i < retries - 1) await new Promise((r) => setTimeout(r, retryDelay * (i + 1)));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
console.error("DB schema sync failed after all retries");
|
|
119
|
+
}
|
|
120
|
+
async function doSyncSchema(schemaPath) {
|
|
121
|
+
if (syncing) return;
|
|
122
|
+
syncing = true;
|
|
123
|
+
try {
|
|
124
|
+
if (!fs.existsSync(schemaPath)) return;
|
|
125
|
+
let schema;
|
|
126
|
+
if (schemaPath.endsWith(".ts")) {
|
|
127
|
+
try {
|
|
128
|
+
schema = await import(`${schemaPath}?t=${Date.now()}`);
|
|
129
|
+
} catch (err) {
|
|
130
|
+
if (err instanceof SyntaxError) {
|
|
131
|
+
console.log("DB: schema file has syntax error, waiting for next change...");
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (err.message.includes("Cannot find module") || err.message.includes("is not a function")) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
throw err;
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
try {
|
|
141
|
+
delete __require.cache[__require.resolve(schemaPath)];
|
|
142
|
+
} catch {
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
schema = __require(schemaPath);
|
|
146
|
+
} catch (err) {
|
|
147
|
+
if (err instanceof SyntaxError) {
|
|
148
|
+
console.log("DB: schema file has syntax error, waiting for next change...");
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (err.message.includes("Cannot find module") || err.message.includes("is not a function")) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
throw err;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
let getTableConfig;
|
|
158
|
+
try {
|
|
159
|
+
getTableConfig = __require("drizzle-orm/pg-core").getTableConfig;
|
|
160
|
+
} catch {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const tables = Object.values(schema).filter(
|
|
164
|
+
(t) => t && typeof t === "object" && /* @__PURE__ */ Symbol.for("drizzle:Name") in t
|
|
165
|
+
);
|
|
166
|
+
if (tables.length === 0) return;
|
|
167
|
+
await post("db/provision", {});
|
|
168
|
+
const existing = (await get("db/tables")).map((t) => t.name);
|
|
169
|
+
const missing = tables.filter((t) => !existing.includes(getTableConfig(t).name));
|
|
170
|
+
if (missing.length > 0) {
|
|
171
|
+
const { generateDrizzleJson, generateMigration } = __require("drizzle-kit/api");
|
|
172
|
+
const missingSchema = {};
|
|
173
|
+
for (const t of missing) missingSchema[getTableConfig(t).name] = t;
|
|
174
|
+
const sqls = await generateMigration(generateDrizzleJson({}), generateDrizzleJson(missingSchema));
|
|
175
|
+
for (const sql of sqls) {
|
|
176
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
177
|
+
try {
|
|
178
|
+
await post("db/query", { sql, params: [] });
|
|
179
|
+
console.log(`DB: Executed: ${sql.slice(0, 80)}...`);
|
|
180
|
+
break;
|
|
181
|
+
} catch (err) {
|
|
182
|
+
if (attempt === 0) {
|
|
183
|
+
console.warn(`DB: Retrying after: ${err.message}`);
|
|
184
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
185
|
+
} else {
|
|
186
|
+
console.error(`DB: Failed: ${sql.slice(0, 80)}... \u2014 ${err.message}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
const existingTables = tables.filter((t) => existing.includes(getTableConfig(t).name));
|
|
193
|
+
for (const t of existingTables) {
|
|
194
|
+
const cfg = getTableConfig(t);
|
|
195
|
+
try {
|
|
196
|
+
const live = await get("db/table-schema", { table: cfg.name });
|
|
197
|
+
const liveCols = new Set((live.columns || []).map((c) => c.name));
|
|
198
|
+
for (const col of cfg.columns) {
|
|
199
|
+
if (!liveCols.has(col.name)) {
|
|
200
|
+
const colType = col.getSQLType();
|
|
201
|
+
const ddl = `ALTER TABLE "${cfg.name}" ADD COLUMN IF NOT EXISTS "${col.name}" ${colType}`;
|
|
202
|
+
try {
|
|
203
|
+
await post("db/query", { sql: ddl, params: [] });
|
|
204
|
+
console.log(`DB: Added column ${col.name} to ${cfg.name}`);
|
|
205
|
+
} catch (err) {
|
|
206
|
+
console.warn(`DB: Failed to add column ${col.name} to ${cfg.name}: ${err.message}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
} catch (err) {
|
|
211
|
+
console.warn(`DB: Column check failed for ${cfg.name}: ${err.message}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
} finally {
|
|
215
|
+
syncing = false;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function watchSchema(schemaPath, options) {
|
|
219
|
+
const { debounceMs = 1e3, retries = 2, retryDelay = 1500 } = options || {};
|
|
220
|
+
if (!fs.existsSync(schemaPath)) return () => {
|
|
221
|
+
};
|
|
222
|
+
let debounce = null;
|
|
223
|
+
fs.watchFile(schemaPath, { interval: 2e3 }, () => {
|
|
224
|
+
if (debounce) clearTimeout(debounce);
|
|
225
|
+
debounce = setTimeout(async () => {
|
|
226
|
+
console.log("DB: schema file changed, re-syncing tables...");
|
|
227
|
+
try {
|
|
228
|
+
await syncSchema({ schemaPath, retries, retryDelay });
|
|
229
|
+
console.log("DB: schema re-sync complete");
|
|
230
|
+
} catch (err) {
|
|
231
|
+
console.error(`DB: schema re-sync failed: ${err.message}`);
|
|
232
|
+
}
|
|
233
|
+
}, debounceMs);
|
|
234
|
+
});
|
|
235
|
+
return () => {
|
|
236
|
+
fs.unwatchFile(schemaPath);
|
|
237
|
+
if (debounce) clearTimeout(debounce);
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// src/server/runtime.ts
|
|
14
242
|
function requireBearerAuth(req, res, next) {
|
|
15
243
|
const apiKey = readAdminApiKey();
|
|
16
244
|
if (!apiKey) {
|
|
@@ -22,9 +250,10 @@ function requireBearerAuth(req, res, next) {
|
|
|
22
250
|
next();
|
|
23
251
|
}
|
|
24
252
|
function createServer(options = {}) {
|
|
25
|
-
const
|
|
253
|
+
const rawPort = process.env.BACKEND_PORT;
|
|
254
|
+
const port = options.port ?? (rawPort ? Number.parseInt(rawPort, 10) : void 0);
|
|
26
255
|
if (!Number.isInteger(port)) {
|
|
27
|
-
throw new Error("createServer requires a port via options.port or
|
|
256
|
+
throw new Error("createServer requires a port via options.port or BACKEND_PORT env var");
|
|
28
257
|
}
|
|
29
258
|
const routesDir = options.routesDir || path.join(process.cwd(), "routes");
|
|
30
259
|
const cronDir = options.cronDir || process.cwd();
|
|
@@ -34,8 +263,8 @@ function createServer(options = {}) {
|
|
|
34
263
|
app.get("/api/health", (_req, res) => {
|
|
35
264
|
res.json({ status: "ok" });
|
|
36
265
|
});
|
|
37
|
-
if (
|
|
38
|
-
for (const file of
|
|
266
|
+
if (fs2.existsSync(routesDir)) {
|
|
267
|
+
for (const file of fs2.readdirSync(routesDir)) {
|
|
39
268
|
if (!file.endsWith(".js") && !file.endsWith(".ts")) continue;
|
|
40
269
|
const name = file.replace(/\.(js|ts)$/, "");
|
|
41
270
|
try {
|
|
@@ -73,108 +302,11 @@ var schemaSync = { run: async () => {
|
|
|
73
302
|
}, watch: () => {
|
|
74
303
|
} };
|
|
75
304
|
function setupSchemaSync(app, schemaDir) {
|
|
76
|
-
let syncing = false;
|
|
77
305
|
let schemaReady = false;
|
|
78
|
-
|
|
79
|
-
if (syncing) return;
|
|
80
|
-
syncing = true;
|
|
81
|
-
try {
|
|
82
|
-
const schemaPath = path.join(schemaDir, "schema.js");
|
|
83
|
-
if (!fs.existsSync(schemaPath)) return;
|
|
84
|
-
try {
|
|
85
|
-
delete __require.cache[__require.resolve(schemaPath)];
|
|
86
|
-
} catch {
|
|
87
|
-
}
|
|
88
|
-
let schema;
|
|
89
|
-
try {
|
|
90
|
-
schema = __require(schemaPath);
|
|
91
|
-
} catch (err) {
|
|
92
|
-
if (err instanceof SyntaxError) {
|
|
93
|
-
console.log("DB: schema.js has syntax error, waiting for next change...");
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
if (err.message.includes("Cannot find module") || err.message.includes("is not a function")) {
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
throw err;
|
|
100
|
-
}
|
|
101
|
-
let getTableConfig;
|
|
102
|
-
try {
|
|
103
|
-
getTableConfig = __require("drizzle-orm/pg-core").getTableConfig;
|
|
104
|
-
} catch {
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
const tables = Object.values(schema).filter(
|
|
108
|
-
(t) => t && typeof t === "object" && /* @__PURE__ */ Symbol.for("drizzle:Name") in t
|
|
109
|
-
);
|
|
110
|
-
if (tables.length === 0) return;
|
|
111
|
-
const { get: dbGet, post: dbPost } = await import("../client-Z45B2GYT.js");
|
|
112
|
-
await dbPost("db/provision");
|
|
113
|
-
const existing = (await dbGet("db/tables")).map((t) => t.name);
|
|
114
|
-
const missing = tables.filter((t) => !existing.includes(getTableConfig(t).name));
|
|
115
|
-
if (missing.length > 0) {
|
|
116
|
-
const { generateDrizzleJson, generateMigration } = __require("drizzle-kit/api");
|
|
117
|
-
const missingSchema = {};
|
|
118
|
-
for (const t of missing) missingSchema[getTableConfig(t).name] = t;
|
|
119
|
-
const sqls = await generateMigration(generateDrizzleJson({}), generateDrizzleJson(missingSchema));
|
|
120
|
-
for (const sql of sqls) {
|
|
121
|
-
for (let attempt = 0; attempt < 2; attempt++) {
|
|
122
|
-
try {
|
|
123
|
-
await dbPost("db/query", { sql, params: [] });
|
|
124
|
-
console.log(`DB: Executed: ${sql.slice(0, 80)}...`);
|
|
125
|
-
break;
|
|
126
|
-
} catch (err) {
|
|
127
|
-
if (attempt === 0) {
|
|
128
|
-
console.warn(`DB: Retrying after: ${err.message}`);
|
|
129
|
-
await new Promise((r) => setTimeout(r, 1500));
|
|
130
|
-
} else {
|
|
131
|
-
console.error(`DB: Failed: ${sql.slice(0, 80)}... \u2014 ${err.message}`);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
const existingTables = tables.filter((t) => existing.includes(getTableConfig(t).name));
|
|
138
|
-
for (const t of existingTables) {
|
|
139
|
-
const cfg = getTableConfig(t);
|
|
140
|
-
try {
|
|
141
|
-
const live = await dbGet("db/table-schema", { table: cfg.name });
|
|
142
|
-
const liveCols = new Set((live.columns || []).map((c) => c.name));
|
|
143
|
-
for (const col of cfg.columns) {
|
|
144
|
-
if (!liveCols.has(col.name)) {
|
|
145
|
-
const colType = col.getSQLType();
|
|
146
|
-
const ddl = `ALTER TABLE "${cfg.name}" ADD COLUMN IF NOT EXISTS "${col.name}" ${colType}`;
|
|
147
|
-
try {
|
|
148
|
-
await dbPost("db/query", { sql: ddl, params: [] });
|
|
149
|
-
console.log(`DB: Added column ${col.name} to ${cfg.name}`);
|
|
150
|
-
} catch (err) {
|
|
151
|
-
console.warn(`DB: Failed to add column ${col.name} to ${cfg.name}: ${err.message}`);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
} catch (err) {
|
|
156
|
-
console.warn(`DB: Column check failed for ${cfg.name}: ${err.message}`);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
} finally {
|
|
160
|
-
syncing = false;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
async function syncWithRetry(retries = 3, delay = 2e3) {
|
|
164
|
-
for (let i = 0; i < retries; i++) {
|
|
165
|
-
try {
|
|
166
|
-
await doSyncSchema();
|
|
167
|
-
return;
|
|
168
|
-
} catch (err) {
|
|
169
|
-
console.error(`DB schema sync attempt ${i + 1}/${retries} failed: ${err.message}`);
|
|
170
|
-
if (i < retries - 1) await new Promise((r) => setTimeout(r, delay * (i + 1)));
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
console.error("DB schema sync failed after all retries");
|
|
174
|
-
}
|
|
306
|
+
const schemaPath = path.join(schemaDir, "schema.js");
|
|
175
307
|
app.post("/api/__sync-schema", requireBearerAuth, async (_req, res) => {
|
|
176
308
|
try {
|
|
177
|
-
await
|
|
309
|
+
await syncSchema({ schemaPath, retries: 2, retryDelay: 1500 });
|
|
178
310
|
res.json({ ok: true });
|
|
179
311
|
} catch (err) {
|
|
180
312
|
res.status(500).json({ ok: false, error: err.message });
|
|
@@ -186,7 +318,7 @@ function setupSchemaSync(app, schemaDir) {
|
|
|
186
318
|
});
|
|
187
319
|
schemaSync.run = async () => {
|
|
188
320
|
try {
|
|
189
|
-
await
|
|
321
|
+
await syncSchema({ schemaPath });
|
|
190
322
|
schemaReady = true;
|
|
191
323
|
console.log("Schema sync complete, API ready");
|
|
192
324
|
} catch {
|
|
@@ -195,21 +327,7 @@ function setupSchemaSync(app, schemaDir) {
|
|
|
195
327
|
}
|
|
196
328
|
};
|
|
197
329
|
schemaSync.watch = () => {
|
|
198
|
-
|
|
199
|
-
if (!fs.existsSync(schemaPath)) return;
|
|
200
|
-
let debounce = null;
|
|
201
|
-
fs.watchFile(schemaPath, { interval: 2e3 }, () => {
|
|
202
|
-
if (debounce) clearTimeout(debounce);
|
|
203
|
-
debounce = setTimeout(async () => {
|
|
204
|
-
console.log("DB: schema.js changed, re-syncing tables...");
|
|
205
|
-
try {
|
|
206
|
-
await syncWithRetry(2, 1500);
|
|
207
|
-
console.log("DB: schema re-sync complete");
|
|
208
|
-
} catch (err) {
|
|
209
|
-
console.error(`DB: schema re-sync failed: ${err.message}`);
|
|
210
|
-
}
|
|
211
|
-
}, 1e3);
|
|
212
|
-
});
|
|
330
|
+
watchSchema(schemaPath);
|
|
213
331
|
};
|
|
214
332
|
}
|
|
215
333
|
function setupCron(app, cronDir) {
|
|
@@ -225,10 +343,10 @@ function setupCron(app, cronDir) {
|
|
|
225
343
|
}
|
|
226
344
|
cronJobs.clear();
|
|
227
345
|
const cronPath = path.join(cronDir, "cron.json");
|
|
228
|
-
if (!
|
|
346
|
+
if (!fs2.existsSync(cronPath)) return;
|
|
229
347
|
let tasks;
|
|
230
348
|
try {
|
|
231
|
-
tasks = JSON.parse(
|
|
349
|
+
tasks = JSON.parse(fs2.readFileSync(cronPath, "utf-8"));
|
|
232
350
|
} catch (e) {
|
|
233
351
|
console.error("Failed to parse cron.json:", e.message);
|
|
234
352
|
return;
|
|
@@ -309,7 +427,7 @@ function setupCron(app, cronDir) {
|
|
|
309
427
|
const cronPath = path.join(cronDir, "cron.json");
|
|
310
428
|
let tasks = [];
|
|
311
429
|
try {
|
|
312
|
-
if (
|
|
430
|
+
if (fs2.existsSync(cronPath)) tasks = JSON.parse(fs2.readFileSync(cronPath, "utf8"));
|
|
313
431
|
} catch {
|
|
314
432
|
tasks = [];
|
|
315
433
|
}
|
|
@@ -318,7 +436,7 @@ function setupCron(app, cronDir) {
|
|
|
318
436
|
}
|
|
319
437
|
const newTask = { id, name, schedule, handler, enabled, timeout };
|
|
320
438
|
tasks.push(newTask);
|
|
321
|
-
|
|
439
|
+
fs2.writeFileSync(cronPath, JSON.stringify(tasks, null, 2));
|
|
322
440
|
loadCronJobs();
|
|
323
441
|
res.status(201).json(newTask);
|
|
324
442
|
});
|
|
@@ -326,7 +444,7 @@ function setupCron(app, cronDir) {
|
|
|
326
444
|
const cronPath = path.join(cronDir, "cron.json");
|
|
327
445
|
let tasks = [];
|
|
328
446
|
try {
|
|
329
|
-
if (
|
|
447
|
+
if (fs2.existsSync(cronPath)) tasks = JSON.parse(fs2.readFileSync(cronPath, "utf8"));
|
|
330
448
|
} catch {
|
|
331
449
|
tasks = [];
|
|
332
450
|
}
|
|
@@ -347,7 +465,7 @@ function setupCron(app, cronDir) {
|
|
|
347
465
|
}
|
|
348
466
|
}
|
|
349
467
|
tasks[idx] = { ...tasks[idx], ...updates, id: req.params.id };
|
|
350
|
-
|
|
468
|
+
fs2.writeFileSync(cronPath, JSON.stringify(tasks, null, 2));
|
|
351
469
|
loadCronJobs();
|
|
352
470
|
res.json(tasks[idx]);
|
|
353
471
|
});
|
|
@@ -355,14 +473,14 @@ function setupCron(app, cronDir) {
|
|
|
355
473
|
const cronPath = path.join(cronDir, "cron.json");
|
|
356
474
|
let tasks = [];
|
|
357
475
|
try {
|
|
358
|
-
if (
|
|
476
|
+
if (fs2.existsSync(cronPath)) tasks = JSON.parse(fs2.readFileSync(cronPath, "utf8"));
|
|
359
477
|
} catch {
|
|
360
478
|
tasks = [];
|
|
361
479
|
}
|
|
362
480
|
const idx = tasks.findIndex((t) => t.id === req.params.id);
|
|
363
481
|
if (idx === -1) return res.status(404).json({ error: "Task not found" });
|
|
364
482
|
tasks.splice(idx, 1);
|
|
365
|
-
|
|
483
|
+
fs2.writeFileSync(cronPath, JSON.stringify(tasks, null, 2));
|
|
366
484
|
cronState.delete(req.params.id);
|
|
367
485
|
loadCronJobs();
|
|
368
486
|
res.json({ ok: true });
|
package/package.json
CHANGED
package/dist/chunk-4NA3GKVD.js
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
-
}) : x)(function(x) {
|
|
4
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
// src/core/config.ts
|
|
9
|
-
var DEFAULT_API_BASE_URL = "https://api.ask.surf/gateway/v1";
|
|
10
|
-
function trimTrailingSlashes(value) {
|
|
11
|
-
return String(value || "").replace(/\/+$/, "");
|
|
12
|
-
}
|
|
13
|
-
function readSurfApiConfig() {
|
|
14
|
-
return {
|
|
15
|
-
baseUrl: trimTrailingSlashes(process.env.SURF_API_BASE_URL || DEFAULT_API_BASE_URL),
|
|
16
|
-
apiKey: process.env.SURF_API_KEY
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
function requireSurfApiConfig() {
|
|
20
|
-
const config = readSurfApiConfig();
|
|
21
|
-
if (!config.apiKey) {
|
|
22
|
-
throw new Error("SURF_API_KEY is required");
|
|
23
|
-
}
|
|
24
|
-
return { baseUrl: config.baseUrl, apiKey: config.apiKey };
|
|
25
|
-
}
|
|
26
|
-
function readAdminApiKey() {
|
|
27
|
-
return process.env.SURF_API_KEY;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// src/core/transport.ts
|
|
31
|
-
function sleep(ms) {
|
|
32
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
33
|
-
}
|
|
34
|
-
function normalizePath(path) {
|
|
35
|
-
return String(path || "").replace(/^\/+/, "");
|
|
36
|
-
}
|
|
37
|
-
function buildUrl(path, params) {
|
|
38
|
-
const { baseUrl } = requireSurfApiConfig();
|
|
39
|
-
const url = new URL(`${baseUrl}/${normalizePath(path)}`);
|
|
40
|
-
if (params) {
|
|
41
|
-
for (const [key, value] of Object.entries(params)) {
|
|
42
|
-
if (value != null) {
|
|
43
|
-
url.searchParams.set(key, String(value));
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
return url.toString();
|
|
48
|
-
}
|
|
49
|
-
function buildHeaders(extra) {
|
|
50
|
-
const { apiKey } = requireSurfApiConfig();
|
|
51
|
-
const headers = new Headers(extra);
|
|
52
|
-
headers.set("Authorization", `Bearer ${apiKey}`);
|
|
53
|
-
return headers;
|
|
54
|
-
}
|
|
55
|
-
async function fetchJson(url, init, retries = 1) {
|
|
56
|
-
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
57
|
-
const res = await fetch(url, init);
|
|
58
|
-
if (!res.ok) {
|
|
59
|
-
const text2 = await res.text();
|
|
60
|
-
throw new Error(`API error ${res.status}: ${text2.slice(0, 200)}`);
|
|
61
|
-
}
|
|
62
|
-
const text = await res.text();
|
|
63
|
-
if (text) {
|
|
64
|
-
return JSON.parse(text);
|
|
65
|
-
}
|
|
66
|
-
if (attempt < retries) {
|
|
67
|
-
await sleep(1e3);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
throw new Error(`Empty response from ${url}`);
|
|
71
|
-
}
|
|
72
|
-
async function getJson(path, params) {
|
|
73
|
-
return fetchJson(buildUrl(path, params), {
|
|
74
|
-
headers: buildHeaders()
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
async function postJson(path, body) {
|
|
78
|
-
return fetchJson(buildUrl(path), {
|
|
79
|
-
method: "POST",
|
|
80
|
-
headers: buildHeaders({
|
|
81
|
-
"Content-Type": "application/json"
|
|
82
|
-
}),
|
|
83
|
-
body: body ? JSON.stringify(body) : void 0
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// src/data/client.ts
|
|
88
|
-
async function get(path, params) {
|
|
89
|
-
return getJson(path, params);
|
|
90
|
-
}
|
|
91
|
-
async function post(path, body) {
|
|
92
|
-
return postJson(path, body);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export {
|
|
96
|
-
__require,
|
|
97
|
-
readAdminApiKey,
|
|
98
|
-
get,
|
|
99
|
-
post
|
|
100
|
-
};
|