godprotocol 2.2.94 → 2.2.95

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/Godprotocol.js CHANGED
@@ -1,3 +1,4 @@
1
+ import Resource from "./server/Resource.js";
1
2
  import Route_table from "./server/Routable/Route_table.js";
2
3
  import version_middleware from "./server/version_control.js";
3
4
 
@@ -5,12 +6,22 @@ class GodProtocol {
5
6
  constructor(config) {
6
7
  this.db_config = config.db_config;
7
8
 
9
+ this.resource = new Resource(this);
10
+
8
11
  this.static_path = config.static_path;
9
12
  this.api_key = config.api_key;
10
13
  this.platform_uri = this.uri_default_domain(config.platform_uri);
11
14
  this.route_table = new Route_table(this);
15
+
16
+ this.memory = new Map();
17
+
18
+ this.on_start();
12
19
  }
13
20
 
21
+ on_start = async (fn) => {
22
+ await fn(this);
23
+ };
24
+
14
25
  uri_default_domain = (uri) => {
15
26
  if (!uri.includes(".")) {
16
27
  uri = `${uri}.savvyaisolution.com`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "godprotocol",
3
- "version": "2.2.94",
3
+ "version": "2.2.95",
4
4
  "description": "A distributed computing environment for Web 4.0 — integrating AI, decentralisation, and virtual computation.",
5
5
  "main": "Index.js",
6
6
  "type": "module",
@@ -0,0 +1,44 @@
1
+ class Cache {
2
+ constructor(gp) {
3
+ this.gp = gp;
4
+ this.local = {};
5
+ }
6
+
7
+ set = async (key, value, opts = {}) => {
8
+ key = `${this.gp.platform_uri}:${key}`;
9
+
10
+ this.local[key] = value;
11
+
12
+ let Cache = await this.gp.route_table.get_service("cache");
13
+ if (!Cache) return;
14
+
15
+ await Cache.call("set", {
16
+ key,
17
+ value,
18
+ options: opts,
19
+ });
20
+ };
21
+
22
+ get = async (key, fill) => {
23
+ key = `${this.gp.platform_uri}:${key}`;
24
+
25
+ let value = this.local[key];
26
+ if (value) return value;
27
+
28
+ let Cache = await this.gp.route_table.get_service("cache");
29
+ if (!Cache) return;
30
+
31
+ value = await Cache.call("get", {
32
+ key,
33
+ fill,
34
+ });
35
+
36
+ if (value.ok) {
37
+ this.local[key] = value.data;
38
+
39
+ return value.data;
40
+ }
41
+ };
42
+ }
43
+
44
+ export default Cache;
@@ -0,0 +1,147 @@
1
+ import Cache from "./Cache.js";
2
+
3
+ class Resource {
4
+ constructor(gp) {
5
+ this.gp = gp;
6
+
7
+ this.cache = new Cache(this.gp);
8
+ }
9
+
10
+ hydrate_runtime = async () => {
11
+ this.runtime = await this.cache.get("runtime", {
12
+ active_requests: 0,
13
+
14
+ requests: new Map(),
15
+
16
+ handlers: {},
17
+ });
18
+
19
+ return this.runtime;
20
+ };
21
+
22
+ persist_runtime = async () => {
23
+ await this.cache.set("runtime", this.runtime, { ttl: 86400 });
24
+ };
25
+
26
+ incoming_request = async (req) => {
27
+ if (!(await this.hydrate_runtime())) return;
28
+
29
+ if (this.runtime.active_requests % 100 === 0) {
30
+ this.cleanup_handlers();
31
+ }
32
+
33
+ const route = `${req.method}:${req.url}`;
34
+
35
+ this.runtime.active_requests++;
36
+
37
+ this.runtime.requests.set(req.request_id, {
38
+ route,
39
+ started: performance.now(),
40
+ });
41
+
42
+ if (!this.runtime.handlers[route]) {
43
+ this.runtime.handlers[route] = {
44
+ active: 0,
45
+ completed: 0,
46
+
47
+ average_duration: 0,
48
+
49
+ max_duration: 0,
50
+ min_duration: Infinity,
51
+
52
+ total_duration: 0,
53
+
54
+ last_seen: Date.now(),
55
+ };
56
+ }
57
+
58
+ this.runtime.handlers[route].active++;
59
+
60
+ await this.persist_runtime();
61
+ };
62
+
63
+ compute_response = async (res) => {
64
+ if (!(await this.hydrate_runtime())) return;
65
+
66
+ const request = this.runtime.requests.get(res.request_id);
67
+
68
+ if (!request) return;
69
+
70
+ const handler = this.runtime.handlers[request.route];
71
+
72
+ if (!handler) {
73
+ this.runtime.requests.delete(res.request_id);
74
+ return;
75
+ }
76
+
77
+ const duration = performance.now() - request.started;
78
+
79
+ handler.completed++;
80
+
81
+ // Exponential Moving Average
82
+ if (handler.average_duration === 0) {
83
+ handler.average_duration = duration;
84
+ } else {
85
+ handler.average_duration =
86
+ handler.average_duration * 0.9 + duration * 0.1;
87
+ }
88
+
89
+ handler.total_duration += duration;
90
+
91
+ handler.max_duration = Math.max(handler.max_duration, duration);
92
+
93
+ handler.min_duration = Math.min(handler.min_duration, duration);
94
+
95
+ handler.last_seen = Date.now();
96
+
97
+ handler.active = Math.max(0, handler.active - 1);
98
+
99
+ this.runtime.active_requests = Math.max(
100
+ 0,
101
+ this.runtime.active_requests - 1,
102
+ );
103
+
104
+ this.runtime.requests.delete(res.request_id);
105
+
106
+ await this.persist_runtime();
107
+ };
108
+
109
+ cleanup_handlers = () => {
110
+ const now = Date.now();
111
+
112
+ const MAX_IDLE = 24 * 60 * 60 * 1000; // 24 hours
113
+
114
+ for (const [route, handler] of Object.entries(this.runtime.handlers)) {
115
+ if (handler.active > 0) continue;
116
+
117
+ // Keep handlers we've gathered meaningful statistics for.
118
+ if (handler.completed >= 100) continue;
119
+
120
+ if (now - handler.last_seen > MAX_IDLE) {
121
+ delete this.runtime.handlers[route];
122
+ }
123
+ }
124
+ };
125
+
126
+ get estimated_work() {
127
+ let total = 0;
128
+
129
+ for (const handler of Object.values(this.runtime.handlers)) {
130
+ total += handler.active * handler.average_duration;
131
+ }
132
+
133
+ return Math.round(total);
134
+ }
135
+
136
+ get server_load() {
137
+ return {
138
+ active_requests: this.runtime.active_requests,
139
+
140
+ estimated_work_ms: this.estimated_work,
141
+
142
+ handlers: this.runtime.handlers,
143
+ };
144
+ }
145
+ }
146
+
147
+ export default Resource;
@@ -119,12 +119,13 @@ class Headers extends Services {
119
119
  headers["authorization"] = `Bearer ${authorization}`;
120
120
  }
121
121
 
122
- let res = await fetch(`${gp_services.profile}/validate`, {
122
+ let gp_profile = gp_services().profile;
123
+ let res = await fetch(`${gp_profile.url}/validate`, {
123
124
  method: "post",
124
125
  headers: {
125
126
  "Content-Type": "application/json",
126
127
  Accept: "application/json",
127
- "x-api-version": "v3",
128
+ "x-api-version": gp_profile.api_version,
128
129
  ...headers,
129
130
  },
130
131
  body: JSON.stringify({}),
@@ -180,21 +181,23 @@ class Headers extends Services {
180
181
  hst = hst.replace("10.0.2.2", "localhost");
181
182
  }
182
183
 
184
+ let gp_profile = gp_services().profile;
185
+
183
186
  let third_party =
184
- xplatform !== this.platform_uri && !gp_services.profile.endsWith(hst);
187
+ xplatform !== this.platform_uri && !gp_profile.url.endsWith(hst);
185
188
 
186
- if (gp_services.profile.endsWith(hst)) {
189
+ if (gp_profile.url.endsWith(hst)) {
187
190
  headers["x-platform"] = xplatform;
188
191
  }
189
192
 
190
193
  let res = await fetch(
191
- `${gp_services.profile}/${third_party ? "third_party_me" : "me"}`,
194
+ `${gp_profile.url}/${third_party ? "third_party_me" : "me"}`,
192
195
  {
193
196
  method: "post",
194
197
  headers: {
195
198
  "Content-Type": "application/json",
196
199
  Accept: "application/json",
197
- "x-api-version": "v3",
200
+ "x-api-version": gp_profile.api_version,
198
201
  ...headers,
199
202
  },
200
203
  body: JSON.stringify(third_party ? { from: xplatform } : {}),
@@ -225,6 +228,8 @@ class Headers extends Services {
225
228
  };
226
229
 
227
230
  handle_security = async (name, request) => {
231
+ if (this.active_version === "*") return true;
232
+
228
233
  let route = await this.get_route(name);
229
234
 
230
235
  if (!route) {
@@ -250,11 +255,14 @@ class Headers extends Services {
250
255
 
251
256
  let val;
252
257
  if (route.config.security?.includes("auth_token")) {
253
- val = await this.validate_third_party(
254
- xplatform || this.platform_uri,
255
- authorisation,
256
- request,
257
- );
258
+ if (authorisation?.startsWith("p")) {
259
+ val = await this.validate_api_key(authorisation);
260
+ } else
261
+ val = await this.validate_third_party(
262
+ xplatform || this.platform_uri,
263
+ authorisation,
264
+ request,
265
+ );
258
266
  } else if (route.config.security?.includes("api_key")) {
259
267
  val = await this.validate_api_key(api_key, authorisation);
260
268
  }
@@ -42,6 +42,13 @@ class Route_table extends Headers {
42
42
  };
43
43
 
44
44
  load_routes = async (routes) => {
45
+ if (this.active_version === "*") {
46
+ this.versions[this.active_version] = {
47
+ handler: routes,
48
+ };
49
+ return;
50
+ }
51
+
45
52
  for (let name in routes) {
46
53
  let { handler, ...config } = routes[name];
47
54
 
@@ -181,6 +188,12 @@ class Route_table extends Headers {
181
188
  // 🚀 EXECUTION PIPELINE
182
189
  // =========================
183
190
  execute = async (name, payload) => {
191
+ if (this.active_version === "*") {
192
+ let version = this.versions[this.active_version];
193
+
194
+ return await version.handler(payload);
195
+ }
196
+
184
197
  let route = await this.get_route(name);
185
198
 
186
199
  if (!route) {
@@ -1,13 +1,31 @@
1
1
  import { DB } from "./Header.js";
2
2
 
3
- let DEV = process.env.DEV;
4
- let gp_services = {
5
- profile: DEV
6
- ? "http://localhost:4000"
7
- : "https://profile-api.savvyaisolution.com",
8
- settings: DEV
9
- ? "http://localhost:4005"
10
- : "https://settings-api.savvyaisolution.com",
3
+ let gp_services = () => {
4
+ let DEV = process.env.DEV;
5
+
6
+ return {
7
+ profile: {
8
+ url: DEV
9
+ ? "http://localhost:4000"
10
+ : "https://profile-api.savvyaisolution.com",
11
+ api_version: "v3",
12
+ uri: "profiles.savvyaisolution.com",
13
+ },
14
+ settings: {
15
+ url: DEV
16
+ ? "http://localhost:4005"
17
+ : "https://settings-api.savvyaisolution.com",
18
+ api_version: "v1",
19
+ uri: "settings.savvyaisolution.com",
20
+ },
21
+ godprotocol: {
22
+ url: DEV
23
+ ? "http://localhost:4001"
24
+ : "https://godprotocol-api.savvyaisolution.com",
25
+ api_version: "v1",
26
+ uri: "godprotocol.savvyaisolution.com",
27
+ },
28
+ };
11
29
  };
12
30
 
13
31
  export { gp_services };
@@ -15,11 +33,14 @@ export { gp_services };
15
33
  class Services {
16
34
  constructor() {
17
35
  this.services = new Object();
36
+
37
+ this.load_services(gp_services);
18
38
  }
19
39
 
20
40
  load_services = async (services) => {
21
41
  if (typeof services === "function") services = await services();
22
- this.services = services;
42
+
43
+ this.services = { ...this.services, ...services };
23
44
  };
24
45
 
25
46
  get_token = async (service, id) => {
@@ -37,12 +58,13 @@ class Services {
37
58
  try {
38
59
  // debug(this.api_key, gp_services.profile);
39
60
 
40
- let ftch = await fetch(`${gp_services.profile}/get_token`, {
61
+ let gp_profile = gp_services().profile;
62
+ let ftch = await fetch(`${gp_profile.url}/get_token`, {
41
63
  method: "POST",
42
64
  headers: {
43
65
  "Content-Type": "application/json",
44
66
  Accept: "application/json",
45
- "x-api-version": "v3",
67
+ "x-api-version": gp_profile.api_version,
46
68
  "x-api-key": this.api_key,
47
69
  },
48
70
  body: JSON.stringify({ platform_uri: service.uri, ...id }),
@@ -85,7 +107,7 @@ class Services {
85
107
  "x-api-version": servic.api_version || "v1",
86
108
  };
87
109
  let token;
88
- if (header) {
110
+ if (header?.token || header?.profile) {
89
111
  if (header.token) {
90
112
  token = header.token;
91
113
  } else if (header.profile) {
@@ -98,10 +120,20 @@ class Services {
98
120
 
99
121
  if (token) headers["Authorization"] = `Bearer ${token}`;
100
122
  headers["x-platform"] = this.platform_uri;
101
- } else {
102
- headers["x-api-key"] = servic.api_key || this.gp.api_key;
123
+ } else if (servic.api_key) {
124
+ headers["x-api-key"] = servic.api_key;
103
125
  }
104
126
 
127
+ if (
128
+ !headers["x-api-key"] &&
129
+ !headers["Authorization"] &&
130
+ !servic.opened
131
+ ) {
132
+ return {
133
+ ok: false,
134
+ meesage: "Service unauthorized",
135
+ };
136
+ }
105
137
  try {
106
138
  let response = await fetch(`${servic.url}/${path}`, {
107
139
  method: "POST",
@@ -40,6 +40,8 @@ const serveStaticFile = (req, res) => {
40
40
  res.setHeader("Content-Type", mimeTypes[ext] || "application/octet-stream");
41
41
 
42
42
  stream.pipe(res);
43
+
44
+ res.gp.compute_response(res);
43
45
  return true;
44
46
  } catch (err) {
45
47
  console.error("Static file error:", err);
@@ -1,5 +1,5 @@
1
- import parseMultipart from "godprotocol/server/parse_multipart_data.js";
2
- import serveStaticFile from "godprotocol/server/serve_static_file.js";
1
+ import parseMultipart from "./parse_multipart_data.js";
2
+ import serveStaticFile from "./serve_static_file.js";
3
3
 
4
4
  const getBody = (req) => {
5
5
  return new Promise((resolve, reject) => {
@@ -83,6 +83,8 @@ const respond = (result, res) => {
83
83
 
84
84
  res.end(JSON.stringify(response));
85
85
 
86
+ res.gp.compute_response(res);
87
+
86
88
  return response;
87
89
  };
88
90
 
@@ -100,6 +102,11 @@ const version_middleware = async (req, res, routers) => {
100
102
  const parsed_url = new URL(req.url, `http://${req.headers.host}`);
101
103
  const pathname = parsed_url.pathname;
102
104
 
105
+ req.request_id = crypto.randomUUID();
106
+ routers.gp.incoming_request(req);
107
+ res.gp = routers.gp;
108
+ res.request_id = req.request_id;
109
+
103
110
  // STATIC ROUTE DETECTION
104
111
  if (routers.static_path && pathname.startsWith(routers.static_path)) {
105
112
  return serveStaticFile(req, res);
@@ -121,17 +128,21 @@ const version_middleware = async (req, res, routers) => {
121
128
  req.headers["x-platform"],
122
129
  );
123
130
 
124
- const versionRouter = await routers.get_version(version);
131
+ let versionRouter = await routers.get_version(version);
125
132
 
126
133
  if (!versionRouter) {
127
- return respond(
128
- {
129
- ok: false,
130
- message: "Invalid API version",
131
- status: 400,
132
- },
133
- res,
134
- );
134
+ version = "*";
135
+ versionRouter = await routers.get_version(version);
136
+
137
+ if (!versionRouter)
138
+ return respond(
139
+ {
140
+ ok: false,
141
+ message: "Invalid API version",
142
+ status: 400,
143
+ },
144
+ res,
145
+ );
135
146
  }
136
147
 
137
148
  let body = {};
@@ -189,7 +200,7 @@ const version_middleware = async (req, res, routers) => {
189
200
  const db = await router.resolve_db(req);
190
201
 
191
202
  // WEBHOOK PRIOR
192
- if (!isWebhook) {
203
+ if (!isWebhook && version !== "*") {
193
204
  error_stage = "Callback";
194
205
  const webPrior = await router.handle_webhook_prior({
195
206
  ...req,
@@ -213,10 +224,11 @@ const version_middleware = async (req, res, routers) => {
213
224
  gp: router.gp,
214
225
  services: router.get_service,
215
226
  method: req.method,
227
+ memory: router.gp.memory,
216
228
  });
217
229
 
218
230
  // WEBHOOK AFTER
219
- if (!isWebhook) {
231
+ if (!isWebhook && version !== "*") {
220
232
  error_stage = "Webhook";
221
233
  await router.handle_webhook_after(req, result);
222
234
  }