Skip to content

Commit ea415c9

Browse files
committed
First commit
1 parent 712383a commit ea415c9

File tree

16 files changed

+6528
-0
lines changed

16 files changed

+6528
-0
lines changed

.dockerignore

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
**/.classpath
2+
**/.dockerignore
3+
**/.env
4+
**/.git
5+
**/.gitignore
6+
**/.project
7+
**/.settings
8+
**/.toolstarget
9+
**/.vs
10+
**/.vscode
11+
**/*.*proj.user
12+
**/*.dbmdl
13+
**/*.jfm
14+
**/charts
15+
**/docker-compose*
16+
**/compose*
17+
**/Dockerfile*
18+
**/node_modules
19+
**/npm-debug.log
20+
**/obj
21+
**/secrets.dev.yaml
22+
**/values.dev.yaml
23+
LICENSE
24+
README.md

.gitignore

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
### Node ###
2+
# Logs
3+
logs
4+
*.log
5+
npm-debug.log*
6+
yarn-debug.log*
7+
yarn-error.log*
8+
lerna-debug.log*
9+
.pnpm-debug.log*
10+
11+
# Diagnostic reports (https://nodejs.org/api/report.html)
12+
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
13+
14+
# Runtime data
15+
pids
16+
*.pid
17+
*.seed
18+
*.pid.lock
19+
20+
# Directory for instrumented libs generated by jscoverage/JSCover
21+
lib-cov
22+
23+
# node-waf configuration
24+
.lock-wscript
25+
26+
# Compiled binary addons (https://nodejs.org/api/addons.html)
27+
build/Release
28+
29+
# Dependency directories
30+
node_modules/
31+
jspm_packages/
32+
33+
# Snowpack dependency directory (https://snowpack.dev/)
34+
web_modules/
35+
36+
# TypeScript cache
37+
*.tsbuildinfo
38+
39+
# Optional npm cache directory
40+
.npm
41+
42+
# Optional eslint cache
43+
.eslintcache
44+
45+
# Optional stylelint cache
46+
.stylelintcache
47+
48+
# Optional REPL history
49+
.node_repl_history
50+
51+
# Output of 'npm pack'
52+
*.tgz
53+
54+
# Stores VSCode versions used for testing VSCode extensions
55+
.vscode-test

.vscode/launch.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"configurations": [
3+
{
4+
"name": "Docker Node.js Launch",
5+
"type": "docker",
6+
"request": "launch",
7+
"preLaunchTask": "docker-run: debug",
8+
"platform": "node"
9+
}
10+
]
11+
}

.vscode/tasks.json

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"version": "2.0.0",
3+
"tasks": [
4+
{
5+
"type": "docker-build",
6+
"label": "docker-build",
7+
"platform": "node",
8+
"dockerBuild": {
9+
"dockerfile": "${workspaceFolder}/Dockerfile",
10+
"context": "${workspaceFolder}",
11+
"pull": true
12+
}
13+
},
14+
{
15+
"type": "docker-run",
16+
"label": "docker-run: release",
17+
"dependsOn": [
18+
"docker-build"
19+
],
20+
"platform": "node"
21+
},
22+
{
23+
"type": "docker-run",
24+
"label": "docker-run: debug",
25+
"dependsOn": [
26+
"docker-build"
27+
],
28+
"dockerRun": {
29+
"env": {
30+
"DEBUG": "*",
31+
"NODE_ENV": "development"
32+
}
33+
},
34+
"node": {
35+
"enableDebugging": true
36+
}
37+
}
38+
]
39+
}

Dockerfile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
FROM node:lts-alpine
2+
ENV NODE_ENV=production
3+
WORKDIR /usr/src/app
4+
COPY ["package.json", "package-lock.json*", "./"]
5+
RUN npm install --production --silent && mv node_modules ../
6+
COPY . .
7+
EXPOSE 3000
8+
RUN chown -R node /usr/src/app
9+
USER node
10+
CMD ["node", "index.js"]

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Socket.io Sample. Simple Chat
2+
3+
This project is intended to explain and teach the use of express and socket.io in a basic level
4+
5+
## Dependencies
6+
7+
If you don't want to install anything else you can use docker only.
8+
9+
To run localy without docker use:
10+
11+
- NodeJs
12+
- ExpressJs
13+
- Jest
14+
- SQLlite
15+
16+
## Run
17+
18+
Its better to run it with docker as follows.
19+
20+
Make sure that the terminal is running in the same directory as this file and run
21+
22+
```bash
23+
docker build -t socket-qserver . && docker run -it -p 3000:3000 socket-qserver:latest
24+
````
25+
26+
Otherwise you can run ```node index.js```

config.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"basePort": 3000,
3+
"dbFileName": "./db/chat.db",
4+
"freshStart": true,
5+
"clusters": false
6+
}

db/chat.db

20 KB
Binary file not shown.

html/index.html

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<!DOCTYPE html>
2+
<html>
3+
4+
<head>
5+
<meta name="viewport" content="width=device-width,initial-scale=1.0">
6+
<title>Socket.IO chat</title>
7+
<style>
8+
body {
9+
margin: 0;
10+
padding-bottom: 3rem;
11+
background: #000000;
12+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
13+
color: #868181;
14+
}
15+
16+
#form {
17+
background: rgba(0, 0, 0, 0.15);
18+
padding: 0.25rem;
19+
position: fixed;
20+
bottom: 0;
21+
left: 0;
22+
right: 0;
23+
display: flex;
24+
height: 3rem;
25+
box-sizing: border-box;
26+
backdrop-filter: blur(10px);
27+
}
28+
29+
#input {
30+
border: none;
31+
padding: 0 1rem;
32+
flex-grow: 1;
33+
border-radius: 2rem;
34+
margin: 0.25rem;
35+
background: #333;
36+
color: #c3c3c3;
37+
}
38+
39+
#input:focus {
40+
outline: none;
41+
}
42+
43+
#form>button {
44+
background: #333;
45+
border: none;
46+
padding: 0 1rem;
47+
margin: 0.25rem;
48+
border-radius: 3px;
49+
outline: none;
50+
color: #fff;
51+
}
52+
53+
#messages {
54+
list-style-type: none;
55+
margin: 0;
56+
padding: 0;
57+
}
58+
59+
#messages>li {
60+
padding: 0.5rem 1rem;
61+
}
62+
63+
#messages>li:nth-child(odd) {
64+
background: #333232;
65+
}
66+
</style>
67+
</head>
68+
69+
<body>
70+
<ul id="messages"></ul>
71+
<form id="form" action="">
72+
<input id="input" autocomplete="off" />
73+
<button>Send</button>
74+
</form>
75+
<script src="/socket.io/socket.io.js"></script>
76+
<script>
77+
let counter = 0;
78+
const nickname = prompt("Enter your nickname:");
79+
80+
const socket = io({
81+
ackTimeout: 10000,
82+
retries: 3,
83+
auth: {
84+
serverOffset: 0
85+
}
86+
});
87+
88+
const form = document.getElementById('form');
89+
const input = document.getElementById('input');
90+
const messages = document.getElementById('messages');
91+
92+
form.addEventListener('submit', (e) => {
93+
e.preventDefault();
94+
if (input.value) {
95+
const clientOffset = `${socket.id}-${counter++}`;
96+
socket.emit('chat message', nickname, input.value, clientOffset);
97+
input.value = '';
98+
}
99+
});
100+
101+
socket.on('chat message', (nickname, msg, serverOffset) => {
102+
const item = document.createElement('li');
103+
item.textContent = nickname + " :: " + msg;
104+
messages.appendChild(item);
105+
window.scrollTo(0, document.body.scrollHeight);
106+
socket.auth.serverOffset = serverOffset;
107+
});
108+
109+
socket.on('connection', (usr) => {
110+
const item = document.createElement('li');
111+
item.textContent = usr;
112+
messages.appendChild(item);
113+
window.scrollTo(0, document.body.scrollHeight);
114+
});
115+
</script>
116+
</body>
117+
118+
</html>

index.js

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { availableParallelism } from 'node:os';
2+
import cluster from 'node:cluster';
3+
import { createServer } from 'node:http';
4+
import { fileURLToPath } from 'node:url';
5+
import { dirname, join } from 'node:path';
6+
import express from 'express';
7+
import { createAdapter, setupPrimary } from '@socket.io/cluster-adapter';
8+
import { Server } from 'socket.io';
9+
import createLogger from './src/logger.js';
10+
import createConfig from './src/config/config-mgr.js';
11+
import createDB from './src/db.js';
12+
13+
/**
14+
* The logger instance
15+
*
16+
*/
17+
const logger = createLogger('server');
18+
/**
19+
* Configuration instance
20+
*
21+
*/
22+
const config = await createConfig();
23+
/**
24+
* Database connection instance
25+
*
26+
*/
27+
const database = await createDB(config);
28+
29+
/**
30+
* The main entry point of the application
31+
* if the clusters are enabled, the primary process will fork the workers
32+
* otherwise, the server will run in a single process
33+
*/
34+
if (cluster.isPrimary && config.clusters) {
35+
const numCPUs = availableParallelism();
36+
database.init();
37+
for (let i = 0; i < numCPUs; i++) {
38+
cluster.fork({
39+
PORT: config.basePort + i
40+
});
41+
}
42+
setupPrimary();
43+
} else {
44+
45+
const app = express();
46+
const server = createServer(app);
47+
const io = new Server(server, {
48+
connectionStateRecovery: {},
49+
adapter: createAdapter()
50+
});
51+
52+
const __dirname = dirname(fileURLToPath(import.meta.url));
53+
54+
app.get('/', (req, res) => {
55+
res.sendFile(join(__dirname, 'html/index.html'));
56+
});
57+
58+
io.on('connection', async (socket) => {
59+
logger.debug('new user connected');
60+
io.emit('connection', "new user connected");
61+
socket.on('chat message', async (nickname, msg, clientOffset, callback) => {
62+
logger.chat(nickname, msg);
63+
const result = database.insertMessage(nickname, msg, clientOffset);
64+
io.emit('chat message',nickname, msg, result.lastID);
65+
callback();
66+
socket.on('disconnect', () => {
67+
io.emit('connection', "user disconnected");
68+
logger.debug('user disconnected');
69+
});
70+
});
71+
72+
if (!socket.recovered) {
73+
logger.debug('socket not recovered');
74+
database.retreiveMesaages((nickname, msg, id) => {
75+
socket.emit('chat message',nickname, msg, id);
76+
});
77+
}
78+
});
79+
80+
server.listen(process.env.PORT, () => {
81+
logger.highlight(`Running at http://localhost:${process.env.PORT}`);
82+
}).on('error', (e) => {
83+
logger.error('Error starting server', e);
84+
});
85+
}

0 commit comments

Comments
 (0)