Initial project structure and beta backend, with half finished frontend
This commit is contained in:
		
							parent
							
								
									acacaece0f
								
							
						
					
					
						commit
						29fa54ce71
					
				
					 10 changed files with 1069 additions and 0 deletions
				
			
		
							
								
								
									
										66
									
								
								index.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								index.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,66 @@
 | 
				
			||||||
 | 
					const express = require('express');
 | 
				
			||||||
 | 
					const { createServer } = require('http');
 | 
				
			||||||
 | 
					const WebSocket = require('ws');
 | 
				
			||||||
 | 
					const path = require('path');
 | 
				
			||||||
 | 
					const MessageRouter = require('./lib/messageRouter');
 | 
				
			||||||
 | 
					const ConnectionManager = require('./lib/connectionManager');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const app = express();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					app.use(express.static(path.join(__dirname, 'public')));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const server = createServer(app);
 | 
				
			||||||
 | 
					const wss = new WebSocket.Server({ server });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const connectionManager = new ConnectionManager();
 | 
				
			||||||
 | 
					const messageRouter = new MessageRouter(connectionManager);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					wss.on('connection', (ws, req) => {
 | 
				
			||||||
 | 
					  const query = req.url?.split('?')[1];
 | 
				
			||||||
 | 
					  const urlParams = new URLSearchParams(query);
 | 
				
			||||||
 | 
					  const username = urlParams.get('u');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!username) {
 | 
				
			||||||
 | 
					    ws.send(JSON.stringify({
 | 
				
			||||||
 | 
					      error: true,
 | 
				
			||||||
 | 
					      message: "Use /?u=... to set a username"
 | 
				
			||||||
 | 
					    }));
 | 
				
			||||||
 | 
					    ws.close();
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!connectionManager.addUser(username, ws)) {
 | 
				
			||||||
 | 
					    ws.send(JSON.stringify({
 | 
				
			||||||
 | 
					      error: true,
 | 
				
			||||||
 | 
					      message: "This user is currently online, pick another username"
 | 
				
			||||||
 | 
					    }));
 | 
				
			||||||
 | 
					    ws.close();
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  console.log(`[Connection] User connected: ${username}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ws.on('message', (data) => {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const parsedData = JSON.parse(data.toString());
 | 
				
			||||||
 | 
					      messageRouter.routeMessage(username, parsedData);
 | 
				
			||||||
 | 
					    } catch (err) {
 | 
				
			||||||
 | 
					      const errorMsg = {
 | 
				
			||||||
 | 
					        error: true,
 | 
				
			||||||
 | 
					        message: `Invalid JSON format: ${err.message}`
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					      ws.send(JSON.stringify(errorMsg));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ws.on('close', () => {
 | 
				
			||||||
 | 
					    connectionManager.removeUser(username);
 | 
				
			||||||
 | 
					    console.log(`[Connection] User disconnected: ${username}`);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ws.send(JSON.stringify({ error: false, message: `Connected as ${username}` }));
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					server.listen(65010, 'localhost', () => {
 | 
				
			||||||
 | 
					  console.log(`[callback] started`);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										44
									
								
								lib/connectionManager.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								lib/connectionManager.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,44 @@
 | 
				
			||||||
 | 
					class ConnectionManager {
 | 
				
			||||||
 | 
					  constructor() {
 | 
				
			||||||
 | 
					    this.users = new Map();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Adds a new user to the active connections.
 | 
				
			||||||
 | 
					   * Returns false if the user already exists.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  addUser(username, ws) {
 | 
				
			||||||
 | 
					    if (this.users.has(username)) {
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this.users.set(username, ws);
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Remove a user by username.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  removeUser(username) {
 | 
				
			||||||
 | 
					    this.users.delete(username);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Get the socket connection for a given username.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  getUserSocket(username) {
 | 
				
			||||||
 | 
					    return this.users.get(username);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Broadcasts a message to all connected users except the supplied username.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  broadcast(message, excludeUser = null) {
 | 
				
			||||||
 | 
					    for (const [username, ws] of this.users.entries()) {
 | 
				
			||||||
 | 
					      if (username !== excludeUser && ws.readyState === ws.OPEN) {
 | 
				
			||||||
 | 
					        ws.send(JSON.stringify(message));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = ConnectionManager;
 | 
				
			||||||
							
								
								
									
										78
									
								
								lib/messageRouter.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								lib/messageRouter.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,78 @@
 | 
				
			||||||
 | 
					class MessageRouter {
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * @param {ConnectionManager} connectionManager An instance of ConnectionManager.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  constructor(connectionManager) {
 | 
				
			||||||
 | 
					    this.connectionManager = connectionManager;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Routes the incoming message based on its content.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * The expected JSON structure:
 | 
				
			||||||
 | 
					   * {
 | 
				
			||||||
 | 
					   *   type: "private" | "broadcast" | "other",
 | 
				
			||||||
 | 
					   *   payload: { 
 | 
				
			||||||
 | 
					   *     u: "recipient username",   // for private messages
 | 
				
			||||||
 | 
					   *     body: "...",               // message body
 | 
				
			||||||
 | 
					   *     // additional fields for other types can be added too
 | 
				
			||||||
 | 
					   *   }
 | 
				
			||||||
 | 
					   * }
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * If 'type' is omitted, it defaults to "private".
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  routeMessage(fromUser, data) {
 | 
				
			||||||
 | 
					    const type = data.type || 'private';
 | 
				
			||||||
 | 
					    switch (type) {
 | 
				
			||||||
 | 
					      case 'private':
 | 
				
			||||||
 | 
					        this.handlePrivateMessage(fromUser, data.payload);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      case 'broadcast':
 | 
				
			||||||
 | 
					        this.handleBroadcastMessage(fromUser, data.payload);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      default:
 | 
				
			||||||
 | 
					        this.sendError(fromUser, `Unknown message type: ${type}`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handlePrivateMessage(fromUser, payload) {
 | 
				
			||||||
 | 
					    if (!payload || !payload.u || !payload.body) {
 | 
				
			||||||
 | 
					      return this.sendError(fromUser, "Invalid payload for a private message. Required: { u, body }");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const recipientSocket = this.connectionManager.getUserSocket(payload.u);
 | 
				
			||||||
 | 
					    if (recipientSocket && recipientSocket.readyState === recipientSocket.OPEN) {
 | 
				
			||||||
 | 
					      recipientSocket.send(JSON.stringify({
 | 
				
			||||||
 | 
					        error: false,
 | 
				
			||||||
 | 
					        message: {
 | 
				
			||||||
 | 
					          from: fromUser,
 | 
				
			||||||
 | 
					          body: payload.body
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }));
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      this.sendError(fromUser, `User ${payload.u} is not online`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleBroadcastMessage(fromUser, payload) {
 | 
				
			||||||
 | 
					    if (!payload || !payload.body) {
 | 
				
			||||||
 | 
					      return this.sendError(fromUser, "Invalid payload for broadcast message. Required: { body }");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const msg = {
 | 
				
			||||||
 | 
					      error: false,
 | 
				
			||||||
 | 
					      message: {
 | 
				
			||||||
 | 
					        from: fromUser,
 | 
				
			||||||
 | 
					        body: payload.body
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    this.connectionManager.broadcast(msg, fromUser);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  sendError(toUser, errorMessage) {
 | 
				
			||||||
 | 
					    const ws = this.connectionManager.getUserSocket(toUser);
 | 
				
			||||||
 | 
					    if (ws && ws.readyState === ws.OPEN) {
 | 
				
			||||||
 | 
					      ws.send(JSON.stringify({ error: true, message: errorMessage }));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = MessageRouter;
 | 
				
			||||||
							
								
								
									
										6
									
								
								package.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								package.json
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						"dependencies": {
 | 
				
			||||||
 | 
							"express": "^4.21.2",
 | 
				
			||||||
 | 
							"ws": "^8.18.1"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										602
									
								
								pnpm-lock.yaml
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										602
									
								
								pnpm-lock.yaml
									
										
									
										generated
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,602 @@
 | 
				
			||||||
 | 
					lockfileVersion: '9.0'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					settings:
 | 
				
			||||||
 | 
					  autoInstallPeers: true
 | 
				
			||||||
 | 
					  excludeLinksFromLockfile: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					importers:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      express:
 | 
				
			||||||
 | 
					        specifier: ^4.21.2
 | 
				
			||||||
 | 
					        version: 4.21.2
 | 
				
			||||||
 | 
					      ws:
 | 
				
			||||||
 | 
					        specifier: ^8.18.1
 | 
				
			||||||
 | 
					        version: 8.18.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					packages:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  accepts@1.3.8:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.6'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  array-flatten@1.1.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  body-parser@1.20.3:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bytes@3.1.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.8'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  call-bind-apply-helpers@1.0.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.4'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  call-bound@1.0.4:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.4'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  content-disposition@0.5.4:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.6'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  content-type@1.0.5:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.6'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  cookie-signature@1.0.6:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  cookie@0.7.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.6'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  debug@2.6.9:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
 | 
				
			||||||
 | 
					    peerDependencies:
 | 
				
			||||||
 | 
					      supports-color: '*'
 | 
				
			||||||
 | 
					    peerDependenciesMeta:
 | 
				
			||||||
 | 
					      supports-color:
 | 
				
			||||||
 | 
					        optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  depd@2.0.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.8'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  destroy@1.2.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  dunder-proto@1.0.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.4'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ee-first@1.1.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  encodeurl@1.0.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.8'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  encodeurl@2.0.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.8'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  es-define-property@1.0.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.4'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  es-errors@1.3.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.4'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  es-object-atoms@1.1.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.4'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  escape-html@1.0.3:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  etag@1.8.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.6'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  express@4.21.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.10.0'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  finalhandler@1.3.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.8'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  forwarded@0.2.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.6'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  fresh@0.5.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.6'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function-bind@1.1.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get-intrinsic@1.3.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.4'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get-proto@1.0.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.4'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  gopd@1.2.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.4'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  has-symbols@1.1.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.4'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  hasown@2.0.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.4'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  http-errors@2.0.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.8'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  iconv-lite@0.4.24:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
 | 
				
			||||||
 | 
					    engines: {node: '>=0.10.0'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  inherits@2.0.4:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ipaddr.js@1.9.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.10'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  math-intrinsics@1.1.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.4'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  media-typer@0.3.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.6'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  merge-descriptors@1.0.3:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  methods@1.1.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.6'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mime-db@1.52.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.6'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mime-types@2.1.35:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.6'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mime@1.6.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
 | 
				
			||||||
 | 
					    engines: {node: '>=4'}
 | 
				
			||||||
 | 
					    hasBin: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ms@2.0.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ms@2.1.3:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  negotiator@0.6.3:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.6'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  object-inspect@1.13.4:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.4'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  on-finished@2.4.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.8'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  parseurl@1.3.3:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.8'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  path-to-regexp@0.1.12:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  proxy-addr@2.0.7:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.10'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  qs@6.13.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==}
 | 
				
			||||||
 | 
					    engines: {node: '>=0.6'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  range-parser@1.2.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.6'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  raw-body@2.5.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.8'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  safe-buffer@5.2.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  safer-buffer@2.1.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  send@0.19.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.8.0'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  serve-static@1.16.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.8.0'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setprototypeof@1.2.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  side-channel-list@1.0.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.4'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  side-channel-map@1.0.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.4'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  side-channel-weakmap@1.0.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.4'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  side-channel@1.1.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.4'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  statuses@2.0.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.8'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  toidentifier@1.0.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
 | 
				
			||||||
 | 
					    engines: {node: '>=0.6'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  type-is@1.6.18:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.6'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  unpipe@1.0.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.8'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  utils-merge@1.0.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.4.0'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  vary@1.1.2:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
 | 
				
			||||||
 | 
					    engines: {node: '>= 0.8'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ws@8.18.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==}
 | 
				
			||||||
 | 
					    engines: {node: '>=10.0.0'}
 | 
				
			||||||
 | 
					    peerDependencies:
 | 
				
			||||||
 | 
					      bufferutil: ^4.0.1
 | 
				
			||||||
 | 
					      utf-8-validate: '>=5.0.2'
 | 
				
			||||||
 | 
					    peerDependenciesMeta:
 | 
				
			||||||
 | 
					      bufferutil:
 | 
				
			||||||
 | 
					        optional: true
 | 
				
			||||||
 | 
					      utf-8-validate:
 | 
				
			||||||
 | 
					        optional: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					snapshots:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  accepts@1.3.8:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      mime-types: 2.1.35
 | 
				
			||||||
 | 
					      negotiator: 0.6.3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  array-flatten@1.1.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  body-parser@1.20.3:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      bytes: 3.1.2
 | 
				
			||||||
 | 
					      content-type: 1.0.5
 | 
				
			||||||
 | 
					      debug: 2.6.9
 | 
				
			||||||
 | 
					      depd: 2.0.0
 | 
				
			||||||
 | 
					      destroy: 1.2.0
 | 
				
			||||||
 | 
					      http-errors: 2.0.0
 | 
				
			||||||
 | 
					      iconv-lite: 0.4.24
 | 
				
			||||||
 | 
					      on-finished: 2.4.1
 | 
				
			||||||
 | 
					      qs: 6.13.0
 | 
				
			||||||
 | 
					      raw-body: 2.5.2
 | 
				
			||||||
 | 
					      type-is: 1.6.18
 | 
				
			||||||
 | 
					      unpipe: 1.0.0
 | 
				
			||||||
 | 
					    transitivePeerDependencies:
 | 
				
			||||||
 | 
					      - supports-color
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bytes@3.1.2: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  call-bind-apply-helpers@1.0.2:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      es-errors: 1.3.0
 | 
				
			||||||
 | 
					      function-bind: 1.1.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  call-bound@1.0.4:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      call-bind-apply-helpers: 1.0.2
 | 
				
			||||||
 | 
					      get-intrinsic: 1.3.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  content-disposition@0.5.4:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      safe-buffer: 5.2.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  content-type@1.0.5: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  cookie-signature@1.0.6: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  cookie@0.7.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  debug@2.6.9:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      ms: 2.0.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  depd@2.0.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  destroy@1.2.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  dunder-proto@1.0.1:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      call-bind-apply-helpers: 1.0.2
 | 
				
			||||||
 | 
					      es-errors: 1.3.0
 | 
				
			||||||
 | 
					      gopd: 1.2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ee-first@1.1.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  encodeurl@1.0.2: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  encodeurl@2.0.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  es-define-property@1.0.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  es-errors@1.3.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  es-object-atoms@1.1.1:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      es-errors: 1.3.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  escape-html@1.0.3: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  etag@1.8.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  express@4.21.2:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      accepts: 1.3.8
 | 
				
			||||||
 | 
					      array-flatten: 1.1.1
 | 
				
			||||||
 | 
					      body-parser: 1.20.3
 | 
				
			||||||
 | 
					      content-disposition: 0.5.4
 | 
				
			||||||
 | 
					      content-type: 1.0.5
 | 
				
			||||||
 | 
					      cookie: 0.7.1
 | 
				
			||||||
 | 
					      cookie-signature: 1.0.6
 | 
				
			||||||
 | 
					      debug: 2.6.9
 | 
				
			||||||
 | 
					      depd: 2.0.0
 | 
				
			||||||
 | 
					      encodeurl: 2.0.0
 | 
				
			||||||
 | 
					      escape-html: 1.0.3
 | 
				
			||||||
 | 
					      etag: 1.8.1
 | 
				
			||||||
 | 
					      finalhandler: 1.3.1
 | 
				
			||||||
 | 
					      fresh: 0.5.2
 | 
				
			||||||
 | 
					      http-errors: 2.0.0
 | 
				
			||||||
 | 
					      merge-descriptors: 1.0.3
 | 
				
			||||||
 | 
					      methods: 1.1.2
 | 
				
			||||||
 | 
					      on-finished: 2.4.1
 | 
				
			||||||
 | 
					      parseurl: 1.3.3
 | 
				
			||||||
 | 
					      path-to-regexp: 0.1.12
 | 
				
			||||||
 | 
					      proxy-addr: 2.0.7
 | 
				
			||||||
 | 
					      qs: 6.13.0
 | 
				
			||||||
 | 
					      range-parser: 1.2.1
 | 
				
			||||||
 | 
					      safe-buffer: 5.2.1
 | 
				
			||||||
 | 
					      send: 0.19.0
 | 
				
			||||||
 | 
					      serve-static: 1.16.2
 | 
				
			||||||
 | 
					      setprototypeof: 1.2.0
 | 
				
			||||||
 | 
					      statuses: 2.0.1
 | 
				
			||||||
 | 
					      type-is: 1.6.18
 | 
				
			||||||
 | 
					      utils-merge: 1.0.1
 | 
				
			||||||
 | 
					      vary: 1.1.2
 | 
				
			||||||
 | 
					    transitivePeerDependencies:
 | 
				
			||||||
 | 
					      - supports-color
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  finalhandler@1.3.1:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      debug: 2.6.9
 | 
				
			||||||
 | 
					      encodeurl: 2.0.0
 | 
				
			||||||
 | 
					      escape-html: 1.0.3
 | 
				
			||||||
 | 
					      on-finished: 2.4.1
 | 
				
			||||||
 | 
					      parseurl: 1.3.3
 | 
				
			||||||
 | 
					      statuses: 2.0.1
 | 
				
			||||||
 | 
					      unpipe: 1.0.0
 | 
				
			||||||
 | 
					    transitivePeerDependencies:
 | 
				
			||||||
 | 
					      - supports-color
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  forwarded@0.2.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  fresh@0.5.2: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function-bind@1.1.2: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get-intrinsic@1.3.0:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      call-bind-apply-helpers: 1.0.2
 | 
				
			||||||
 | 
					      es-define-property: 1.0.1
 | 
				
			||||||
 | 
					      es-errors: 1.3.0
 | 
				
			||||||
 | 
					      es-object-atoms: 1.1.1
 | 
				
			||||||
 | 
					      function-bind: 1.1.2
 | 
				
			||||||
 | 
					      get-proto: 1.0.1
 | 
				
			||||||
 | 
					      gopd: 1.2.0
 | 
				
			||||||
 | 
					      has-symbols: 1.1.0
 | 
				
			||||||
 | 
					      hasown: 2.0.2
 | 
				
			||||||
 | 
					      math-intrinsics: 1.1.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get-proto@1.0.1:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      dunder-proto: 1.0.1
 | 
				
			||||||
 | 
					      es-object-atoms: 1.1.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  gopd@1.2.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  has-symbols@1.1.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  hasown@2.0.2:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      function-bind: 1.1.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  http-errors@2.0.0:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      depd: 2.0.0
 | 
				
			||||||
 | 
					      inherits: 2.0.4
 | 
				
			||||||
 | 
					      setprototypeof: 1.2.0
 | 
				
			||||||
 | 
					      statuses: 2.0.1
 | 
				
			||||||
 | 
					      toidentifier: 1.0.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  iconv-lite@0.4.24:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      safer-buffer: 2.1.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  inherits@2.0.4: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ipaddr.js@1.9.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  math-intrinsics@1.1.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  media-typer@0.3.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  merge-descriptors@1.0.3: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  methods@1.1.2: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mime-db@1.52.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mime-types@2.1.35:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      mime-db: 1.52.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mime@1.6.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ms@2.0.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ms@2.1.3: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  negotiator@0.6.3: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  object-inspect@1.13.4: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  on-finished@2.4.1:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      ee-first: 1.1.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  parseurl@1.3.3: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  path-to-regexp@0.1.12: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  proxy-addr@2.0.7:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      forwarded: 0.2.0
 | 
				
			||||||
 | 
					      ipaddr.js: 1.9.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  qs@6.13.0:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      side-channel: 1.1.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  range-parser@1.2.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  raw-body@2.5.2:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      bytes: 3.1.2
 | 
				
			||||||
 | 
					      http-errors: 2.0.0
 | 
				
			||||||
 | 
					      iconv-lite: 0.4.24
 | 
				
			||||||
 | 
					      unpipe: 1.0.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  safe-buffer@5.2.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  safer-buffer@2.1.2: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  send@0.19.0:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      debug: 2.6.9
 | 
				
			||||||
 | 
					      depd: 2.0.0
 | 
				
			||||||
 | 
					      destroy: 1.2.0
 | 
				
			||||||
 | 
					      encodeurl: 1.0.2
 | 
				
			||||||
 | 
					      escape-html: 1.0.3
 | 
				
			||||||
 | 
					      etag: 1.8.1
 | 
				
			||||||
 | 
					      fresh: 0.5.2
 | 
				
			||||||
 | 
					      http-errors: 2.0.0
 | 
				
			||||||
 | 
					      mime: 1.6.0
 | 
				
			||||||
 | 
					      ms: 2.1.3
 | 
				
			||||||
 | 
					      on-finished: 2.4.1
 | 
				
			||||||
 | 
					      range-parser: 1.2.1
 | 
				
			||||||
 | 
					      statuses: 2.0.1
 | 
				
			||||||
 | 
					    transitivePeerDependencies:
 | 
				
			||||||
 | 
					      - supports-color
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  serve-static@1.16.2:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      encodeurl: 2.0.0
 | 
				
			||||||
 | 
					      escape-html: 1.0.3
 | 
				
			||||||
 | 
					      parseurl: 1.3.3
 | 
				
			||||||
 | 
					      send: 0.19.0
 | 
				
			||||||
 | 
					    transitivePeerDependencies:
 | 
				
			||||||
 | 
					      - supports-color
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setprototypeof@1.2.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  side-channel-list@1.0.0:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      es-errors: 1.3.0
 | 
				
			||||||
 | 
					      object-inspect: 1.13.4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  side-channel-map@1.0.1:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      call-bound: 1.0.4
 | 
				
			||||||
 | 
					      es-errors: 1.3.0
 | 
				
			||||||
 | 
					      get-intrinsic: 1.3.0
 | 
				
			||||||
 | 
					      object-inspect: 1.13.4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  side-channel-weakmap@1.0.2:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      call-bound: 1.0.4
 | 
				
			||||||
 | 
					      es-errors: 1.3.0
 | 
				
			||||||
 | 
					      get-intrinsic: 1.3.0
 | 
				
			||||||
 | 
					      object-inspect: 1.13.4
 | 
				
			||||||
 | 
					      side-channel-map: 1.0.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  side-channel@1.1.0:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      es-errors: 1.3.0
 | 
				
			||||||
 | 
					      object-inspect: 1.13.4
 | 
				
			||||||
 | 
					      side-channel-list: 1.0.0
 | 
				
			||||||
 | 
					      side-channel-map: 1.0.1
 | 
				
			||||||
 | 
					      side-channel-weakmap: 1.0.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  statuses@2.0.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  toidentifier@1.0.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  type-is@1.6.18:
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      media-typer: 0.3.0
 | 
				
			||||||
 | 
					      mime-types: 2.1.35
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  unpipe@1.0.0: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  utils-merge@1.0.1: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  vary@1.1.2: {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ws@8.18.1: {}
 | 
				
			||||||
							
								
								
									
										183
									
								
								public/components.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								public/components.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,183 @@
 | 
				
			||||||
 | 
					nr.defineComponent({
 | 
				
			||||||
 | 
					  name: "login",
 | 
				
			||||||
 | 
					  template: `
 | 
				
			||||||
 | 
					    <div style="height: 100vh; width: 100vw;" class="d-flex justify-content-center align-items-center">
 | 
				
			||||||
 | 
					      <div id="login" class="card" style="max-width: 30rem; flex: 1 1 auto;">
 | 
				
			||||||
 | 
					        <div class="header w-100 d-flex justify-content-center">
 | 
				
			||||||
 | 
					          <img src="/favicon.png" style="width: 3rem;">
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="header">Welcome to <code>callback</code>.</div>
 | 
				
			||||||
 | 
					        <div class="body">
 | 
				
			||||||
 | 
					          <div class="fill-error alert m-0 m-top-2" style="display: none;">
 | 
				
			||||||
 | 
					            error
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div class="textfield m-0 m-bottom-2 m-top-2">
 | 
				
			||||||
 | 
					            <span class="label">Username</span>
 | 
				
			||||||
 | 
					            <input type="text" placeholder="">
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div class="fill-primary linear-progress indeterminate m-bottom-2" style="display: none;"><div></div></div>
 | 
				
			||||||
 | 
					          <button class="small w-100 fill-primary btn" type="submit">Login</button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  `,
 | 
				
			||||||
 | 
					  beforeCreate: () => {
 | 
				
			||||||
 | 
					    return { useShadowRoot: false }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  afterCreate: (shadowRoot, config = {}, env) => {
 | 
				
			||||||
 | 
					    const compName = nr.components[nr.mountedComponents[env.mountingTo]].name;
 | 
				
			||||||
 | 
					    const ctx = nr.findComponentById(env.cid);
 | 
				
			||||||
 | 
					    const form = {
 | 
				
			||||||
 | 
					      loginButton: ctx.querySelector('button'),
 | 
				
			||||||
 | 
					      progressBar: ctx.querySelector('.linear-progress'),
 | 
				
			||||||
 | 
					      usernameField: ctx.querySelector('.textfield input'),
 | 
				
			||||||
 | 
					      errorContainer: ctx.querySelector('.alert')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (localStorage.getItem('username')) {
 | 
				
			||||||
 | 
					      form.usernameField.value = localStorage.getItem('username');
 | 
				
			||||||
 | 
					      handleLogin();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    form.loginButton.addEventListener('click', handleLogin);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function showError(error) {
 | 
				
			||||||
 | 
					      form.progressBar.style.display = 'none';
 | 
				
			||||||
 | 
					      form.loginButton.disabled = false;
 | 
				
			||||||
 | 
					      form.usernameField.disabled = false;
 | 
				
			||||||
 | 
					      form.errorContainer.style.display = 'block';
 | 
				
			||||||
 | 
					      form.errorContainer.textContent = error;
 | 
				
			||||||
 | 
					      form.usernameField.focus();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function handleLogin() {
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        form.errorContainer.style.display = 'none';
 | 
				
			||||||
 | 
					        form.progressBar.style.display = 'block';
 | 
				
			||||||
 | 
					        form.loginButton.disabled = true;
 | 
				
			||||||
 | 
					        form.usernameField.disabled = true;
 | 
				
			||||||
 | 
					        const username = form.usernameField.value;
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					        if (!username) throw new Error('Username cannot be empty');
 | 
				
			||||||
 | 
					        console.log(`[${compName}] Trying to connect as ${username}`);
 | 
				
			||||||
 | 
					        let protocol
 | 
				
			||||||
 | 
					        switch (window.location.protocol) {
 | 
				
			||||||
 | 
					          case 'http:':
 | 
				
			||||||
 | 
					            protocol = 'ws:';
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          case 'https:':
 | 
				
			||||||
 | 
					            protocol = 'wss:';
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          default:
 | 
				
			||||||
 | 
					            throw new Error('Unknown page protocol');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        window.callback = {
 | 
				
			||||||
 | 
					          socket: new WebSocket(`${protocol}//${window.location.host}?u=${encodeURIComponent(username)}`)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        function handleWSLogin(message) {
 | 
				
			||||||
 | 
					          try {
 | 
				
			||||||
 | 
					            const res = JSON.parse(message.data);
 | 
				
			||||||
 | 
					            if (res.error) throw new Error(res.message);
 | 
				
			||||||
 | 
					            localStorage.setItem('username', username);
 | 
				
			||||||
 | 
					            console.log(`[${compName}] Logged in as ${username}`)
 | 
				
			||||||
 | 
					            nr.unmount(env.mountingTo);
 | 
				
			||||||
 | 
					            nr.mount(config?.appComponent || 'callback', env.mountingTo)
 | 
				
			||||||
 | 
					          } catch (e) {
 | 
				
			||||||
 | 
					            showError(e.message);
 | 
				
			||||||
 | 
					            console.error(e);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        window.callback.socket.addEventListener('open', console.log(`[${compName}] Connected to websocket`));
 | 
				
			||||||
 | 
					        window.callback.socket.addEventListener('message', handleWSLogin);
 | 
				
			||||||
 | 
					      } catch (e) {
 | 
				
			||||||
 | 
					        showError(e.message);
 | 
				
			||||||
 | 
					        console.error(e);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					nr.defineComponent({
 | 
				
			||||||
 | 
					  name: 'callback',
 | 
				
			||||||
 | 
					  template: `
 | 
				
			||||||
 | 
					    <div class="d-flex">
 | 
				
			||||||
 | 
					      <div class="d-flex" style="height: 100vh; width: 100vw; position: fixed; left: -100%; transition: left 0.25s ease-out; z-index: 1000" id="sidebar">
 | 
				
			||||||
 | 
					        <div class="navdrawer modal" id="sidebarbody">
 | 
				
			||||||
 | 
					          <div style="all: unset;">
 | 
				
			||||||
 | 
					            <div class="d-flex w-100 justify-content-space-between align-items-center">
 | 
				
			||||||
 | 
					              <img src="/favicon.png" style="width: 1.5rem" alt="Logo" class="m-right-3">
 | 
				
			||||||
 | 
					              <div class="w-100 d-flex align-items-center">
 | 
				
			||||||
 | 
					                <span class="text-bold" style="margin-right: auto;">Chats</span>
 | 
				
			||||||
 | 
					                <button class="small fill-primary btn w-50">New</button>
 | 
				
			||||||
 | 
					              </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div id="chats">
 | 
				
			||||||
 | 
					            <div class="active">
 | 
				
			||||||
 | 
					              <span class="label">Label</span>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div>
 | 
				
			||||||
 | 
					              <span class="label">Label</span>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <div class="d-flex flex-column w-100" style="height: 100vh;">
 | 
				
			||||||
 | 
					        <div style="background-color: var(--md-sys-color-surface-container)" class="d-flex align-items-center w-100 p-3 gap-3">
 | 
				
			||||||
 | 
					          <span class="material-symbols-outlined" id="menutoggle">menu</span>
 | 
				
			||||||
 | 
					          <span>Person</span>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div style="height: 100%;">
 | 
				
			||||||
 | 
					          CHATDIV
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div style="background-color: var(--md-sys-color-surface-container)" class="d-flex align-items-center w-100">
 | 
				
			||||||
 | 
					          <div class="textfield m-0 w-100 m-left-3">
 | 
				
			||||||
 | 
					            <span class="label">Message</span>
 | 
				
			||||||
 | 
					            <input type="text" placeholder="">
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <button type="submit" class="material-symbols-outlined fill-primary btn m-right-3" disabled>send</button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  `,
 | 
				
			||||||
 | 
					  beforeCreate: () => {
 | 
				
			||||||
 | 
					    return { useShadowRoot: false }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  afterCreate: () => {
 | 
				
			||||||
 | 
					    const sidebar = document.querySelector('#sidebar');
 | 
				
			||||||
 | 
					    const sidebarBody = document.querySelector('#sidebarbody');
 | 
				
			||||||
 | 
					    const menutoggle = document.querySelector('#menutoggle');
 | 
				
			||||||
 | 
					    menutoggle.addEventListener('click', () => {
 | 
				
			||||||
 | 
					      sidebar.style.left = "0";
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    sidebar.addEventListener('click', (event) => {
 | 
				
			||||||
 | 
					      if (!sidebarBody.contains(event.target)) {
 | 
				
			||||||
 | 
					        sidebar.style.left = "-100%";
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const resizeObserver = new ResizeObserver(entries => {
 | 
				
			||||||
 | 
					      for (let entry of entries) {
 | 
				
			||||||
 | 
					        const rootFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize);
 | 
				
			||||||
 | 
					        const fortyRemInPixels = 65 * rootFontSize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (entry.contentRect.width > fortyRemInPixels) {
 | 
				
			||||||
 | 
					          sidebar.style.position = 'static';
 | 
				
			||||||
 | 
					          sidebarBody.classList.remove('modal');
 | 
				
			||||||
 | 
					          sidebarBody.classList.add('standart');
 | 
				
			||||||
 | 
					          menutoggle.style.display = 'none';
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          sidebar.style.position = 'fixed';
 | 
				
			||||||
 | 
					          sidebarBody.classList.remove('standart');
 | 
				
			||||||
 | 
					          sidebarBody.classList.add('modal');
 | 
				
			||||||
 | 
					          menutoggle.style.display = 'block';
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    resizeObserver.observe(document.body);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								public/favicon.ico
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/favicon.ico
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 7.7 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								public/favicon.png
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/favicon.png
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 7.7 KiB  | 
							
								
								
									
										31
									
								
								public/index.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								public/index.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,31 @@
 | 
				
			||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
 | 
					<html lang="en">
 | 
				
			||||||
 | 
					<head>
 | 
				
			||||||
 | 
					  <meta charset="UTF-8">
 | 
				
			||||||
 | 
					  <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
				
			||||||
 | 
					  <title>callback</title>
 | 
				
			||||||
 | 
					  <meta name="color-scheme" content="dark">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <meta property="og:title" content="callback">
 | 
				
			||||||
 | 
					  <meta property="og:description" content="Callback is a messanger made by @true1ann">
 | 
				
			||||||
 | 
					  <meta property="og:image" content="https://callback.true1ann.me/favicon.png">
 | 
				
			||||||
 | 
					  <meta property="og:url" content="https://callback.true1ann.me/">
 | 
				
			||||||
 | 
					  <meta property="og:type" content="website">
 | 
				
			||||||
 | 
					  <meta name="twitter:card" content="summary_large_image">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/true1ann/mmdy.scss@latest/css/styles.css">
 | 
				
			||||||
 | 
					  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/true1ann/mmdy.scss@latest/css/roboto.css">
 | 
				
			||||||
 | 
					  <link rel="stylesheet" href="https://ann.is-a.dev/mmdy.scss/roboto.css">
 | 
				
			||||||
 | 
					  <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200">
 | 
				
			||||||
 | 
					  <link rel="stylesheet" href="/mmdy-theme.css">
 | 
				
			||||||
 | 
					</head>
 | 
				
			||||||
 | 
					<body style="height: 100vh; width: 100vw;">
 | 
				
			||||||
 | 
					  <script src="https://cdn.jsdelivr.net/npm/markdown-it/dist/markdown-it.min.js"></script>
 | 
				
			||||||
 | 
					  <script src="https://cdn.jsdelivr.net/gh/true1ann/neorender@latest/dist/bundle.js"></script>
 | 
				
			||||||
 | 
					  <script src="/components.js"></script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <div id="app"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <script>nr.mount('login', '#app');</script>
 | 
				
			||||||
 | 
					</body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
							
								
								
									
										59
									
								
								public/mmdy-theme.css
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								public/mmdy-theme.css
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,59 @@
 | 
				
			||||||
 | 
					:root {
 | 
				
			||||||
 | 
					  --md-sys-color-primary: rgb(219 198 110);
 | 
				
			||||||
 | 
					  --md-sys-color-surface-tint: rgb(219 198 110);
 | 
				
			||||||
 | 
					  --md-sys-color-on-primary: rgb(58 48 0);
 | 
				
			||||||
 | 
					  --md-sys-color-primary-container: rgb(83 70 0);
 | 
				
			||||||
 | 
					  --md-sys-color-on-primary-container: rgb(248 226 135);
 | 
				
			||||||
 | 
					  --md-sys-color-secondary: rgb(209 198 161);
 | 
				
			||||||
 | 
					  --md-sys-color-on-secondary: rgb(54 48 22);
 | 
				
			||||||
 | 
					  --md-sys-color-secondary-container: rgb(78 71 42);
 | 
				
			||||||
 | 
					  --md-sys-color-on-secondary-container: rgb(238 226 188);
 | 
				
			||||||
 | 
					  --md-sys-color-tertiary: rgb(157 212 158);
 | 
				
			||||||
 | 
					  --md-sys-color-on-tertiary: rgb(1 57 19);
 | 
				
			||||||
 | 
					  --md-sys-color-tertiary-container: rgb(30 81 40);
 | 
				
			||||||
 | 
					  --md-sys-color-on-tertiary-container: rgb(184 241 185);
 | 
				
			||||||
 | 
					  --md-sys-color-error: rgb(255 180 171);
 | 
				
			||||||
 | 
					  --md-sys-color-on-error: rgb(105 0 5);
 | 
				
			||||||
 | 
					  --md-sys-color-error-container: rgb(147 0 10);
 | 
				
			||||||
 | 
					  --md-sys-color-on-error-container: rgb(255 218 214);
 | 
				
			||||||
 | 
					  --md-sys-color-background: rgb(21 19 11);
 | 
				
			||||||
 | 
					  --md-sys-color-on-background: rgb(232 226 212);
 | 
				
			||||||
 | 
					  --md-sys-color-surface: rgb(21 19 11);
 | 
				
			||||||
 | 
					  --md-sys-color-on-surface: rgb(232 226 212);
 | 
				
			||||||
 | 
					  --md-sys-color-surface-variant: rgb(75 71 57);
 | 
				
			||||||
 | 
					  --md-sys-color-on-surface-variant: rgb(205 198 180);
 | 
				
			||||||
 | 
					  --md-sys-color-outline: rgb(150 144 128);
 | 
				
			||||||
 | 
					  --md-sys-color-outline-variant: rgb(75 71 57);
 | 
				
			||||||
 | 
					  --md-sys-color-shadow: rgb(0 0 0);
 | 
				
			||||||
 | 
					  --md-sys-color-scrim: rgb(0 0 0);
 | 
				
			||||||
 | 
					  --md-sys-color-inverse-surface: rgb(232 226 212);
 | 
				
			||||||
 | 
					  --md-sys-color-inverse-on-surface: rgb(51 48 39);
 | 
				
			||||||
 | 
					  --md-sys-color-inverse-primary: rgb(109 94 15);
 | 
				
			||||||
 | 
					  --md-sys-color-primary-fixed: rgb(248 226 135);
 | 
				
			||||||
 | 
					  --md-sys-color-on-primary-fixed: rgb(34 27 0);
 | 
				
			||||||
 | 
					  --md-sys-color-primary-fixed-dim: rgb(219 198 110);
 | 
				
			||||||
 | 
					  --md-sys-color-on-primary-fixed-variant: rgb(83 70 0);
 | 
				
			||||||
 | 
					  --md-sys-color-secondary-fixed: rgb(238 226 188);
 | 
				
			||||||
 | 
					  --md-sys-color-on-secondary-fixed: rgb(33 27 4);
 | 
				
			||||||
 | 
					  --md-sys-color-secondary-fixed-dim: rgb(209 198 161);
 | 
				
			||||||
 | 
					  --md-sys-color-on-secondary-fixed-variant: rgb(78 71 42);
 | 
				
			||||||
 | 
					  --md-sys-color-tertiary-fixed: rgb(184 241 185);
 | 
				
			||||||
 | 
					  --md-sys-color-on-tertiary-fixed: rgb(0 33 8);
 | 
				
			||||||
 | 
					  --md-sys-color-tertiary-fixed-dim: rgb(157 212 158);
 | 
				
			||||||
 | 
					  --md-sys-color-on-tertiary-fixed-variant: rgb(30 81 40);
 | 
				
			||||||
 | 
					  --md-sys-color-surface-dim: rgb(21 19 11);
 | 
				
			||||||
 | 
					  --md-sys-color-surface-bright: rgb(60 57 48);
 | 
				
			||||||
 | 
					  --md-sys-color-surface-container-lowest: rgb(16 14 7);
 | 
				
			||||||
 | 
					  --md-sys-color-surface-container-low: rgb(30 27 19);
 | 
				
			||||||
 | 
					  --md-sys-color-surface-container: rgb(34 32 23);
 | 
				
			||||||
 | 
					  --md-sys-color-surface-container-high: rgb(45 42 33);
 | 
				
			||||||
 | 
					  --md-sys-color-surface-container-highest: rgb(56 53 43);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#sidebar:has(.navdrawer.standart) {
 | 
				
			||||||
 | 
					  width: 40% !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.navdrawer.standart {
 | 
				
			||||||
 | 
					  width: 100% !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue