Compare commits

...

58 Commits

Author SHA1 Message Date
Kayne Ruse 1b5cbaea17 Docker behaviour changed, fixed 2023-06-26 23:20:34 +10:00
Kayne Ruse 0f2b8d3f52 Updated dependencies, bumped patch version 2023-06-26 23:06:15 +10:00
Kayne Ruse 88c2239fdb Updated dependencies 2023-05-29 07:56:13 +10:00
Kayne Ruse b5f9d0a7fc Updated dependencies 2023-05-03 21:32:31 +10:00
Kayne Ruse 1ec29e4519 Updated depencencies, bumped version 2023-03-25 01:50:32 +11:00
Kayne Ruse a15c43b3d0 Updated dependencies 2023-03-19 02:53:17 +11:00
Kayne Ruse 9bc96bdb5f Updated dependencies 2023-02-21 09:30:15 +11:00
Kayne Ruse 4bdcee11ea Updated dependencies, License 2023-01-12 08:08:34 +11:00
Kayne Ruse e1cd1ec001 Bumped version number 2023-01-04 12:57:13 +00:00
Kayne Ruse e8a9a79687 Switched to a slim docker distro 2023-01-04 23:51:49 +11:00
Kayne Ruse 7d628be826 Updated libraries 2022-12-31 19:03:50 +00:00
Kayne Ruse 7a42ab3108 Updated dependencies 2022-11-29 05:06:21 +00:00
Kayne Ruse ec573e1074 Updated dependencies 2022-11-13 02:17:33 +00:00
Kayne Ruse 9c5033daea Updated dependencies 2022-08-01 10:40:43 +01:00
Kayne Ruse fb4d857224 Tweaked cors handing 2022-07-26 13:40:18 +01:00
Kayne Ruse e0d6260d1a Updated dependencies 2022-07-23 11:49:18 +01:00
Kayne Ruse 53ea726c89 Bumped patch version 2022-06-10 17:26:27 +01:00
Kayne Ruse c44ed79e6d Updated dependencies 2022-06-10 17:09:31 +01:00
Kayne Ruse 518f5dbee7 Updated dependencies 2022-05-30 06:57:11 +01:00
Kayne Ruse be57dbe51c Added FUNDING.yml 2022-02-13 07:48:48 +11:00
Kayne Ruse 8309c3b832 Updated libraries 2022-01-20 13:49:05 +11:00
Kayne Ruse 95d229c561 Bumped version number 2022-01-03 08:31:56 +00:00
Kayne Ruse 0b1456ebde Merge branch 'timestamps' 2022-01-03 08:30:23 +00:00
Kayne Ruse bd5b6e8233 Added async keyword 2022-01-03 08:30:03 +00:00
Keith Campbell 05eecf8bdd removed everything but lines 50-60 2022-01-02 18:35:50 -05:00
Keith Campbell 2aedb6e938 ready for PR/review 2022-01-02 18:00:16 -05:00
Keith Campbell ab73d05471 updated original code, using createdAt from log 2022-01-01 21:45:09 -05:00
Keith Campbell f72b0e5522 added timestamp to messages and logs, need to test 2022-01-01 17:11:11 -05:00
Kayne Ruse 900312752b Cleaning up tools 2021-12-23 14:00:47 +00:00
Kayne Ruse 1b2868d68f Updated README.md 2021-12-21 19:38:08 +00:00
Kayne Ruse 33157d48d3 Re-established a database relationship 2021-12-12 16:53:33 +00:00
Kayne Ruse e0b3193607 Preparing for Egg Trainer merge 2021-11-18 15:00:56 +00:00
Kayne Ruse 655c81174e Updated package-lock.json 2021-11-17 06:14:31 +00:00
Kayne Ruse 8c053e75aa Bumped node to version 16 LTS 2021-11-17 04:43:11 +00:00
Kayne Ruse 69aff6ec32 Updated package-lock.json 2021-11-15 22:40:16 +00:00
Kayne Ruse c42d84864e Updated documentation 2021-08-21 22:45:27 +01:00
Kayne Ruse 6b01bfaad0 Update package.json 2021-07-23 20:42:48 +10:00
Kayne Ruse 5ae0c1c47c Updated packages 2021-07-23 19:59:36 +10:00
Kayne Ruse 812766d96a Merged private changes 2021-07-23 19:58:08 +10:00
Kayne Ruse 31d19df4a5 Updated .dockerignore 2021-07-23 19:12:53 +10:00
Kayne Ruse 53776438a9 Updated packages 2021-07-15 09:25:14 +10:00
Kayne Ruse 488b932282 Updated packages 2021-04-28 21:29:08 +10:00
Kayne Ruse 370d7905eb Renaming database primary key 2021-04-23 21:20:22 +10:00
Kayne Ruse 0e88c9e64c Updated modules 2021-04-20 18:50:39 +10:00
Kayne Ruse 254e5f2d18 Updated packages 2021-04-19 06:05:27 +10:00
Kayne Ruse e077d4b2d5 Updated .dockerignore 2021-04-16 16:25:24 +10:00
Kayne Ruse 14c4f52a93 Added .dockerignore 2021-04-16 13:24:38 +10:00
Kayne Ruse 8556465796 Tweaked package.json 2021-04-10 04:59:24 +10:00
Kayne Ruse 37e6c35b9c Ripped out unneeded stuff 2021-04-07 02:00:33 +10:00
Kayne Ruse b503df3399 Funky config stuff gives me a headache 2021-04-03 03:26:59 +11:00
Kayne Ruse 928c76fc84 Tweaked Dockerfile 2021-04-03 02:35:00 +11:00
Kayne Ruse ee5394f895 Tweaked Dockerfile 2021-04-03 02:11:21 +11:00
Kayne Ruse 7b85bb1aeb Formatting errors 2021-04-03 01:59:30 +11:00
Kayne Ruse 028766c82b Working on multiplatform things 2021-04-03 01:42:37 +11:00
Kayne Ruse 266cf4070f Added docker publishing 2021-04-03 01:08:10 +11:00
Kayne Ruse ab0bad4f73 Chat report table working 2021-03-28 07:57:56 +11:00
Kayne Ruse f83ef938ab Updated admin and mod flag system 2021-03-24 08:23:02 +11:00
Kayne Ruse 0b5cc49e6e Added reporting feature 2021-03-24 03:20:29 +11:00
20 changed files with 727 additions and 3139 deletions
+10
View File
@@ -0,0 +1,10 @@
.git*
tools*
mysql*
letsencrypt*
test*
.env*
.github*
LICENSE*
+2
View File
@@ -1,5 +1,7 @@
WEB_PORT=3300
WEB_ORIGIN=http://localhost:3001
DB_HOSTNAME=database
DB_DATABASE=chat
DB_USERNAME=chat
+5
View File
@@ -0,0 +1,5 @@
# These are supported funding model platforms
patreon: krgamestudios
ko_fi: krgamestudios
custom: ["https://www.paypal.com/donate/?hosted_button_id=73Q82T2ZHV8AA"]
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
docker_image: krgamestudios/chat-server
- name: Login to DockerHub
uses: docker/login-action@v1
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
+2 -2
View File
@@ -1,7 +1,7 @@
FROM node:15
FROM node:18-bullseye-slim
WORKDIR "/app"
COPY package*.json ./
COPY package*.json /app
RUN npm install --production
COPY . /app
EXPOSE 3300
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2021 Kayne Ruse, KR Game Studios
Copyright (c) 2021-2023 Kayne Ruse, KR Game Studios
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
+24 -2
View File
@@ -2,13 +2,34 @@
An API centric chat server. Uses Sequelize and mariaDB by default.
This server is available via docker hub at krgamestudios/chat-server.
# Setup
There are multiple ways to run this app - it can run on it's own via `npm start` (for production) or `npm run dev` (for development). it can also run inside docker using `docker-compose up --build` - run `node configure-script.js` to generate docker-compose.yml.
There are multiple ways to run this app - it can run on it's own via `npm start` (for production) or `npm run dev` (for development). it can also run inside docker using `docker-compose up --build` - run `node configure-script.js` to generate docker-compose.yml and startup.sql.
To generate an authorization token, use [auth-server](https://github.com/krgamestudios/auth-server). A public-facing development auth-server is available here (tokens are valid for 10 minutes):
```
POST https://dev-auth.krgamestudios.com/auth/login HTTP/1.1
Content-Type: application/json
{
"email": "example@example.com",
"password": "helloworld"
}
```
# API
This server uses socket.io for communication. Be aware that every chat message requires a valid JWT. See the [auth-server](https://github.com/krgamestudios/auth-server) for details.
This server uses socket.io for communication. Be aware that every 'open chat', 'message' and 'report' signal requires a valid JWT, as part of the message:
```js
socket.emit('message', {
accessToken,
text: inputRef.current.value
});
```
The event types are as follows:
@@ -19,6 +40,7 @@ on 'error' -> Server emits and logs an error
on 'open chat' -> Preps the server for your messages, places you in the room 'general'
on 'message' -> Server broadcasts to all other users in your room
on 'disconnect' -> Server will no longer accept your messages
on 'report' -> Report the chatlog with the index 'id'
Chat Commands:
+5 -3
View File
@@ -30,10 +30,11 @@ const question = (prompt, def = null) => {
//project configuration
const appName = await question('App Name', 'chat');
const appWebAddress = await question('Web Addr', `${appName}.example.com`);
const appWebOrigin = await question('Web Origin', `https://example.com`); //TODO: clean these up properly
const appPort = await question('App Port', '3300');
const appDBUser = await question('DB User', appName);
const appDBPass = await question('DB Pass', uuid());
const appDBPass = await question('DB Pass', 'blastoise');
const dbRootPass = await question('DB Root Pass');
const appSecretAccess = await question('Access Token Secret', uuid(32));
@@ -59,6 +60,7 @@ services:
- "traefik.http.services.${appName}service.loadbalancer.server.port=${appPort}"
environment:
- WEB_PORT=${appPort}
- WEB_ORIGIN=${appWebOrigin}
- DB_HOSTNAME=database
- DB_DATABASE=${appName}
- DB_USERNAME=${appDBUser}
@@ -76,7 +78,7 @@ services:
MYSQL_USER: ${appDBUser}
MYSQL_PASSWORD: ${appDBPass}
MYSQL_ROOT_PASSWORD: ${dbRootPass}
networks:
networks:
- app-network
volumes:
- ./mysql:/var/lib/mysql
@@ -108,7 +110,7 @@ networks:
`;
const dockerfile = `
FROM node:15
FROM node:18-bullseye-slim
WORKDIR "/app"
COPY package*.json ./
RUN npm install --production
+506 -3098
View File
File diff suppressed because it is too large Load Diff
+11 -9
View File
@@ -1,6 +1,6 @@
{
"name": "chat-server",
"version": "1.0.0",
"version": "1.4.6",
"description": "An API centric chat server. Uses Sequelize and mariaDB by default.",
"main": "server/server.js",
"scripts": {
@@ -19,16 +19,18 @@
},
"homepage": "https://github.com/krgamestudios/chat-server#readme",
"dependencies": {
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"jsonwebtoken": "^8.5.1",
"mariadb": "^2.5.2",
"sequelize": "^6.5.0",
"socket.io": "^4.0.0"
"dotenv": "^16.3.1",
"express": "^4.18.2",
"jsonwebtoken": "^9.0.0",
"mariadb": "^3.2.0",
"sequelize": "^6.32.1",
"socket.io": "^4.7.0"
},
"devDependencies": {
"nodemon": "^2.0.7"
"nodemon": "^2.0.22"
},
"overrides": {
"semver": "^7.5.2"
}
}
+21
View File
@@ -0,0 +1,21 @@
const express = require('express');
const router = express.Router();
//middleware
const tokenAuth = require('../utilities/token-auth');
router.use(tokenAuth);
router.use((req, res, next) => {
//check the user's admin status
if (!req.user.mod) {
return res.status(401).send('Mods only');
}
next();
});
//basic route management
router.get('/reports', require('./reports'));
router.delete('/reports', require('./reports-delete'));
module.exports = router;
+15
View File
@@ -0,0 +1,15 @@
const { chatlog, reports } = require('../database/models');
//admin/reports
const route = async (req, res) => {
const reps = await reports.destroy({
where: {
chatlogIndex: req.body.chatlogIndex
}
});
//respond
res.status(200).end();
};
module.exports = route;
+31
View File
@@ -0,0 +1,31 @@
const { chatlog, reports } = require('../database/models');
//admin/reports
const route = async (req, res) => {
const reps = await reports.findAll({
include: [{
model: chatlog,
required: true
}],
order: ['chatlogIndex']
});
//collate
const response = [];
for(let i = 0; i < reps.length; i++) {
//new chatlog
if (response.length == 0 || response[response.length - 1].chatlogIndex != reps[i].chatlogIndex) {
response.push(reps[i]);
response[response.length - 1].reporter = [response[response.length - 1].reporter]; //reporters in an array
continue;
}
//multiple people reported this, add to the existing array
response[response.length - 1].reporter.push(reps[i].reporter);
}
//respond
res.status(200).json(response);
};
module.exports = route;
+28 -15
View File
@@ -1,13 +1,13 @@
const jwt = require('jsonwebtoken');
const { Op } = require('sequelize');
const { chatlog, mute } = require('../database/models');
const { chatlog, mute, reports } = require('../database/models');
const chat = io => {
io.on('connection', socket => {
//middleware
socket.use((request, next) => {
//verify request format
if (!['open chat', 'message'].includes(request[0])) {
if (!['open chat', 'message', 'report'].includes(request[0])) {
return next(`Invalid request to the chat server ${request[0]}`);
}
return next();
@@ -35,7 +35,7 @@ const chat = io => {
});
//from here, handles all normal messages
socket.on('open chat', message => {
socket.on('open chat', async message => {
//handle rooms - only in a room if you've opened chat
const newlyOpened = !socket.user.room;
socket.user.room = socket.user.room || 'general'; //default to general
@@ -46,11 +46,8 @@ const chat = io => {
socket.join(socket.user.room);
//broadcast to this room
socket.broadcast.to(socket.user.room).emit('message', { emphasis: true, text: `${socket.user.username} entered chat` });
//log
chatlog.create({
const log = await chatlog.create({
notification: true,
username: socket.user.username,
text: `${socket.user.username} entered chat`,
@@ -58,6 +55,9 @@ const chat = io => {
emphasis: true
});
//broadcast to this room
socket.broadcast.to(socket.user.room).emit('message', {timestamp: log.createdAt, emphasis: true, text: `${socket.user.username} entered chat` });
//send backlog to the user
chatlog.findAll({
where: {
@@ -72,7 +72,7 @@ const chat = io => {
}
},
order: [
['id', 'DESC']
['index', 'DESC']
],
limit: 50
})
@@ -109,18 +109,18 @@ const chat = io => {
return;
}
//broadcast to this room
socket.broadcast.to(socket.user.room).emit('message', { username: socket.user.username, text: message.text });
//log
chatlog.create({
const log = await chatlog.create({
username: socket.user.username,
text: message.text,
room: socket.user.room
});
//broadcast to this room (with the id)
socket.broadcast.to(socket.user.room).emit('message', log);
});
socket.on('disconnect', reason => {
socket.on('disconnect', async reason => {
//broadcast to this room
if (!socket.user) {
return;
@@ -137,6 +137,19 @@ const chat = io => {
emphasis: true
});
});
socket.on('report', info => {
//handle reports of malicious content
if (!info.index) {
return;
}
//report
reports.create({
reporter: socket.user.username,
chatlogIndex: info.index
});
});
});
};
@@ -186,7 +199,7 @@ const executeCommand = (io, socket, command) => {
}
case '/mute': {//NOTE: mutes globally, broadcasts only to admin's room
if (socket.user.privilege != 'administrator' && socket.user.privilege != 'moderator') {
if (!socket.user.admin && !socket.user.mod) {
socket.emit('message', { emphasis: true, text: '/mute is only available to admins and mods' });
break;
}
@@ -229,7 +242,7 @@ const executeCommand = (io, socket, command) => {
}
case '/unmute': {
if (socket.user.privilege != 'administrator' && socket.user.privilege != 'moderator') {
if (!socket.user.admin && !socket.user.mod) {
socket.emit('message', { emphasis: true, text: '/unmute is only available to admins and mods' });
break;
}
+1 -1
View File
@@ -2,7 +2,7 @@ const Sequelize = require('sequelize');
const sequelize = require('..');
module.exports = sequelize.define('chatlog', {
id: {
index: {
type: Sequelize.INTEGER(11),
allowNull: false,
autoIncrement: true,
+11 -3
View File
@@ -1,4 +1,12 @@
const chatlog = require('./chatlog');
const mute = require('./mute');
const reports = require('./reports');
//relationships
reports.belongsTo(chatlog);
module.exports = {
chatlog: require('./chatlog'),
mute: require('./mute')
};
chatlog,
mute,
reports,
};
+17
View File
@@ -0,0 +1,17 @@
const Sequelize = require('sequelize');
const sequelize = require('..');
module.exports = sequelize.define('reports', {
index: {
type: Sequelize.INTEGER(11),
allowNull: false,
autoIncrement: true,
primaryKey: true,
unique: true
},
reporter: {
type: 'varchar(320)',
allowNull: false
},
});
+11 -4
View File
@@ -7,19 +7,26 @@ const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server, {
cors: {
origin: '*'
origin: process.env.WEB_ORIGIN
}
});
const bodyParser = require('body-parser');
const cors = require('cors');
//config
app.use(bodyParser.json());
app.use(cors());
app.use(express.json());
app.use(cors({
credentials: true,
origin: [`${process.env.WEB_ORIGIN}`], //because auth-server
allowedHeaders: ['Origin', 'X-Requested-With', 'Content-Type', 'Accept', 'Authorization', 'Set-Cookie'],
exposedHeaders: ['Origin', 'X-Requested-With', 'Content-Type', 'Accept', 'Authorization', 'Set-Cookie'],
}));
//database connection
const database = require('./database');
//admin stuff
app.use('/admin', require('./admin'));
//access the chat
require('./chat')(io.of('/chat'));
+21
View File
@@ -0,0 +1,21 @@
const jwt = require('jsonwebtoken');
//middleware to authenticate the JWT token
module.exports = (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader?.split (' ')[1]; //'Bearer token'
if (!token) {
return res.status(401).send('No token found');
}
return jwt.verify(token, process.env.SECRET_ACCESS, (err, user) => {
if (err) {
return res.status(403).send(err);
}
req.user = user;
return next();
});
};
+4
View File
@@ -0,0 +1,4 @@
#use this while debugging
CREATE DATABASE IF NOT EXISTS chat;
CREATE USER IF NOT EXISTS 'chat'@'%' IDENTIFIED BY 'blastoise';
GRANT ALL PRIVILEGES ON chat.* TO 'chat'@'%';