993b94ac8ee4fa02dfb60bab89ddcc2d23ecc341
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

1) declare var setInterval: any;
2) 
3) 
4) type type_conf =
5) (
6) 	null
7) 	|
8) 	{
9) 		port: int;
10) 		verbosity: int;
11) 		cleaning:
12) 		{
13) 			timeout_in_seconds: int;
14) 			worker_interval_in_seconds: int;
15) 		};
16) 	}
17) );
18) 
19) 
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

20) type type_event =
21) {
Christian Fraß [mod] allow multiple connec...

Christian Fraß authored 2 years ago

22) 	timestamp: int;
23) 	kind: string;
24) 	data: any;
25) };
26) 
27) 
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

28) type type_user =
29) {
Christian Fraß [mod] allow multiple connec...

Christian Fraß authored 2 years ago

30) 	name: string;
31) 	role: string;
32) };
33) 
34) 
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

35) type type_connection =
36) {
Christian Fraß [mod] allow multiple connec...

Christian Fraß authored 2 years ago

37) 	client: any;
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

38) 	eventqueue: Array<type_event>;
39) 	termination: (null | int);
40) };
41) 
42) 
43) type type_id = string;
44) 
45) 
46) type type_model =
47) {
48) 	counter: int;
49) 	connections: Record<type_id, type_connection>;
Christian Fraß [mod] allow multiple connec...

Christian Fraß authored 2 years ago

50) };
51) 
52) 
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

53) type type_internal_request =
Christian Fraß [ini]

Christian Fraß authored 2 years ago

54) {
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

55) 	id: (null | type_id);
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

56) 	action: string;
57) 	data: any;
58) };
59) 
Christian Fraß [ini]

Christian Fraß authored 2 years ago

60) 
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

61) type type_internal_response = any;
Christian Fraß [mod] allow multiple connec...

Christian Fraß authored 2 years ago

62) 
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

63) 
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

64) /**
65)  * the node module "irc"
66)  */
67) var nm_irc: any;
68) 
69) 
70) /**
71)  * gets the current UNIX timestamp
72)  */
73) function get_timestamp
74) (
75) ): int
Christian Fraß [mod] allow multiple connec...

Christian Fraß authored 2 years ago

76) {
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

77) 	return Math.floor(Date.now()/1000);
Christian Fraß [mod] allow multiple connec...

Christian Fraß authored 2 years ago

78) }
79) 
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

80) 
81) /**
82)  * generates a unique 8 digit long string
83)  */
84) function generate_id
85) (
86) 	model: type_model
87) ): type_id
Christian Fraß [mod] allow multiple connec...

Christian Fraß authored 2 years ago

88) {
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

89) 	model.counter += 1;
90) 	return model.counter.toFixed(0).padStart(8, '0');
Christian Fraß [mod] allow multiple connec...

Christian Fraß authored 2 years ago

91) }
92) 
93) 
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

94) /**
95)  * writes a message to stderr
96)  */
97) function log
98) (
99) 	conf: type_conf,
100) 	level: int,
101) 	incident: string,
102) 	details: Record<string, any> = {}
103) ): void
104) {
105) 	if (level <= conf.verbosity)
106) 	{
107) 		process.stderr.write(`-- ${incident} | ${lib_json.encode(details)}\n`);
108) 	}
109) 	else
110) 	{
111) 		// do nothing
112) 	}
113) }
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

114) 
115) 
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

116) /**
117)  * updates the termination timestamp of a connection, prolonging its lifetime
118)  */
119) function connection_touch
120) (
121) 	conf: type_conf,
122) 	connection: type_connection
123) ): void
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

124) {
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

125) 	if (conf.cleaning.timeout_in_seconds !== null)
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

126) 	{
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

127) 		connection.termination = (get_timestamp() + conf.cleaning.timeout_in_seconds);
128) 	}
129) 	else
130) 	{
131) 		connection.termination = null;
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

132) 	}
133) }
Christian Fraß [mod] allow multiple connec...

Christian Fraß authored 2 years ago

134) 
135) 
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

136) /**
137)  * gets a connection by its ID
138)  */
139) function get_connection
140) (
141) 	conf: type_conf,
142) 	model: type_model,
143) 	id: type_id
144) ): type_connection
Christian Fraß [mod] allow multiple connec...

Christian Fraß authored 2 years ago

145) {
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

146) 	if (! model.connections.hasOwnProperty(id))
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

147) 	{
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

148) 		throw (new Error(`no connection for ID '${id}'`));
Christian Fraß [mod] allow multiple connec...

Christian Fraß authored 2 years ago

149) 	}
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

150) 	else
151) 	{
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

152) 		const connection: type_connection = model.connections[id];
153) 		connection_touch(conf, connection);
154) 		return connection;
Christian Fraß [mod] allow multiple connec...

Christian Fraß authored 2 years ago

155) 	}
156) }
157) 
158) 
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

159) /**
160)  * executes a request
161)  */
162) async function execute
163) (
164) 	conf: type_conf,
165) 	model: type_model,
166) 	internal_request: type_internal_request,
167) 	ip_address: string
168) ): Promise<type_internal_response>
Christian Fraß [ini]

Christian Fraß authored 2 years ago

169) {
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

170) 	switch (internal_request.action)
171) 	{
172) 		default:
173) 		{
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

174) 			throw (new Error(`unhandled action '${internal_request.action}'`));
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

175) 			break;
176) 		}
177) 		case "connect":
178) 		{
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

179) 			if (model.connections.hasOwnProperty(internal_request.id))
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

180) 			{
181) 				throw (new Error("already connected"));
182) 			}
183) 			else
184) 			{
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

185) 				const id: type_id = generate_id(model);
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

186) 				const client = new nm_irc.Client
187) 				(
188) 					internal_request.data["server"],
189) 					internal_request.data["nickname"],
190) 					{
Christian Fraß [mod] "say" -> "send"

Christian Fraß authored 2 years ago

191) 						"realName": "webirc",
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

192) 						"userName": lib_sha256.get(ip_address).slice(0, 8),
193) 						"channels": internal_request.data["channels"],
194) 						"showErrors": true,
195) 						"autoConnect": false,
Christian Fraß [mod] allow multiple connec...

Christian Fraß authored 2 years ago

196) 					}
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

197) 				);
Christian Fraß [mod] "say" -> "send"

Christian Fraß authored 2 years ago

198) 				let connection: type_connection =
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

199) 				{
200) 					"client": client,
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

201) 					"eventqueue": [],
202) 					"termination": null,
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

203) 				};
204) 				client.addListener
205) 				(
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

206) 					"message#",
207) 					(nick, to, text, message) =>
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

208) 					{
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

209) 						connection.eventqueue.push
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

210) 						({
211) 							"timestamp": get_timestamp(),
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

212) 							"kind": "message_channel",
213) 							"data": {"channel": to, "sender": nick, "content": text}
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

214) 						});
Christian Fraß [mod] allow multiple connec...

Christian Fraß authored 2 years ago

215) 					}
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

216) 				);
217) 				client.addListener
218) 				(
219) 					"pm",
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

220) 					(nick, text, message) =>
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

221) 					{
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

222) 						connection.eventqueue.push
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

223) 						({
224) 							"timestamp": get_timestamp(),
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

225) 							"kind": "message_query",
226) 							"data": {"user_name": nick, "sender": nick, "content": text}
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

227) 						});
Christian Fraß [mod] allow multiple connec...

Christian Fraß authored 2 years ago

228) 					}
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

229) 				);
230) 				client.addListener
231) 				(
232) 					"names",
233) 					(channel, users) =>
234) 					{
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

235) 						
236) 						connection.eventqueue.push
237) 						({
238) 							"timestamp": get_timestamp(),
239) 							"kind": "userlist",
240) 							"data": {"channel": channel, "users": Object.entries(users).map(([name, role]) => ({"name": name, "role": role}))}
241) 						});
Christian Fraß [mod] allow multiple connec...

Christian Fraß authored 2 years ago

242) 					}
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

243) 				);
244) 				client.addListener
245) 				(
246) 					"error",
247) 					(error) =>
248) 					{
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

249) 						log(conf, 0, "irc_error", {"reason": error.message});
Christian Fraß [mod] allow multiple connec...

Christian Fraß authored 2 years ago

250) 					}
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

251) 				);
252) 				client.connect
253) 				(
254) 					3,
255) 					() =>
256) 					{
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

257) 						model.connections[id] = connection;
258) 						connection_touch(conf, connection);
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

259) 					}
260) 				);
261) 				return Promise.resolve<type_internal_response>(id);
262) 			}
263) 			break;
264) 		}
265) 		case "check":
266) 		{
267) 			try
268) 			{
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

269) 				get_connection(conf, model, internal_request.id);
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

270) 				return Promise.resolve<type_internal_response>(true);
271) 			}
272) 			catch (error)
273) 			{
274) 				return Promise.resolve<type_internal_response>(false);
275) 			}
276) 			break;
277) 		}
278) 		case "disconnect":
279) 		{
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

280) 			const connection: type_connection = get_connection(conf, model, internal_request.id);
281) 			delete model.connections[internal_request.id];
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

282) 			connection.client.disconnect("", () => {});
283) 			return Promise.resolve<type_internal_response>(null);
284) 			break;
285) 		}
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

286) 		case "send_channel":
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

287) 		{
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

288) 			const connection: type_connection = get_connection(conf, model, internal_request.id);
289) 			connection.client.say(internal_request.data["channel"], internal_request.data["content"]);
290) 			return Promise.resolve<type_internal_response>(null);
291) 			break;
292) 		}
293) 		case "send_query":
294) 		{
295) 			const connection: type_connection = get_connection(conf, model, internal_request.id);
296) 			connection.client.say(internal_request.data["receiver"], internal_request.data["content"]);
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

297) 			return Promise.resolve<type_internal_response>(null);
298) 			break;
299) 		}
300) 		case "fetch":
301) 		{
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

302) 			const connection: type_connection = get_connection(conf, model, internal_request.id);
303) 			const internal_response: type_internal_response = connection.eventqueue;
304) 			connection.eventqueue = [];
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

305) 			return Promise.resolve<type_internal_response>(internal_response);
306) 			break;
307) 		}
308) 	}
309) }
310) 
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

311) 
312) /**
313)  * sets up the worker for terminating old connections
314)  */
315) function setup_cleaner
316) (
317) 	conf: type_conf,
318) 	model: type_model
319) ): Promise<void>
320) {
321) 	setInterval
322) 	(
323) 		() =>
324) 		{
325) 			const now: int = get_timestamp();
326) 			for (const [id, connection] of Object.entries(model.connections))
327) 			{
328) 				if ((connection.termination !== null) && (now > connection.termination))
329) 				{
330) 					delete model.connections[id];
331) 					connection.client.disconnect("timeout", () => {});
332) 					log(conf, 1, "connection terminated after timeout", {"id": id});
333) 				}
334) 			}
335) 		},
336) 		(conf.cleaning.worker_interval_in_seconds * 1000)
337) 	);
338) 	return Promise.resolve<void>(undefined);
339) }
340) 
341) 
342) /**
343)  * sets up the server, accepting HTTP request
344)  */
345) function setup_server
346) (
347) 	conf: type_conf,
348) 	model: type_model
349) ): Promise<void>
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

350) {
351) 	const server: lib_server.class_server = new lib_server.class_server
352) 	(
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

353) 		conf.port,
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

354) 		async (input: string, metadata?: lib_server.type_metadata): Promise<string> =>
355) 		{
356) 			const http_request: lib_http.type_request = lib_http.decode_request(input);
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

357) 			log(conf, 2, "http_request", http_request);
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

358) 			const internal_request: type_internal_request = lib_json.decode(http_request.body);
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

359) 			log(conf, 1, "internal_request", internal_request);
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

360) 			let internal_response: type_internal_response;
361) 			let error: (null | Error);
362) 			try
363) 			{
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

364) 				internal_response = await execute(conf, model, internal_request, metadata.ip_address);
Christian Fraß [ini]

Christian Fraß authored 2 years ago

365) 				error = null;
366) 			}
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

367) 			catch (error_)
368) 			{
369) 				internal_response = null;
Christian Fraß [ini]

Christian Fraß authored 2 years ago

370) 				error = error_;
371) 			}
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

372) 			let http_response: lib_http.type_response;
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

373) 			if (error !== null)
374) 			{
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

375) 				log(conf, 0, "error_in_execution", {"reason": error.toString()});
376) 				http_response =
377) 				{
Christian Fraß [ini]

Christian Fraß authored 2 years ago

378) 					"statuscode": 500,
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

379) 					"headers": {"Access-Control-Allow-Origin": "*", "Content-Type": "text/plain"},
380) 					"body": "error executing the request; check the server logs for details",
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

381) 				};
382) 			}
383) 			else
384) 			{
385) 				log(conf, 1, "internal_response", {"value": internal_response});
386) 				http_response =
387) 				{
Christian Fraß [ini]

Christian Fraß authored 2 years ago

388) 					"statuscode": 200,
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

389) 					"headers": {"Access-Control-Allow-Origin": "*", "Content-Type": "application/json"},
390) 					"body": lib_json.encode(internal_response)
Christian Fraß [ini]

Christian Fraß authored 2 years ago

391) 				}
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

392) 			}
393) 			log(conf, 2, "http_response", http_response);
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

394) 			const output: string = lib_http.encode_response(http_response);
Christian Fraß [ini]

Christian Fraß authored 2 years ago

395) 			return Promise.resolve<string>(output);
396) 		}
397) 	);
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

398) 	server.start();
399) 	return Promise.resolve<void>(undefined);
400) }
401) 
402) 
403) /**
404)  * initializes and starts the whole system
405)  */
406) async function main
407) (
408) ): Promise<void>
409) {
410) 	nm_irc = require("irc");
411) 	const conf: type_conf = await lib_plankton.file.read("conf.json").then<type_conf>(lib_json.decode);
412) 	let model: type_model =
413) 	{
414) 		"counter": 0,
415) 		"connections": {},
416) 	};
417) 	await Promise.all([
418) 		setup_cleaner(conf, model),
419) 		setup_server(conf, model),
420) 	]);
421) 	return Promise.resolve<void>(undefined);
Christian Fraß [ini]

Christian Fraß authored 2 years ago

422) }
423) 
Christian Fraß [mod] logging [mod] code st...

Christian Fraß authored 2 years ago

424) 
Christian Fraß [mod] stateless

Christian Fraß authored 2 years ago

425) main().then(() => {}).catch((reason) => process.stderr.write(reason.toString()));