瀏覽代碼

move css to own file

close #85
close #86
close #87
Markus-Rost 4 年之前
父節點
當前提交
bd01269fe4
共有 6 個文件被更改,包括 288 次插入355 次删除
  1. 4 4
      bot.js
  2. 17 171
      dashboard/index.html
  3. 40 7
      dashboard/index.js
  4. 11 171
      dashboard/login.html
  5. 209 0
      dashboard/src/index.css
  6. 7 2
      main.js

+ 4 - 4
bot.js

@@ -344,13 +344,13 @@ global.log_warn = function(warning, api = true) {
  * End the process gracefully.
  * End the process gracefully.
  * @param {NodeJS.Signals} signal - The signal received.
  * @param {NodeJS.Signals} signal - The signal received.
  */
  */
-async function graceful(signal) {
+function graceful(signal) {
 	isStop = true;
 	isStop = true;
 	console.log( '- ' + shardId + ': ' + signal + ': Preparing to close...' );
 	console.log( '- ' + shardId + ': ' + signal + ': Preparing to close...' );
-	setTimeout( async () => {
+	setTimeout( () => {
 		console.log( '- ' + shardId + ': ' + signal + ': Destroying client...' );
 		console.log( '- ' + shardId + ': ' + signal + ': Destroying client...' );
-		await client.destroy();
-		await db.close( dberror => {
+		client.destroy();
+		db.close( dberror => {
 			if ( dberror ) {
 			if ( dberror ) {
 				console.log( '- ' + shardId + ': ' + signal + ': Error while closing the database connection: ' + dberror );
 				console.log( '- ' + shardId + ': ' + signal + ': Error while closing the database connection: ' + dberror );
 				return dberror;
 				return dberror;

+ 17 - 171
dashboard/index.html

@@ -5,180 +5,12 @@
 	<title>Wiki-Bot Settings</title>
 	<title>Wiki-Bot Settings</title>
 	<link rel="shortcut icon" href="https://cdn.discordapp.com/avatars/461189216198590464/f69cdc197791aed829882b64f9760dbb.png?size=64">
 	<link rel="shortcut icon" href="https://cdn.discordapp.com/avatars/461189216198590464/f69cdc197791aed829882b64f9760dbb.png?size=64">
 	<meta name="description" content="Wiki-Bot is a bot with the purpose to easily search for and link to wiki pages. Wiki-Bot shows short descriptions and additional info about pages and is able to resolve redirects and follow interwiki links.">
 	<meta name="description" content="Wiki-Bot is a bot with the purpose to easily search for and link to wiki pages. Wiki-Bot shows short descriptions and additional info about pages and is able to resolve redirects and follow interwiki links.">
+	<meta property="og:description" content="Wiki-Bot is a bot with the purpose to easily search for and link to wiki pages. Wiki-Bot shows short descriptions and additional info about pages and is able to resolve redirects and follow interwiki links.">
 	<meta property="og:image" content="https://cdn.discordapp.com/avatars/461189216198590464/f69cdc197791aed829882b64f9760dbb.png?size=1024">
 	<meta property="og:image" content="https://cdn.discordapp.com/avatars/461189216198590464/f69cdc197791aed829882b64f9760dbb.png?size=1024">
 	<meta property="og:title" content="Wiki-Bot Settings">
 	<meta property="og:title" content="Wiki-Bot Settings">
 	<meta property="og:site_name" content="Wiki-Bot Settings">
 	<meta property="og:site_name" content="Wiki-Bot Settings">
 	<meta itemprop="author" content="MarkusRost">
 	<meta itemprop="author" content="MarkusRost">
-	<style>
-		html {
-			height: calc(100% - 48px);
-		}
-		body {
-			font-family: Whitney,Helvetica Neue,Helvetica,Arial,sans-serif;
-			text-rendering: optimizeLegibility;
-			background: #36393f;
-			color: #dcddde;
-			position: relative;
-			min-height: 100%;
-			margin: 0;
-		}
-		a {
-			text-decoration: none;
-			color: inherit;
-		}
-		.text a {
-			color: #00b0f4;
-		}
-		.text a:hover {
-			text-decoration: underline;
-		}
-		.text {
-			position: relative;
-			padding: 8px;
-			width: calc(100% - 88px);
-			top: 48px;
-			left: 72px;
-		}
-		.notice {
-			padding: 5px 10px;
-			line-height: 1.6;
-			text-align: center;
-			margin: 0 auto 1em;
-			width: fit-content;
-			background: #200;
-			border: 2px solid #500;
-		}
-		.navbar {
-			background: #2f3136;
-			position: fixed;
-			top: 0;
-			left: 72px;
-			right: 0;
-			display: flex;
-			align-items: center;
-			justify-content: space-between;
-			height: 48px;
-			padding: 0 0 0 8px;
-			font-size: 16px;
-			line-height: 20px;
-			font-weight: bold;
-		}
-		.navbar a {
-			display: flex;
-			align-items: center;
-			height: 100%;
-			padding-left: 10px;
-		}
-		.navbar a:hover {
-			background: #202225;
-		}
-		.navbar .avatar {
-			width: 32px;
-			height: 32px;
-		}
-		.navbar span {
-			padding: 0 10px;
-		}
-		.sidebar {
-			background: #202225;
-			position: absolute;
-			top: 0;
-			left: 0;
-			min-height: calc(100% + 48px);
-			width: 72px;
-			display: flex;
-		}
-		.guildlist {
-			padding: 12px 0;
-			position: relative;
-			flex: 1 1 auto;
-		}
-		.guild {
-			margin: 0 0 8px;
-			position: relative;
-			display: flex;
-			justify-content: center;
-		}
-		.avatar {
-			border-radius: 50%;
-			width: 48px;
-			height: 48px;
-			display: flex;
-			align-items: center;
-			justify-content: center;
-			font-weight: 500;
-			line-height: 1.2em;
-			white-space: nowrap;
-			overflow: hidden;
-			color: #dcddde;
-			font-weight: bold;
-		}
-		.navbar a:hover .avatar,
-		.guild.selected .avatar,
-		.guild:hover .avatar {
-			border-radius: 30%;
-			color: #ffffff;
-		}
-		.noicon {
-			width: 48px;
-			height: 48px;
-			background: #36393f;
-		}
-		.guild.selected .noicon,
-		.guild:hover .noicon {
-			background-color: #7289da;
-		}
-		.svg-avatar {
-			color: #43b581;
-			background: #36393f;
-		}
-		.guild:hover .svg-avatar {
-			color: #ffffff;
-			background-color: #43b581;
-		}
-		.separator {
-			height: 2px;
-			width: 32px;
-			border-radius: 1px;
-			background-color: rgba(255,255,255,0.06);;
-		}
-		.bar {
-			position: absolute;
-			left: 0;
-			top: 0;
-			display: block;
-			width: 8px;
-			border-radius: 0 4px 4px 0;
-			margin-left: -4px;
-			background-color: #ffffff;
-		}
-		.guild:hover .bar {
-			margin-top: 14px;
-			height: 20px;
-		}
-		.guild.selected .bar {
-			margin-top: 4px;
-			height: 40px;
-		}
-		a[alt]:hover:after {
-			content: attr(alt);
-			position: absolute;
-			background: #000000;
-			color: #dcddde;
-			font-weight: bold;
-			font-size: 90%;
-			white-space: nowrap;
-			border-radius: 4px;
-			padding: 8px;
-		}
-		.guild a[alt]:hover:after {
-			top: 20%;
-			left: 72px;
-		}
-		.navbar a[alt]:hover:after {
-			top: 48px;
-		}
-	</style>
+	<link rel="stylesheet" type="text/css" href="/src/index.css">
 </head>
 </head>
 <body class="settings">
 <body class="settings">
 	<div class="text">
 	<div class="text">
@@ -188,7 +20,7 @@
 	<div class="navbar">
 	<div class="navbar">
 		<div style="width: 150px;"></div>
 		<div style="width: 150px;"></div>
 		<a id="support" href="https://discord.gg/v77RTk5" target="_blank" alt="Support server">
 		<a id="support" href="https://discord.gg/v77RTk5" target="_blank" alt="Support server">
-			<img class="avatar" src="https://cdn.discordapp.com/avatars/461189216198590464/f69cdc197791aed829882b64f9760dbb.png?size=64" alt="Support server" width="32" height="32">
+			<img class="avatar" src="https://cdn.discordapp.com/icons/464084451165732868/c6a8b9fc902b09545de8194a911e6045.png?size=128" alt="Support server" width="32" height="32">
 			<span>Support server</span>
 			<span>Support server</span>
 		</a>
 		</a>
 		<a id="logout" href="/logout" alt="Logout">
 		<a id="logout" href="/logout" alt="Logout">
@@ -223,6 +55,20 @@
 				</a>
 				</a>
 			</div>
 			</div>
 		</div>
 		</div>
+		<div class="channellist">
+			<a class="channel">
+				<svg width="20" height="20" viewBox="0 0 24 24">
+					<path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M5.88657 21C5.57547 21 5.3399 20.7189 5.39427 20.4126L6.00001 17H2.59511C2.28449 17 2.04905 16.7198 2.10259 16.4138L2.27759 15.4138C2.31946 15.1746 2.52722 15 2.77011 15H6.35001L7.41001 9H4.00511C3.69449 9 3.45905 8.71977 3.51259 8.41381L3.68759 7.41381C3.72946 7.17456 3.93722 7 4.18011 7H7.76001L8.39677 3.41262C8.43914 3.17391 8.64664 3 8.88907 3H9.87344C10.1845 3 10.4201 3.28107 10.3657 3.58738L9.76001 7H15.76L16.3968 3.41262C16.4391 3.17391 16.6466 3 16.8891 3H17.8734C18.1845 3 18.4201 3.28107 18.3657 3.58738L17.76 7H21.1649C21.4755 7 21.711 7.28023 21.6574 7.58619L21.4824 8.58619C21.4406 8.82544 21.2328 9 20.9899 9H17.41L16.35 15H19.7549C20.0655 15 20.301 15.2802 20.2474 15.5862L20.0724 16.5862C20.0306 16.8254 19.8228 17 19.5799 17H16L15.3632 20.5874C15.3209 20.8261 15.1134 21 14.8709 21H13.8866C13.5755 21 13.3399 20.7189 13.3943 20.4126L14 17H8.00001L7.36325 20.5874C7.32088 20.8261 7.11337 21 6.87094 21H5.88657ZM9.41045 9L8.35045 15H14.3504L15.4104 9H9.41045Z"></path>
+				</svg>
+				<div>Keks</div>
+			</a>
+			<a class="channel">
+				<svg width="20" height="20" viewBox="0 0 24 24">
+					<path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M5.88657 21C5.57547 21 5.3399 20.7189 5.39427 20.4126L6.00001 17H2.59511C2.28449 17 2.04905 16.7198 2.10259 16.4138L2.27759 15.4138C2.31946 15.1746 2.52722 15 2.77011 15H6.35001L7.41001 9H4.00511C3.69449 9 3.45905 8.71977 3.51259 8.41381L3.68759 7.41381C3.72946 7.17456 3.93722 7 4.18011 7H7.76001L8.39677 3.41262C8.43914 3.17391 8.64664 3 8.88907 3H9.87344C10.1845 3 10.4201 3.28107 10.3657 3.58738L9.76001 7H15.76L16.3968 3.41262C16.4391 3.17391 16.6466 3 16.8891 3H17.8734C18.1845 3 18.4201 3.28107 18.3657 3.58738L17.76 7H21.1649C21.4755 7 21.711 7.28023 21.6574 7.58619L21.4824 8.58619C21.4406 8.82544 21.2328 9 20.9899 9H17.41L16.35 15H19.7549C20.0655 15 20.301 15.2802 20.2474 15.5862L20.0724 16.5862C20.0306 16.8254 19.8228 17 19.5799 17H16L15.3632 20.5874C15.3209 20.8261 15.1134 21 14.8709 21H13.8866C13.5755 21 13.3399 20.7189 13.3943 20.4126L14 17H8.00001L7.36325 20.5874C7.32088 20.8261 7.11337 21 6.87094 21H5.88657ZM9.41045 9L8.35045 15H14.3504L15.4104 9H9.41045Z"></path>
+				</svg>
+				<div>Keks</div>
+			</a>
+		</div>
 	</div>
 	</div>
 </body>
 </body>
 </html>
 </html>

+ 40 - 7
dashboard/index.js

@@ -21,9 +21,34 @@ const oauth = new DiscordOauth2( {
 } );
 } );
 
 
 const fs = require('fs');
 const fs = require('fs');
+const path = require('path');
 const files = {
 const files = {
 	index: fs.readFileSync('./dashboard/index.html'),
 	index: fs.readFileSync('./dashboard/index.html'),
-	login: fs.readFileSync('./dashboard/login.html')
+	login: fs.readFileSync('./dashboard/login.html'),
+	src: new Map(fs.readdirSync( './dashboard/src' ).map( file => {
+		let contentType = 'text/html';
+		switch ( path.extname(file) ) {
+			case '.css':
+				contentType = 'text/css';
+				break;
+			case '.js':
+				contentType = 'text/javascript';
+				break;
+			case '.json':
+				contentType = 'application/json';
+				break;
+			case '.png':
+				contentType = 'image/png';
+				break;
+			case '.jpg':
+				contentType = 'image/jpg';
+				break;
+		}
+		return [`/src/${file}`, {
+			name: file, contentType,
+			path: `./dashboard/src/${file}`
+		}];
+	} ))
 }
 }
 
 
 /**
 /**
@@ -96,11 +121,19 @@ const server = http.createServer((req, res) => {
 		return res.end();
 		return res.end();
 	}
 	}
 
 
-	if ( req.url === '/favicon.ico' ) {
+	var reqURL = new URL(req.url, process.env.dashboard);
+
+	if ( reqURL.pathname === '/favicon.ico' ) {
 		res.writeHead(302, {Location: 'https://cdn.discordapp.com/avatars/461189216198590464/f69cdc197791aed829882b64f9760dbb.png?size=64'});
 		res.writeHead(302, {Location: 'https://cdn.discordapp.com/avatars/461189216198590464/f69cdc197791aed829882b64f9760dbb.png?size=64'});
 		return res.end();
 		return res.end();
 	}
 	}
 
 
+	if ( files.src.has(reqURL.pathname) ) {
+		let file = files.src.get(reqURL.pathname);
+		res.writeHead(200, {'Content-Type': file.contentType});
+		return fs.createReadStream(file.path).pipe(res);
+	}
+
 	res.setHeader('Content-Type', 'text/html');
 	res.setHeader('Content-Type', 'text/html');
 	res.setHeader('Content-Language', ['en']);
 	res.setHeader('Content-Language', ['en']);
 
 
@@ -113,8 +146,6 @@ const server = http.createServer((req, res) => {
 		return cookie.split('=')[0] === 'wikibot';
 		return cookie.split('=')[0] === 'wikibot';
 	} )?.map( cookie => cookie.replace( /^wikibot="(\w+(?:-\d+)?)"$/, '$1' ) )?.join();
 	} )?.map( cookie => cookie.replace( /^wikibot="(\w+(?:-\d+)?)"$/, '$1' ) )?.join();
 
 
-	var reqURL = new URL(req.url, process.env.dashboard);
-
 	if ( reqURL.pathname === '/login' ) {
 	if ( reqURL.pathname === '/login' ) {
 		if ( settingsData.has(state) ) {
 		if ( settingsData.has(state) ) {
 			res.writeHead(302, {Location: '/'});
 			res.writeHead(302, {Location: '/'});
@@ -157,6 +188,7 @@ const server = http.createServer((req, res) => {
 			scope: ['identify', 'guilds'],
 			scope: ['identify', 'guilds'],
 			prompt: 'none', state
 			prompt: 'none', state
 		} );
 		} );
+		$('a#login').attr('href', url);
 		$('replace#text').replaceWith(`<a href="${url}">Login</a>`);
 		$('replace#text').replaceWith(`<a href="${url}">Login</a>`);
 		let body = $.html();
 		let body = $.html();
 		res.writeHead(responseCode, {
 		res.writeHead(responseCode, {
@@ -441,17 +473,18 @@ function hasPerm(all, permission, admin = true) {
  * End the process gracefully.
  * End the process gracefully.
  * @param {NodeJS.Signals} signal - The signal received.
  * @param {NodeJS.Signals} signal - The signal received.
  */
  */
-async function graceful(signal) {
+function graceful(signal) {
 	console.log( '- Dashboard: ' + signal + ': Closing the dashboard...' );
 	console.log( '- Dashboard: ' + signal + ': Closing the dashboard...' );
-	await server.close( () => {
+	server.close( () => {
 		console.log( '- Dashboard: ' + signal + ': Closed the dashboard server.' );
 		console.log( '- Dashboard: ' + signal + ': Closed the dashboard server.' );
 	} );
 	} );
-	await db.close( dberror => {
+	db.close( dberror => {
 		if ( dberror ) {
 		if ( dberror ) {
 			console.log( '- Dashboard: ' + signal + ': Error while closing the database connection: ' + dberror );
 			console.log( '- Dashboard: ' + signal + ': Error while closing the database connection: ' + dberror );
 			return dberror;
 			return dberror;
 		}
 		}
 		console.log( '- Dashboard: ' + signal + ': Closed the database connection.' );
 		console.log( '- Dashboard: ' + signal + ': Closed the database connection.' );
+		process.exit(0);
 	} );
 	} );
 }
 }
 
 

+ 11 - 171
dashboard/login.html

@@ -5,180 +5,12 @@
 	<title>Login – Wiki-Bot Settings</title>
 	<title>Login – Wiki-Bot Settings</title>
 	<link rel="shortcut icon" href="https://cdn.discordapp.com/avatars/461189216198590464/f69cdc197791aed829882b64f9760dbb.png?size=64">
 	<link rel="shortcut icon" href="https://cdn.discordapp.com/avatars/461189216198590464/f69cdc197791aed829882b64f9760dbb.png?size=64">
 	<meta name="description" content="Wiki-Bot is a bot with the purpose to easily search for and link to wiki pages. Wiki-Bot shows short descriptions and additional info about pages and is able to resolve redirects and follow interwiki links.">
 	<meta name="description" content="Wiki-Bot is a bot with the purpose to easily search for and link to wiki pages. Wiki-Bot shows short descriptions and additional info about pages and is able to resolve redirects and follow interwiki links.">
+	<meta property="og:description" content="Wiki-Bot is a bot with the purpose to easily search for and link to wiki pages. Wiki-Bot shows short descriptions and additional info about pages and is able to resolve redirects and follow interwiki links.">
 	<meta property="og:image" content="https://cdn.discordapp.com/avatars/461189216198590464/f69cdc197791aed829882b64f9760dbb.png?size=1024">
 	<meta property="og:image" content="https://cdn.discordapp.com/avatars/461189216198590464/f69cdc197791aed829882b64f9760dbb.png?size=1024">
 	<meta property="og:title" content="Login – Wiki-Bot Settings">
 	<meta property="og:title" content="Login – Wiki-Bot Settings">
 	<meta property="og:site_name" content="Wiki-Bot Settings">
 	<meta property="og:site_name" content="Wiki-Bot Settings">
 	<meta itemprop="author" content="MarkusRost">
 	<meta itemprop="author" content="MarkusRost">
-	<style>
-		html {
-			height: calc(100% - 48px);
-		}
-		body {
-			font-family: Whitney,Helvetica Neue,Helvetica,Arial,sans-serif;
-			text-rendering: optimizeLegibility;
-			background: #36393f;
-			color: #dcddde;
-			position: relative;
-			min-height: 100%;
-			margin: 0;
-		}
-		a {
-			text-decoration: none;
-			color: inherit;
-		}
-		.text a {
-			color: #00b0f4;
-		}
-		.text a:hover {
-			text-decoration: underline;
-		}
-		.text {
-			position: relative;
-			padding: 8px;
-			width: calc(100% - 88px);
-			top: 48px;
-			left: 72px;
-		}
-		.notice {
-			padding: 5px 10px;
-			line-height: 1.6;
-			text-align: center;
-			margin: 0 auto 1em;
-			width: fit-content;
-			background: #200;
-			border: 2px solid #500;
-		}
-		.navbar {
-			background: #2f3136;
-			position: fixed;
-			top: 0;
-			left: 72px;
-			right: 0;
-			display: flex;
-			align-items: center;
-			justify-content: space-between;
-			height: 48px;
-			padding: 0 0 0 8px;
-			font-size: 16px;
-			line-height: 20px;
-			font-weight: bold;
-		}
-		.navbar a {
-			display: flex;
-			align-items: center;
-			height: 100%;
-			padding-left: 10px;
-		}
-		.navbar a:hover {
-			background: #202225;
-		}
-		.navbar .avatar {
-			width: 32px;
-			height: 32px;
-		}
-		.navbar span {
-			padding: 0 10px;
-		}
-		.sidebar {
-			background: #202225;
-			position: absolute;
-			top: 0;
-			left: 0;
-			min-height: calc(100% + 48px);
-			width: 72px;
-			display: flex;
-		}
-		.guildlist {
-			padding: 12px 0;
-			position: relative;
-			flex: 1 1 auto;
-		}
-		.guild {
-			margin: 0 0 8px;
-			position: relative;
-			display: flex;
-			justify-content: center;
-		}
-		.avatar {
-			border-radius: 50%;
-			width: 48px;
-			height: 48px;
-			display: flex;
-			align-items: center;
-			justify-content: center;
-			font-weight: 500;
-			line-height: 1.2em;
-			white-space: nowrap;
-			overflow: hidden;
-			color: #dcddde;
-			font-weight: bold;
-		}
-		.navbar a:hover .avatar,
-		.guild.selected .avatar,
-		.guild:hover .avatar {
-			border-radius: 30%;
-			color: #ffffff;
-		}
-		.noicon {
-			width: 48px;
-			height: 48px;
-			background: #36393f;
-		}
-		.guild.selected .noicon,
-		.guild:hover .noicon {
-			background-color: #7289da;
-		}
-		.svg-avatar {
-			color: #43b581;
-			background: #36393f;
-		}
-		.guild:hover .svg-avatar {
-			color: #ffffff;
-			background-color: #43b581;
-		}
-		.separator {
-			height: 2px;
-			width: 32px;
-			border-radius: 1px;
-			background-color: rgba(255,255,255,0.06);;
-		}
-		.bar {
-			position: absolute;
-			left: 0;
-			top: 0;
-			display: block;
-			width: 8px;
-			border-radius: 0 4px 4px 0;
-			margin-left: -4px;
-			background-color: #ffffff;
-		}
-		.guild:hover .bar {
-			margin-top: 14px;
-			height: 20px;
-		}
-		.guild.selected .bar {
-			margin-top: 4px;
-			height: 40px;
-		}
-		a[alt]:hover:after {
-			content: attr(alt);
-			position: absolute;
-			background: #000000;
-			color: #dcddde;
-			font-weight: bold;
-			font-size: 90%;
-			white-space: nowrap;
-			border-radius: 4px;
-			padding: 8px;
-		}
-		.guild a[alt]:hover:after {
-			top: 20%;
-			left: 72px;
-		}
-		.navbar a[alt]:hover:after {
-			top: 48px;
-		}
-	</style>
+	<link rel="stylesheet" type="text/css" href="/src/index.css">
 </head>
 </head>
 <body class="settings">
 <body class="settings">
 	<div class="text">
 	<div class="text">
@@ -188,7 +20,7 @@
 	<div class="navbar">
 	<div class="navbar">
 		<div style="width: 150px;"></div>
 		<div style="width: 150px;"></div>
 		<a id="support" href="https://discord.gg/v77RTk5" target="_blank" alt="Support server">
 		<a id="support" href="https://discord.gg/v77RTk5" target="_blank" alt="Support server">
-			<img class="avatar" src="https://cdn.discordapp.com/avatars/461189216198590464/f69cdc197791aed829882b64f9760dbb.png?size=64" alt="Support server" width="32" height="32">
+			<img class="avatar" src="https://cdn.discordapp.com/icons/464084451165732868/c6a8b9fc902b09545de8194a911e6045.png?size=128" alt="Support server" width="32" height="32">
 			<span>Support server</span>
 			<span>Support server</span>
 		</a>
 		</a>
 	</div>
 	</div>
@@ -205,6 +37,14 @@
 				</a>
 				</a>
 			</div>
 			</div>
 		</div>
 		</div>
+		<div class="channellist">
+			<a class="channel" id="login">
+				<svg width="20" height="20" viewBox="0 0 24 24">
+					<path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M5.88657 21C5.57547 21 5.3399 20.7189 5.39427 20.4126L6.00001 17H2.59511C2.28449 17 2.04905 16.7198 2.10259 16.4138L2.27759 15.4138C2.31946 15.1746 2.52722 15 2.77011 15H6.35001L7.41001 9H4.00511C3.69449 9 3.45905 8.71977 3.51259 8.41381L3.68759 7.41381C3.72946 7.17456 3.93722 7 4.18011 7H7.76001L8.39677 3.41262C8.43914 3.17391 8.64664 3 8.88907 3H9.87344C10.1845 3 10.4201 3.28107 10.3657 3.58738L9.76001 7H15.76L16.3968 3.41262C16.4391 3.17391 16.6466 3 16.8891 3H17.8734C18.1845 3 18.4201 3.28107 18.3657 3.58738L17.76 7H21.1649C21.4755 7 21.711 7.28023 21.6574 7.58619L21.4824 8.58619C21.4406 8.82544 21.2328 9 20.9899 9H17.41L16.35 15H19.7549C20.0655 15 20.301 15.2802 20.2474 15.5862L20.0724 16.5862C20.0306 16.8254 19.8228 17 19.5799 17H16L15.3632 20.5874C15.3209 20.8261 15.1134 21 14.8709 21H13.8866C13.5755 21 13.3399 20.7189 13.3943 20.4126L14 17H8.00001L7.36325 20.5874C7.32088 20.8261 7.11337 21 6.87094 21H5.88657ZM9.41045 9L8.35045 15H14.3504L15.4104 9H9.41045Z"></path>
+				</svg>
+				<div>Login</div>
+			</a>
+		</div>
 	</div>
 	</div>
 </body>
 </body>
 </html>
 </html>

+ 209 - 0
dashboard/src/index.css

@@ -0,0 +1,209 @@
+html {
+	height: calc(100% - 48px);
+}
+body {
+	font-family: Whitney,Helvetica Neue,Helvetica,Arial,sans-serif;
+	text-rendering: optimizeLegibility;
+	background: #36393f;
+	color: #dcddde;
+	position: relative;
+	min-height: 100%;
+	margin: 0;
+}
+a {
+	text-decoration: none;
+	color: inherit;
+}
+a[alt]:hover:after {
+	content: attr(alt);
+	position: absolute;
+	background: #000000;
+	color: #dcddde;
+	font-weight: bold;
+	font-size: 90%;
+	white-space: nowrap;
+	border-radius: 4px;
+	padding: 8px;
+}
+.text a {
+	color: #00b0f4;
+}
+.text a:hover {
+	text-decoration: underline;
+}
+.text {
+	position: relative;
+	padding: 8px;
+	width: calc(100% - 328px);
+	top: 48px;
+	left: 312px;
+}
+.notice {
+	padding: 5px 10px;
+	line-height: 1.6;
+	text-align: center;
+	margin: 0 auto 1em;
+	width: fit-content;
+	background: #200;
+	border: 2px solid #500;
+}
+.navbar {
+	background: #2f3136;
+	position: fixed;
+	top: 0;
+	left: 72px;
+	right: 0;
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	height: 48px;
+	padding: 0 0 0 8px;
+	font-size: 16px;
+	line-height: 20px;
+	font-weight: bold;
+	box-shadow: 0 1px 0 rgba(4,4,5,0.2),
+				0 1.5px 0 rgba(6,6,7,0.05),
+				0 2px 0 rgba(4,4,5,0.05);
+	z-index: 2;
+}
+.navbar a {
+	display: flex;
+	align-items: center;
+	height: 100%;
+	padding-left: 10px;
+}
+.navbar a:hover {
+	background: #202225;
+}
+.navbar .avatar {
+	width: 32px;
+	height: 32px;
+}
+.navbar span {
+	padding: 0 10px;
+}
+.sidebar {
+	background: #202225;
+	position: absolute;
+	top: 0;
+	left: 0;
+	min-height: calc(100% + 48px);
+	width: 72px;
+	display: flex;
+}
+.guildlist {
+	padding: 12px 0;
+	position: relative;
+	flex: 1 1 auto;
+	z-index: 2;
+}
+.guild {
+	margin: 0 0 8px;
+	position: relative;
+	display: flex;
+	justify-content: center;
+}
+.avatar {
+	border-radius: 50%;
+	width: 48px;
+	height: 48px;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	font-weight: 500;
+	line-height: 1.2em;
+	white-space: nowrap;
+	overflow: hidden;
+	color: #dcddde;
+	font-weight: bold;
+}
+.navbar a:hover .avatar,
+.guild.selected .avatar,
+.guild:hover .avatar {
+	border-radius: 30%;
+	color: #ffffff;
+}
+.noicon {
+	width: 48px;
+	height: 48px;
+	background: #36393f;
+}
+.guild.selected .noicon,
+.guild:hover .noicon {
+	background-color: #7289da;
+}
+.svg-avatar {
+	color: #43b581;
+	background: #36393f;
+}
+.guild:hover .svg-avatar {
+	color: #ffffff;
+	background-color: #43b581;
+}
+.separator {
+	height: 2px;
+	width: 32px;
+	border-radius: 1px;
+	background-color: rgba(255,255,255,0.06);
+}
+.bar {
+	position: absolute;
+	left: 0;
+	top: 0;
+	display: block;
+	width: 8px;
+	border-radius: 0 4px 4px 0;
+	margin-left: -4px;
+	background-color: #ffffff;
+}
+.guild:hover .bar {
+	margin-top: 14px;
+	height: 20px;
+}
+.guild.selected .bar {
+	margin-top: 4px;
+	height: 40px;
+}
+.guild a[alt]:hover:after {
+	top: 20%;
+	left: 72px;
+}
+.navbar a[alt]:hover:after {
+	top: 48px;
+}
+.channellist {
+	background: #2f3136;
+	position: absolute;
+	min-height: calc(100% - 80px);
+	padding: 16px 0;
+	width: 240px;
+	top: 48px;
+	left: 72px;
+	z-index: 1;
+}
+.channel {
+	padding: 0 8px;
+	margin: 0 8px 2px;
+	height: 32px;
+	border-radius: 4px;
+	display: flex;
+	align-items: center;
+	color: #8e9297;
+}
+.channel:hover {
+	background: rgba(79,84,92,0.16);
+}
+.channel svg {
+	margin-right: 6px;
+	width: 20;
+	height: 20;
+}
+.channel div {
+	font-size: 16px;
+	line-height: 20px;
+	text-shadow: 0 0 1px;
+	white-space: nowrap;
+}
+.channel:hover div {
+	color: #dcddde;
+}

+ 7 - 2
main.js

@@ -35,6 +35,7 @@ manager.on( 'shardCreate', shard => {
 		if ( message === 'SIGKILL' ) {
 		if ( message === 'SIGKILL' ) {
 			console.log( '\n- Killing all shards!\n\n' );
 			console.log( '\n- Killing all shards!\n\n' );
 			manager.shards.forEach( shard => shard.kill() );
 			manager.shards.forEach( shard => shard.kill() );
+			if ( typeof server !== 'undefined' ) server.kill();
 		}
 		}
 		if ( message === 'postStats' && process.env.botlist ) postStats();
 		if ( message === 'postStats' && process.env.botlist ) postStats();
 	} );
 	} );
@@ -157,7 +158,7 @@ function postStats(botList = JSON.parse(process.env.botlist), shardCount = manag
  * End the process gracefully.
  * End the process gracefully.
  * @param {NodeJS.Signals} signal - The signal received.
  * @param {NodeJS.Signals} signal - The signal received.
  */
  */
-async function graceful(signal) {
+function graceful(signal) {
 	console.log( '- ' + signal + ': Disabling respawn...' );
 	console.log( '- ' + signal + ': Disabling respawn...' );
 	manager.respawn = false;
 	manager.respawn = false;
 }
 }
@@ -175,6 +176,10 @@ if ( isDebug && process.argv[3]?.startsWith( '--timeout:' ) ) {
 	setTimeout( () => {
 	setTimeout( () => {
 		console.log( `\n- Running for ${timeout} seconds, closing process!\n` );
 		console.log( `\n- Running for ${timeout} seconds, closing process!\n` );
 		manager.shards.forEach( shard => shard.kill() );
 		manager.shards.forEach( shard => shard.kill() );
-		if ( typeof server !== 'undefined' ) server.kill('SIGTERM');
+		if ( typeof server !== 'undefined' ) server.kill();
+		setTimeout( () => {
+			console.log( `\n- Process stayed open, exiting maually!\n` );
+			process.exit(0);
+		}, timeout  * 1000 ).unref();
 	}, timeout  * 1000 ).unref();
 	}, timeout  * 1000 ).unref();
 }
 }