feat: add javascript linter format
This commit is contained in:
parent
3b3aaf29af
commit
7ce9f2dd93
13 changed files with 1561 additions and 1557 deletions
3
.prettierrc
Normal file
3
.prettierrc
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"singleQuote": true
|
||||||
|
}
|
|
@ -2,4 +2,4 @@
|
||||||
|
|
||||||
Web-based memos that support real-time collaboration. Inspired by scrumblr.
|
Web-based memos that support real-time collaboration. Inspired by scrumblr.
|
||||||
|
|
||||||
test Léo
|
Run 'bun install' for installing deps and 'redis-server --daemonize yes && bun run start' to start the servers
|
||||||
|
|
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
945
client/script.js
945
client/script.js
File diff suppressed because it is too large
Load diff
31
config.js
31
config.js
|
@ -1,4 +1,4 @@
|
||||||
/*exports.database = {
|
/* exports.database = {
|
||||||
type: 'mongodb',
|
type: 'mongodb',
|
||||||
hostname: 'localhost',
|
hostname: 'localhost',
|
||||||
port: 27017,
|
port: 27017,
|
||||||
|
@ -6,7 +6,7 @@
|
||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var argv = require("yargs").usage(
|
const { argv } = require('yargs').usage(
|
||||||
'Usage: $0 [--port INTEGER [8080]] \
|
'Usage: $0 [--port INTEGER [8080]] \
|
||||||
[--baseurl STRING ["/"]] \
|
[--baseurl STRING ["/"]] \
|
||||||
[--redis STRING:INT [127.0.0.1:6379]] \
|
[--redis STRING:INT [127.0.0.1:6379]] \
|
||||||
|
@ -16,28 +16,27 @@ var argv = require("yargs").usage(
|
||||||
[--logoUrl STRING] \
|
[--logoUrl STRING] \
|
||||||
[--faviconUrl STRING] \ '
|
[--faviconUrl STRING] \ '
|
||||||
|
|
||||||
|
)
|
||||||
).argv
|
|
||||||
|
|
||||||
exports.server = {
|
exports.server = {
|
||||||
port: argv.port || 8080,
|
port: argv.port || 8080,
|
||||||
baseurl: argv.baseurl || "/",
|
baseurl: argv.baseurl || '/'
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.googleanalytics = {
|
exports.googleanalytics = {
|
||||||
enabled: argv["gaEnabled"] || false,
|
enabled: argv.gaEnabled || false,
|
||||||
account: argv["gaAccount"] || "UA-2069672-4",
|
account: argv.gaAccount || 'UA-2069672-4'
|
||||||
}
|
}
|
||||||
|
|
||||||
var redis_conf = argv.redis || "127.0.0.1:6379"
|
const redis_conf = argv.redis || '127.0.0.1:6379'
|
||||||
exports.database = {
|
exports.database = {
|
||||||
sock: argv["sock"] || false,
|
sock: argv.sock || false,
|
||||||
type: "redis",
|
type: 'redis',
|
||||||
prefix: "#scrumblr#",
|
prefix: '#scrumblr#',
|
||||||
host: redis_conf.split(":")[0] || "127.0.0.1",
|
host: redis_conf.split(':')[0] || '127.0.0.1',
|
||||||
port: redis_conf.split(":")[1] || 6379,
|
port: redis_conf.split(':')[1] || 6379
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.headerBarUrl = argv['headerBarUrl'] || null /* example url with appropriate json markup : 'https://colibris-lemouvement.org/archipel-markup?domain=colibris-outilslibres.org' */
|
exports.headerBarUrl = argv.headerBarUrl || null /* example url with appropriate json markup : 'https://colibris-lemouvement.org/archipel-markup?domain=colibris-outilslibres.org' */
|
||||||
exports.logoUrl = argv['logoUrl'] || null /* example logo url : 'https://postit.colibris-outilslibres.org/images/logo-Post-it.svg' */
|
exports.logoUrl = argv.logoUrl || null /* example logo url : 'https://postit.colibris-outilslibres.org/images/logo-Post-it.svg' */
|
||||||
exports.faviconUrl = argv['faviconUrl'] || null /* example favicon url : 'https://postit.colibris-outilslibres.org/images/favicon.png' */
|
exports.faviconUrl = argv.faviconUrl || null /* example favicon url : 'https://postit.colibris-outilslibres.org/images/favicon.png' */
|
||||||
|
|
49
eslint.config.mjs
Normal file
49
eslint.config.mjs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import { defineConfig, globalIgnores } from 'eslint/config'
|
||||||
|
import globals from 'globals'
|
||||||
|
import path from 'node:path'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
import js from '@eslint/js'
|
||||||
|
import { FlatCompat } from '@eslint/eslintrc'
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url)
|
||||||
|
const __dirname = path.dirname(__filename)
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: __dirname,
|
||||||
|
recommendedConfig: js.configs.recommended,
|
||||||
|
allConfig: js.configs.all
|
||||||
|
})
|
||||||
|
|
||||||
|
export default defineConfig([globalIgnores([
|
||||||
|
'node_modules',
|
||||||
|
'client/lib'
|
||||||
|
]), {
|
||||||
|
extends: compat.extends('airbnb-base'),
|
||||||
|
|
||||||
|
languageOptions: { ecmaVersion: 13 },
|
||||||
|
|
||||||
|
rules: {
|
||||||
|
semi: ['error', 'never'],
|
||||||
|
|
||||||
|
'max-len': ['error', { code: 104 }],
|
||||||
|
|
||||||
|
'vars-on-top': 'off',
|
||||||
|
'class-methods-use-this': 'off',
|
||||||
|
'import/no-unresolved': 'off',
|
||||||
|
'import/extensions': ['error', 'always'],
|
||||||
|
'import/prefer-default-export': ['off'],
|
||||||
|
'no-use-before-define': ['off'],
|
||||||
|
eqeqeq: ['error', 'smart'],
|
||||||
|
'comma-dangle': ['error', 'never'],
|
||||||
|
|
||||||
|
'object-curly-newline': ['error', { multiline: true }],
|
||||||
|
|
||||||
|
'func-names': ['error', 'never'],
|
||||||
|
'space-before-function-paren': ['error', 'never'],
|
||||||
|
|
||||||
|
'lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: true }],
|
||||||
|
|
||||||
|
'no-new': 'off',
|
||||||
|
'no-restricted-syntax': 'off',
|
||||||
|
'guard-for-in': 'off'
|
||||||
|
}
|
||||||
|
}])
|
|
@ -1,6 +1,6 @@
|
||||||
var conf = require('../config.js').database;
|
const conf = require('../config.js').database
|
||||||
|
|
||||||
exports.db = require('./data/'+conf.type+'.js').db;
|
exports.db = require(`./data/${conf.type}.js`).db
|
||||||
|
|
||||||
/*
|
/*
|
||||||
var db = function(callback) { }
|
var db = function(callback) { }
|
||||||
|
|
|
@ -1,195 +1,181 @@
|
||||||
var Db = require('mongodb').Db;
|
const { Db } = require('mongodb')
|
||||||
Server = require('mongodb').Server,
|
Server = require('mongodb').Server,
|
||||||
BSON = require('mongodb').BSONNative,
|
BSON = require('mongodb').BSONNative,
|
||||||
conf = require('../../config.js').database;
|
conf = require('../../config.js').database
|
||||||
|
|
||||||
var db = function(callback)
|
const db = function(callback) {
|
||||||
{
|
this.rooms = false
|
||||||
this.rooms = false;
|
const t = this
|
||||||
var t = this;
|
|
||||||
|
|
||||||
var db = new Db(conf.database, new Server(conf.hostname, conf.port), {native_parser:true});
|
const db = new Db(conf.database, new Server(conf.hostname, conf.port), { native_parser: true })
|
||||||
db.open(function(err, db) {
|
db.open((err, db) => {
|
||||||
db.collection('rooms', function(err, collection) {
|
db.collection('rooms', (err, collection) => {
|
||||||
// make sure we have an index on name
|
// make sure we have an index on name
|
||||||
collection.ensureIndex([['name',1]],false,function() {});
|
collection.ensureIndex([['name', 1]], false, () => {})
|
||||||
t.rooms = collection;
|
t.rooms = collection
|
||||||
});
|
})
|
||||||
callback();
|
callback()
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
db.prototype = {
|
db.prototype = {
|
||||||
clearRoom: function(room, callback)
|
clearRoom(room, callback) {
|
||||||
{
|
this.rooms.remove({ name: room }, callback)
|
||||||
this.rooms.remove({name:room},callback);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// theme commands
|
// theme commands
|
||||||
setTheme: function(room, theme)
|
setTheme(room, theme) {
|
||||||
{
|
|
||||||
this.rooms.update(
|
this.rooms.update(
|
||||||
{name:room},
|
{ name: room },
|
||||||
{$set:{theme:theme}}
|
{ $set: { theme } }
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
getTheme: function(room, callback)
|
getTheme(room, callback) {
|
||||||
{
|
|
||||||
this.rooms.findOne(
|
this.rooms.findOne(
|
||||||
{name:room},
|
{ name: room },
|
||||||
{theme:true},
|
{ theme: true },
|
||||||
function(err, room) {
|
(err, room) => {
|
||||||
if(room) {
|
if (room) {
|
||||||
callback(room.theme);
|
callback(room.theme)
|
||||||
} else {
|
} else {
|
||||||
callback();
|
callback()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
// revision commands
|
// revision commands
|
||||||
setRevisions: function(room, revisions) {
|
setRevisions(room, revisions) {
|
||||||
this.rooms.update(
|
this.rooms.update(
|
||||||
{name:room},
|
{ name: room },
|
||||||
{$set:{revisions:revisions}}
|
{ $set: { revisions } }
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
getRevisions: function(room, callback) {
|
getRevisions(room, callback) {
|
||||||
this.rooms.findOne(
|
this.rooms.findOne(
|
||||||
{name:room},
|
{ name: room },
|
||||||
{revisions:true},
|
{ revisions: true },
|
||||||
function(err, room) {
|
(err, room) => {
|
||||||
if(room) {
|
if (room) {
|
||||||
callback(room.revisions);
|
callback(room.revisions)
|
||||||
} else {
|
} else {
|
||||||
callback();
|
callback()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Column commands
|
// Column commands
|
||||||
createColumn: function(room, name, callback)
|
createColumn(room, name, callback) {
|
||||||
{
|
|
||||||
this.rooms.update(
|
this.rooms.update(
|
||||||
{name:room},
|
{ name: room },
|
||||||
{$push:{columns:name}},
|
{ $push: { columns: name } },
|
||||||
{upsert:true}
|
{ upsert: true },
|
||||||
,callback
|
callback
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
getAllColumns: function(room, callback)
|
getAllColumns(room, callback) {
|
||||||
{
|
this.rooms.findOne({ name: room }, { columns: true }, (err, room) => {
|
||||||
this.rooms.findOne({name:room},{columns:true},function(err, room) {
|
if (room) {
|
||||||
if(room) {
|
callback(room.columns)
|
||||||
callback(room.columns);
|
|
||||||
} else {
|
} else {
|
||||||
callback();
|
callback()
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteColumn: function(room)
|
deleteColumn(room) {
|
||||||
{
|
|
||||||
this.rooms.update(
|
this.rooms.update(
|
||||||
{name:room},
|
{ name: room },
|
||||||
{$pop:{columns:1}}
|
{ $pop: { columns: 1 } }
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
setColumns: function(room, columns)
|
setColumns(room, columns) {
|
||||||
{
|
|
||||||
this.rooms.update(
|
this.rooms.update(
|
||||||
{name:room},
|
{ name: room },
|
||||||
{$set:{columns:columns}},
|
{ $set: { columns } },
|
||||||
{upsert:true}
|
{ upsert: true }
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Card commands
|
// Card commands
|
||||||
createCard: function(room, id, card)
|
createCard(room, id, card) {
|
||||||
{
|
const doc = {}
|
||||||
var doc = {};
|
doc[`cards.${id}`] = card
|
||||||
doc['cards.'+id] = card;
|
|
||||||
this.rooms.update(
|
this.rooms.update(
|
||||||
{name:room},
|
{ name: room },
|
||||||
{$set:doc},
|
{ $set: doc },
|
||||||
{upsert:true}
|
{ upsert: true }
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
getAllCards: function(room, callback)
|
getAllCards(room, callback) {
|
||||||
{
|
this.rooms.findOne({ name: room }, { cards: true }, (err, room) => {
|
||||||
this.rooms.findOne({name:room},{cards:true},function(err, room) {
|
if (room) {
|
||||||
if(room) {
|
callback(room.cards)
|
||||||
callback(room.cards);
|
|
||||||
} else {
|
} else {
|
||||||
callback();
|
callback()
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
cardEdit: function(room, id, text)
|
cardEdit(room, id, text) {
|
||||||
{
|
const doc = {}
|
||||||
var doc = {};
|
doc[`cards.${id}.text`] = text
|
||||||
doc['cards.'+id+'.text'] = text;
|
|
||||||
this.rooms.update(
|
this.rooms.update(
|
||||||
{name:room},
|
{ name: room },
|
||||||
{$set:doc}
|
{ $set: doc }
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
cardSetXY: function(room, id, x, y)
|
cardSetXY(room, id, x, y) {
|
||||||
{
|
const doc = {}
|
||||||
var doc = {};
|
doc[`cards.${id}.x`] = x
|
||||||
doc['cards.'+id+'.x'] = x;
|
doc[`cards.${id}.y`] = y
|
||||||
doc['cards.'+id+'.y'] = y;
|
|
||||||
this.rooms.update(
|
this.rooms.update(
|
||||||
{name:room},
|
{ name: room },
|
||||||
{$set:doc}
|
{ $set: doc }
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteCard: function(room, id)
|
deleteCard(room, id) {
|
||||||
{
|
const doc = {}
|
||||||
var doc = {};
|
doc[`cards.${id}`] = true
|
||||||
doc['cards.'+id] = true;
|
|
||||||
this.rooms.update(
|
this.rooms.update(
|
||||||
{name:room},
|
{ name: room },
|
||||||
{$unset:doc}
|
{ $unset: doc }
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
addSticker: function(room, cardId, stickerId)
|
addSticker(room, cardId, stickerId) {
|
||||||
{
|
const doc = {}
|
||||||
var doc = {};
|
doc[`cards.${cardId}.sticker`] = stickerId
|
||||||
doc['cards.'+cardId+'.sticker'] = stickerId;
|
|
||||||
this.rooms.update(
|
this.rooms.update(
|
||||||
{name:room},
|
{ name: room },
|
||||||
{$set:doc}
|
{ $set: doc }
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
getBoardSize: function(room, callback) {
|
getBoardSize(room, callback) {
|
||||||
this.rooms.findOne(
|
this.rooms.findOne(
|
||||||
{name:room},
|
{ name: room },
|
||||||
function(err, room) {
|
(err, room) => {
|
||||||
if(room) {
|
if (room) {
|
||||||
callback(room.size);
|
callback(room.size)
|
||||||
} else {
|
} else {
|
||||||
callback();
|
callback()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
setBoardSize: function(room, size) {
|
setBoardSize(room, size) {
|
||||||
this.rooms.update(
|
this.rooms.update(
|
||||||
{name:room},
|
{ name: room },
|
||||||
{$set:{'size':size}}
|
{ $set: { size } }
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
exports.db = db;
|
exports.db = db
|
||||||
|
|
|
@ -1,202 +1,197 @@
|
||||||
var conf = require('../../config.js').database;
|
const conf = require('../../config.js').database
|
||||||
|
|
||||||
var redis = require("redis"),
|
const redis = require('redis')
|
||||||
redisClient = null; //redis.createClient();
|
|
||||||
|
|
||||||
var async = require("async");
|
let redisClient = null // redis.createClient();
|
||||||
var sets = require('simplesets');
|
|
||||||
|
const async = require('async')
|
||||||
|
const sets = require('simplesets')
|
||||||
|
|
||||||
// If you want Memory Store instead...
|
// If you want Memory Store instead...
|
||||||
// var MemoryStore = require('connect/middleware/session/memory');
|
// var MemoryStore = require('connect/middleware/session/memory');
|
||||||
// var session_store = new MemoryStore();
|
// var session_store = new MemoryStore();
|
||||||
|
|
||||||
var REDIS_PREFIX = '#scrumblr#';
|
const REDIS_PREFIX = '#scrumblr#'
|
||||||
|
|
||||||
//For Redis Debugging
|
// For Redis Debugging
|
||||||
|
|
||||||
|
const db = function(callback) {
|
||||||
var db = function(callback) {
|
|
||||||
if (conf.sock) {
|
if (conf.sock) {
|
||||||
console.log('Opening redis connection to socket ' + conf.host);
|
console.log(`Opening redis connection to socket ${conf.host}`)
|
||||||
redisClient = redis.createClient(conf.host);
|
redisClient = redis.createClient(conf.host)
|
||||||
} else {
|
} else {
|
||||||
console.log('Opening redis connection to ' + conf.host + ':' + conf.port);
|
console.log(`Opening redis connection to ${conf.host}:${conf.port}`)
|
||||||
redisClient = redis.createClient(conf.port, conf.host, {});
|
redisClient = redis.createClient(conf.port, conf.host, {})
|
||||||
}
|
}
|
||||||
redisClient.on("connect", function (err) {
|
redisClient.on('connect', (err) => {
|
||||||
callback();
|
callback()
|
||||||
});
|
})
|
||||||
|
|
||||||
redisClient.on("error", function (err) {
|
redisClient.on('error', (err) => {
|
||||||
console.log("Redis error: " + err);
|
console.log(`Redis error: ${err}`)
|
||||||
});
|
})
|
||||||
|
}
|
||||||
};
|
|
||||||
|
|
||||||
db.prototype = {
|
db.prototype = {
|
||||||
clearRoom: function(room, callback) {
|
clearRoom(room, callback) {
|
||||||
redisClient.del(REDIS_PREFIX + '-room:/demo-cards', function (err, res) {
|
redisClient.del(`${REDIS_PREFIX}-room:/demo-cards`, (err, res) => {
|
||||||
redisClient.del(REDIS_PREFIX + '-room:/demo-columns', function (err, res) {
|
redisClient.del(`${REDIS_PREFIX}-room:/demo-columns`, (err, res) => {
|
||||||
callback();
|
callback()
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// theme commands
|
// theme commands
|
||||||
setTheme: function(room, theme) {
|
setTheme(room, theme) {
|
||||||
redisClient.set(REDIS_PREFIX + '-room:' + room + '-theme', theme);
|
redisClient.set(`${REDIS_PREFIX}-room:${room}-theme`, theme)
|
||||||
},
|
},
|
||||||
|
|
||||||
getTheme: function(room, callback) {
|
getTheme(room, callback) {
|
||||||
redisClient.get(REDIS_PREFIX + '-room:' + room + '-theme', function (err, res) {
|
redisClient.get(`${REDIS_PREFIX}-room:${room}-theme`, (err, res) => {
|
||||||
callback(res);
|
callback(res)
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// revision commands
|
// revision commands
|
||||||
setRevisions: function(room, revisions) {
|
setRevisions(room, revisions) {
|
||||||
if (Object.keys(revisions).length === 0) {
|
if (Object.keys(revisions).length === 0) {
|
||||||
redisClient.del(REDIS_PREFIX + '-room:' + room + '-revisions');
|
redisClient.del(`${REDIS_PREFIX}-room:${room}-revisions`)
|
||||||
} else {
|
} else {
|
||||||
redisClient.set(REDIS_PREFIX + '-room:' + room + '-revisions', JSON.stringify(revisions));
|
redisClient.set(`${REDIS_PREFIX}-room:${room}-revisions`, JSON.stringify(revisions))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getRevisions: function(room, callback) {
|
getRevisions(room, callback) {
|
||||||
redisClient.get(REDIS_PREFIX + '-room:' + room + '-revisions', function (err, res) {
|
redisClient.get(`${REDIS_PREFIX}-room:${room}-revisions`, (err, res) => {
|
||||||
callback(JSON.parse(res));
|
callback(JSON.parse(res))
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// Column commands
|
// Column commands
|
||||||
createColumn: function(room, name, callback) {
|
createColumn(room, name, callback) {
|
||||||
redisClient.rpush(REDIS_PREFIX + '-room:' + room + '-columns', name,
|
redisClient.rpush(
|
||||||
function (err, res) {
|
`${REDIS_PREFIX}-room:${room}-columns`,
|
||||||
if (typeof callback != "undefined" && callback !== null) callback();
|
name,
|
||||||
|
(err, res) => {
|
||||||
|
if (typeof callback != 'undefined' && callback !== null) callback()
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
getAllColumns: function(room, callback) {
|
getAllColumns(room, callback) {
|
||||||
redisClient.lrange(REDIS_PREFIX + '-room:' + room + '-columns', 0, -1, function(err, res) {
|
redisClient.lrange(`${REDIS_PREFIX}-room:${room}-columns`, 0, -1, (err, res) => {
|
||||||
callback(res);
|
callback(res)
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteColumn: function(room) {
|
deleteColumn(room) {
|
||||||
redisClient.rpop(REDIS_PREFIX + '-room:' + room + '-columns');
|
redisClient.rpop(`${REDIS_PREFIX}-room:${room}-columns`)
|
||||||
},
|
},
|
||||||
|
|
||||||
setColumns: function(room, columns) {
|
setColumns(room, columns) {
|
||||||
//1. first delete all columns
|
// 1. first delete all columns
|
||||||
redisClient.del(REDIS_PREFIX + '-room:' + room + '-columns', function () {
|
redisClient.del(`${REDIS_PREFIX}-room:${room}-columns`, () => {
|
||||||
//2. now add columns for each thingy
|
// 2. now add columns for each thingy
|
||||||
async.forEachSeries(
|
async.forEachSeries(
|
||||||
columns,
|
columns,
|
||||||
function( item, callback ) {
|
(item, callback) => {
|
||||||
//console.log('rpush: ' + REDIS_PREFIX + '-room:' + room + '-columns' + ' -- ' + item);
|
// console.log('rpush: ' + REDIS_PREFIX + '-room:' + room + '-columns' + ' -- ' + item);
|
||||||
redisClient.rpush(REDIS_PREFIX + '-room:' + room + '-columns', item,
|
redisClient.rpush(
|
||||||
function (err, res) {
|
`${REDIS_PREFIX}-room:${room}-columns`,
|
||||||
callback();
|
item,
|
||||||
|
(err, res) => {
|
||||||
|
callback()
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
function() {
|
() => {
|
||||||
//this happens when the series is complete
|
// this happens when the series is complete
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
// Card commands
|
// Card commands
|
||||||
createCard: function(room, id, card) {
|
createCard(room, id, card) {
|
||||||
var cardString = JSON.stringify(card);
|
const cardString = JSON.stringify(card)
|
||||||
redisClient.hset(
|
redisClient.hset(
|
||||||
REDIS_PREFIX + '-room:' + room + '-cards',
|
`${REDIS_PREFIX}-room:${room}-cards`,
|
||||||
id,
|
id,
|
||||||
cardString
|
cardString
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
getAllCards: function(room, callback) {
|
getAllCards(room, callback) {
|
||||||
redisClient.hgetall(REDIS_PREFIX + '-room:' + room + '-cards', function (err, res) {
|
redisClient.hgetall(`${REDIS_PREFIX}-room:${room}-cards`, (err, res) => {
|
||||||
|
const cards = []
|
||||||
|
|
||||||
var cards = [];
|
for (const i in res) {
|
||||||
|
cards.push(JSON.parse(res[i]))
|
||||||
for (var i in res) {
|
|
||||||
cards.push( JSON.parse(res[i]) );
|
|
||||||
}
|
}
|
||||||
//console.dir(cards);
|
// console.dir(cards);
|
||||||
|
|
||||||
callback(cards);
|
callback(cards)
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
cardEdit: function(room, id, text) {
|
cardEdit(room, id, text) {
|
||||||
redisClient.hget(REDIS_PREFIX + '-room:' + room + '-cards', id, function(err, res) {
|
redisClient.hget(`${REDIS_PREFIX}-room:${room}-cards`, id, (err, res) => {
|
||||||
var card = JSON.parse(res);
|
const card = JSON.parse(res)
|
||||||
if (card !== null) {
|
if (card !== null) {
|
||||||
card.text = text;
|
card.text = text
|
||||||
redisClient.hset(REDIS_PREFIX + '-room:' + room + '-cards', id, JSON.stringify(card));
|
redisClient.hset(`${REDIS_PREFIX}-room:${room}-cards`, id, JSON.stringify(card))
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
cardSetXY: function(room, id, x, y) {
|
cardSetXY(room, id, x, y) {
|
||||||
redisClient.hget(REDIS_PREFIX + '-room:' + room + '-cards', id, function(err, res) {
|
redisClient.hget(`${REDIS_PREFIX}-room:${room}-cards`, id, (err, res) => {
|
||||||
var card = JSON.parse(res);
|
const card = JSON.parse(res)
|
||||||
if (card !== null) {
|
if (card !== null) {
|
||||||
card.x = x;
|
card.x = x
|
||||||
card.y = y;
|
card.y = y
|
||||||
redisClient.hset(REDIS_PREFIX + '-room:' + room + '-cards', id, JSON.stringify(card));
|
redisClient.hset(`${REDIS_PREFIX}-room:${room}-cards`, id, JSON.stringify(card))
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteCard: function(room, id) {
|
deleteCard(room, id) {
|
||||||
redisClient.hdel(
|
redisClient.hdel(
|
||||||
REDIS_PREFIX + '-room:' + room + '-cards',
|
`${REDIS_PREFIX}-room:${room}-cards`,
|
||||||
id
|
id
|
||||||
);
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
addSticker: function(room, cardId, stickerId) {
|
addSticker(room, cardId, stickerId) {
|
||||||
redisClient.hget(REDIS_PREFIX + '-room:' + room + '-cards', cardId, function(err, res) {
|
redisClient.hget(`${REDIS_PREFIX}-room:${room}-cards`, cardId, (err, res) => {
|
||||||
var card = JSON.parse(res);
|
const card = JSON.parse(res)
|
||||||
if (card !== null) {
|
if (card !== null) {
|
||||||
if (stickerId === "nosticker")
|
if (stickerId === 'nosticker') {
|
||||||
{
|
card.sticker = null
|
||||||
card.sticker = null;
|
|
||||||
|
|
||||||
redisClient.hset(REDIS_PREFIX + '-room:' + room + '-cards', cardId, JSON.stringify(card));
|
redisClient.hset(`${REDIS_PREFIX}-room:${room}-cards`, cardId, JSON.stringify(card))
|
||||||
|
} else {
|
||||||
|
if (card.sticker !== null) { stickerSet = new sets.Set(card.sticker) } else { stickerSet = new sets.Set() }
|
||||||
|
|
||||||
|
stickerSet.add(stickerId)
|
||||||
|
|
||||||
|
card.sticker = stickerSet.array()
|
||||||
|
|
||||||
|
redisClient.hset(`${REDIS_PREFIX}-room:${room}-cards`, cardId, JSON.stringify(card))
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
if (card.sticker !== null)
|
|
||||||
stickerSet = new sets.Set( card.sticker );
|
|
||||||
else
|
|
||||||
stickerSet = new sets.Set();
|
|
||||||
|
|
||||||
stickerSet.add(stickerId);
|
|
||||||
|
|
||||||
card.sticker = stickerSet.array();
|
|
||||||
|
|
||||||
redisClient.hset(REDIS_PREFIX + '-room:' + room + '-cards', cardId, JSON.stringify(card));
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setBoardSize: function(room, size) {
|
setBoardSize(room, size) {
|
||||||
redisClient.set(REDIS_PREFIX + '-room:' + room + '-size', JSON.stringify(size));
|
redisClient.set(`${REDIS_PREFIX}-room:${room}-size`, JSON.stringify(size))
|
||||||
},
|
},
|
||||||
|
|
||||||
getBoardSize: function(room, callback) {
|
getBoardSize(room, callback) {
|
||||||
redisClient.get(REDIS_PREFIX + '-room:' + room + '-size', function (err, res) {
|
redisClient.get(`${REDIS_PREFIX}-room:${room}-size`, (err, res) => {
|
||||||
callback(JSON.parse(res));
|
callback(JSON.parse(res))
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
}
|
||||||
exports.db = db;
|
exports.db = db
|
||||||
|
|
239
lib/rooms.js
239
lib/rooms.js
|
@ -3,153 +3,137 @@
|
||||||
|
|
||||||
// PubSubCore: Simple pub/sub library for Node.js and Socket.IO
|
// PubSubCore: Simple pub/sub library for Node.js and Socket.IO
|
||||||
|
|
||||||
var util = require('util');
|
const util = require('util')
|
||||||
var sets = require('simplesets');
|
const sets = require('simplesets')
|
||||||
var io = require('socket.io');
|
const io = require('socket.io')
|
||||||
var net = require('net');
|
const net = require('net')
|
||||||
|
|
||||||
//////////////////////////////
|
/// ///////////////////////////
|
||||||
// Tracking who's in what room
|
// Tracking who's in what room
|
||||||
//////////////////////////////
|
/// ///////////////////////////
|
||||||
|
|
||||||
// Dict mapping room names with people to sets of client objects.
|
// Dict mapping room names with people to sets of client objects.
|
||||||
var rooms = {};
|
const rooms = {}
|
||||||
// Dict mapping room names with people to sets of usernames.
|
// Dict mapping room names with people to sets of usernames.
|
||||||
var room_users = {};
|
const room_users = {}
|
||||||
// Dict mapping sids to sets of rooms.
|
// Dict mapping sids to sets of rooms.
|
||||||
var sid_rooms = {};
|
const sid_rooms = {}
|
||||||
|
|
||||||
|
|
||||||
// Add a client to a room and return the sid:client mapping.
|
// Add a client to a room and return the sid:client mapping.
|
||||||
exports.add_to_room = function (client, room, callback) {
|
exports.add_to_room = function(client, room, callback) {
|
||||||
//console.log('Client ' + client.username + ' (' + client.id + ') added to room ' + room);
|
// console.log('Client ' + client.username + ' (' + client.id + ') added to room ' + room);
|
||||||
|
|
||||||
if (!(sid_rooms.hasOwnProperty(client.id))) sid_rooms[client.id] = new sets.Set();
|
if (!(sid_rooms.hasOwnProperty(client.id))) sid_rooms[client.id] = new sets.Set()
|
||||||
sid_rooms[client.id].add(room);
|
sid_rooms[client.id].add(room)
|
||||||
|
|
||||||
if (!(rooms.hasOwnProperty(room))) rooms[room] = new sets.Set();
|
if (!(rooms.hasOwnProperty(room))) rooms[room] = new sets.Set()
|
||||||
rooms[room].add(client);
|
rooms[room].add(client)
|
||||||
|
|
||||||
if (!(room_users.hasOwnProperty(room))) room_users[room] = new sets.Set();
|
if (!(room_users.hasOwnProperty(room))) room_users[room] = new sets.Set()
|
||||||
room_users[room].add(client.username);
|
room_users[room].add(client.username)
|
||||||
|
|
||||||
callback(rooms[room].array());
|
callback(rooms[room].array())
|
||||||
};
|
}
|
||||||
|
|
||||||
// Remove a client from all rooms and return the username:client
|
// Remove a client from all rooms and return the username:client
|
||||||
// mapping for everybody in those rooms.
|
// mapping for everybody in those rooms.
|
||||||
exports.remove_from_all_rooms = function (client, callback) {
|
exports.remove_from_all_rooms = function(client, callback) {
|
||||||
var affected_clients = new sets.Set();
|
const affected_clients = new sets.Set()
|
||||||
if (sid_rooms.hasOwnProperty(client.id)) {
|
if (sid_rooms.hasOwnProperty(client.id)) {
|
||||||
var client_rooms = sid_rooms[client.id].array();
|
const client_rooms = sid_rooms[client.id].array()
|
||||||
for (var i = 0; i < client_rooms.length; i++) {
|
for (let i = 0; i < client_rooms.length; i++) {
|
||||||
var room = client_rooms[i];
|
const room = client_rooms[i]
|
||||||
if (rooms.hasOwnProperty(room)) {
|
if (rooms.hasOwnProperty(room)) {
|
||||||
rooms[room].remove(client);
|
rooms[room].remove(client)
|
||||||
if (rooms[room].size() === 0)
|
if (rooms[room].size() === 0) { delete rooms[room] }
|
||||||
delete rooms[room];
|
|
||||||
}
|
}
|
||||||
if (room_users.hasOwnProperty(room)) {
|
if (room_users.hasOwnProperty(room)) {
|
||||||
room_users[room].remove(client.username);
|
room_users[room].remove(client.username)
|
||||||
if (room_users[room].size() === 0)
|
if (room_users[room].size() === 0) { delete room_users[room] }
|
||||||
delete room_users[room];
|
|
||||||
}
|
}
|
||||||
if (rooms.hasOwnProperty(room)) {
|
if (rooms.hasOwnProperty(room)) {
|
||||||
var this_room = rooms[room].array();
|
const this_room = rooms[room].array()
|
||||||
for (var j = 0; j < this_room.length; j++)
|
for (let j = 0; j < this_room.length; j++) { affected_clients.add(this_room[j]) }
|
||||||
affected_clients.add(this_room[j]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//console.log('Client ' + client.username + ' (' + client.id + ') disconnected.');
|
// console.log('Client ' + client.username + ' (' + client.id + ') disconnected.');
|
||||||
delete sid_rooms[client.id];
|
delete sid_rooms[client.id]
|
||||||
callback(affected_clients.array());
|
callback(affected_clients.array())
|
||||||
};
|
}
|
||||||
|
|
||||||
// Remove a client from a room and return the username:client mapping
|
// Remove a client from a room and return the username:client mapping
|
||||||
// for everybody in that room. Returns [] if the room does not exist,
|
// for everybody in that room. Returns [] if the room does not exist,
|
||||||
// or if the client was not in the room to begin with.
|
// or if the client was not in the room to begin with.
|
||||||
function remove_from_room(client, room, callback) {
|
function remove_from_room(client, room, callback) {
|
||||||
if (!rooms.hasOwnProperty(room) || !rooms[room].has(client)) {
|
if (!rooms.hasOwnProperty(room) || !rooms[room].has(client)) {
|
||||||
callback([]);
|
callback([])
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete from the room
|
// Delete from the room
|
||||||
rooms[room].remove(client);
|
rooms[room].remove(client)
|
||||||
if (rooms[room].size() === 0)
|
if (rooms[room].size() === 0) { delete rooms[room] }
|
||||||
delete rooms[room];
|
|
||||||
if (room_users.hasOwnProperty(room)) {
|
if (room_users.hasOwnProperty(room)) {
|
||||||
room_users[room].remove(client.username);
|
room_users[room].remove(client.username)
|
||||||
if (room_users[room].size() === 0)
|
if (room_users[room].size() === 0) { delete room_users[room] }
|
||||||
delete room_users[room];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(exports.room_clients(room));
|
callback(exports.room_clients(room))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return list of clients in the given room.
|
// Return list of clients in the given room.
|
||||||
exports.room_clients = function(room) {
|
exports.room_clients = function(room) {
|
||||||
return rooms.hasOwnProperty(room) ? rooms[room].array() : [];
|
return rooms.hasOwnProperty(room) ? rooms[room].array() : []
|
||||||
};
|
}
|
||||||
|
|
||||||
// Return true if room contains the given client, false otherwise.
|
// Return true if room contains the given client, false otherwise.
|
||||||
exports.client_in_room = function(room, client) {
|
exports.client_in_room = function(room, client) {
|
||||||
return rooms.hasOwnProperty(room) && rooms[room].has(client);
|
return rooms.hasOwnProperty(room) && rooms[room].has(client)
|
||||||
};
|
}
|
||||||
|
|
||||||
// Return list of usernames in given room
|
// Return list of usernames in given room
|
||||||
exports.users_in_room = function(room) {
|
exports.users_in_room = function(room) {
|
||||||
return room_users.hasOwnProperty(room) ? room_users[room].array() : [];
|
return room_users.hasOwnProperty(room) ? room_users[room].array() : []
|
||||||
};
|
}
|
||||||
|
|
||||||
// Return list of usernames in given room
|
// Return list of usernames in given room
|
||||||
exports.room_clients_other_than_me = function(room, client) {
|
exports.room_clients_other_than_me = function(room, client) {
|
||||||
if (rooms.hasOwnProperty(room))
|
if (rooms.hasOwnProperty(room)) {
|
||||||
{
|
const clients = rooms[room]
|
||||||
var clients = rooms[room];
|
// console.dir(clients.array());
|
||||||
//console.dir(clients.array());
|
|
||||||
|
|
||||||
clients.remove(client);
|
clients.remove(client)
|
||||||
//console.dir(clients.array());
|
// console.dir(clients.array());
|
||||||
return clients.array();
|
return clients.array()
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//gets the current room of the client (assumes one room -- will select first one if in multiple)
|
|
||||||
exports.get_room = function (client) {
|
|
||||||
var client_rooms = null;
|
|
||||||
|
|
||||||
if (sid_rooms.hasOwnProperty(client.id))
|
|
||||||
{
|
|
||||||
client_rooms = sid_rooms[client.id].array();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( client_rooms !== null )
|
return []
|
||||||
return client_rooms[0];
|
}
|
||||||
else
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
// gets the current room of the client (assumes one room -- will select first one if in multiple)
|
||||||
|
exports.get_room = function(client) {
|
||||||
|
let client_rooms = null
|
||||||
|
|
||||||
|
if (sid_rooms.hasOwnProperty(client.id)) {
|
||||||
|
client_rooms = sid_rooms[client.id].array()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client_rooms !== null) { return client_rooms[0] }
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
// Generic server code
|
// Generic server code
|
||||||
|
|
||||||
exports.add_to_room_and_announce = function (client, room, msg) {
|
exports.add_to_room_and_announce = function(client, room, msg) {
|
||||||
|
|
||||||
// Add user info to the current dramatis personae
|
// Add user info to the current dramatis personae
|
||||||
exports.add_to_room(client, room, function(clients) {
|
exports.add_to_room(client, room, (clients) => {
|
||||||
// Broadcast new-user notification
|
// Broadcast new-user notification
|
||||||
for (var i = 0; i < clients.length; i++)
|
for (let i = 0; i < clients.length; i++) {
|
||||||
{
|
if (clients[i].id != client.id) { clients[i].json.send(msg) }
|
||||||
if (clients[i].id != client.id)
|
|
||||||
clients[i].json.send(msg);
|
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
exports.on_leave_room = function (client, room) {
|
exports.on_leave_room = function (client, room) {
|
||||||
|
@ -165,67 +149,58 @@ exports.on_leave_room = function (client, room) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
}*/
|
} */
|
||||||
|
|
||||||
//remember that this announces to all rooms that this client was a member of
|
// remember that this announces to all rooms that this client was a member of
|
||||||
exports.remove_from_all_rooms_and_announce = function (client, msg) {
|
exports.remove_from_all_rooms_and_announce = function(client, msg) {
|
||||||
exports.remove_from_all_rooms(client, function(clients) {
|
exports.remove_from_all_rooms(client, (clients) => {
|
||||||
for (var i = 0; i < clients.length; i++)
|
for (let i = 0; i < clients.length; i++) {
|
||||||
{
|
if (clients[i].id != client.id) { clients[i].json.send(msg) }
|
||||||
if (clients[i].id != client.id)
|
|
||||||
clients[i].json.send(msg);
|
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
//////////////////////////////
|
/// ///////////////////////////
|
||||||
// Broadcasting functions
|
// Broadcasting functions
|
||||||
//////////////////////////////
|
/// ///////////////////////////
|
||||||
|
|
||||||
// Broadcast message to all clients
|
// Broadcast message to all clients
|
||||||
exports.broadcast = function(msg) {
|
exports.broadcast = function(msg) {
|
||||||
if (socket) socket.broadcast(msg);
|
if (socket) socket.broadcast(msg)
|
||||||
net_server_streams.each(function(stream) {
|
net_server_streams.each((stream) => {
|
||||||
stream.write(JSON.stringify(msg)+'\r\n');
|
stream.write(`${JSON.stringify(msg)}\r\n`)
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
// Broadcast message to all clients in a given room.
|
// Broadcast message to all clients in a given room.
|
||||||
exports.broadcast_room = function(room, msg) {
|
exports.broadcast_room = function(room, msg) {
|
||||||
var clients = exports.room_clients(room);
|
const clients = exports.room_clients(room)
|
||||||
for (var i = 0; i < clients.length; i++)
|
for (let i = 0; i < clients.length; i++) { clients[i].json.send(msg) }
|
||||||
clients[i].json.send(msg);
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// Broadcast message to all the other clients that are in rooms with this client
|
// Broadcast message to all the other clients that are in rooms with this client
|
||||||
exports.broadcast_to_roommates = function (client, msg) {
|
exports.broadcast_to_roommates = function(client, msg) {
|
||||||
var roommates = new sets.Set();
|
let roommates = new sets.Set()
|
||||||
|
|
||||||
if (sid_rooms.hasOwnProperty(client.id))
|
if (sid_rooms.hasOwnProperty(client.id)) {
|
||||||
{
|
const client_rooms = sid_rooms[client.id].array()
|
||||||
var client_rooms = sid_rooms[client.id].array();
|
for (let i = 0; i < client_rooms.length; i++) {
|
||||||
for (var i = 0; i < client_rooms.length; i++)
|
const room = client_rooms[i]
|
||||||
{
|
if (rooms.hasOwnProperty(room)) {
|
||||||
var room = client_rooms[i];
|
const this_room = rooms[room].array()
|
||||||
if (rooms.hasOwnProperty(room))
|
for (let j = 0; j < this_room.length; j++) { roommates.add(this_room[j]) }
|
||||||
{
|
|
||||||
var this_room = rooms[room].array();
|
|
||||||
for (var j = 0; j < this_room.length; j++)
|
|
||||||
roommates.add(this_room[j]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//remove self from the set
|
// remove self from the set
|
||||||
roommates.remove(client);
|
roommates.remove(client)
|
||||||
roommates = roommates.array();
|
roommates = roommates.array()
|
||||||
|
|
||||||
//console.log('client: ' + client.id + " is broadcasting to: ");
|
// console.log('client: ' + client.id + " is broadcasting to: ");
|
||||||
|
|
||||||
|
for (let k = 0; k < roommates.length; k++) {
|
||||||
for (var k = 0; k < roommates.length; k++)
|
// console.log(' - ' + roommates[i].id);
|
||||||
{
|
roommates[k].json.send(msg)
|
||||||
//console.log(' - ' + roommates[i].id);
|
|
||||||
roommates[k].json.send(msg);
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
},
|
},
|
||||||
"author": "Florian Schmitt",
|
"author": "Florian Schmitt",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"lint-js": "eslint --fix --ext .js,.ts .",
|
||||||
"start": "nodemon server.js -e js,css,jade,json"
|
"start": "nodemon server.js -e js,css,jade,json"
|
||||||
},
|
},
|
||||||
"nodemonConfig": {
|
"nodemonConfig": {
|
||||||
|
@ -34,6 +35,13 @@
|
||||||
"yargs": "~2.3.0"
|
"yargs": "~2.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@eslint/eslintrc": "^3.3.0",
|
||||||
|
"@eslint/js": "^9.22.0",
|
||||||
|
"eslint": "^9.22.0",
|
||||||
|
"eslint-config-airbnb-base": "^15.0.0",
|
||||||
|
"eslint-plugin-import": "^2.31",
|
||||||
|
"globals": "^16.0.0",
|
||||||
|
"prettier": "^3.5",
|
||||||
"forever": "^0.15.3",
|
"forever": "^0.15.3",
|
||||||
"nodemon": "^2.0.16"
|
"nodemon": "^2.0.16"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue