Compare commits
No commits in common. "8ebd9b78ecfddb40e95ab497a86ee1411e2e45cd" and "416592d7dfd1cc587000c9d15f5c6b4cc88de6fa" have entirely different histories.
8ebd9b78ec
...
416592d7df
3
server/.gitignore
vendored
3
server/.gitignore
vendored
@ -117,9 +117,6 @@ dist
|
|||||||
# DynamoDB Local files
|
# DynamoDB Local files
|
||||||
.dynamodb/
|
.dynamodb/
|
||||||
|
|
||||||
# DB local files
|
|
||||||
.db
|
|
||||||
|
|
||||||
# TernJS port file
|
# TernJS port file
|
||||||
.tern-port
|
.tern-port
|
||||||
|
|
||||||
|
@ -31,11 +31,14 @@ class APIMethods {
|
|||||||
info (con, req, cb) {
|
info (con, req, cb) {
|
||||||
if (!req.fields) req.fields = "*";
|
if (!req.fields) req.fields = "*";
|
||||||
if (!req.fieldsExtSource) req.fieldsExtSource = "*";
|
if (!req.fieldsExtSource) req.fieldsExtSource = "*";
|
||||||
const cacheKey = `${req.fields}\u0000${req.fieldsExtSource}`;
|
const cache = this.info.cache[
|
||||||
|
JSON.stringify({
|
||||||
const cache = this.info.cache[cacheKey];
|
f: req.fields, e: req.fieldsExtSource
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
if (cache) {
|
if (cache) {
|
||||||
|
console.debug("CACHE!");
|
||||||
return cb({ result: cache, trace_id: req.trace_id, ended: true });
|
return cb({ result: cache, trace_id: req.trace_id, ended: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +55,11 @@ class APIMethods {
|
|||||||
if (!fields[k]) serverInfo.extSource[k] = undefined;
|
if (!fields[k]) serverInfo.extSource[k] = undefined;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.info.cache[cacheKey] = serverInfo;
|
this.info.cache[
|
||||||
|
JSON.stringify({
|
||||||
|
f: req.fields, e: req.fieldsExtSource
|
||||||
|
})
|
||||||
|
] = serverInfo;
|
||||||
|
|
||||||
cb({ result: serverInfo, trace_id: req.trace_id, ended: true });
|
cb({ result: serverInfo, trace_id: req.trace_id, ended: true });
|
||||||
}
|
}
|
||||||
@ -97,35 +104,6 @@ class APIMethods {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async characters (con, req, cb) {
|
|
||||||
const authedMethod = this.authed;
|
|
||||||
const isAuthed = await (new Promise((rs, rj) => {
|
|
||||||
authedMethod(con, req, ({ result, ended }) => {
|
|
||||||
if (ended)
|
|
||||||
return rs(result);
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (!isAuthed) {
|
|
||||||
return cb({
|
|
||||||
error: "require authorize",
|
|
||||||
trace_id: req.trace_id,
|
|
||||||
ended: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// getting characters
|
|
||||||
const user = global.authed.get(con);
|
|
||||||
const result = (await user.getCharacters())
|
|
||||||
.map(character => {
|
|
||||||
return character;
|
|
||||||
});
|
|
||||||
|
|
||||||
cb({
|
|
||||||
result, trace_id: req.trace_id, ended: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/*setSession (isEncrypted, address, { key, counter }, cb) {
|
/*setSession (isEncrypted, address, { key, counter }, cb) {
|
||||||
if (!global.config.server.secureMode)
|
if (!global.config.server.secureMode)
|
||||||
return cb({ error: "Encryption is off by configuration file into server" });
|
return cb({ error: "Encryption is off by configuration file into server" });
|
||||||
|
@ -13,7 +13,7 @@ module.exports.registerToken = function (user) {
|
|||||||
token = module.exports();
|
token = module.exports();
|
||||||
} while (global.authed.tokens[token] !== undefined);
|
} while (global.authed.tokens[token] !== undefined);
|
||||||
global.authed.tokens[token] = user;
|
global.authed.tokens[token] = user;
|
||||||
// TODO: Потом добавить регистрацию в БД!
|
// (!) Потом добавить регистрацию в БД!
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
};
|
};
|
||||||
|
@ -9,46 +9,26 @@
|
|||||||
* * 5 - root
|
* * 5 - root
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const ACCESS_LEVELS = {
|
|
||||||
"-1": "banned",
|
|
||||||
0: "user",
|
|
||||||
1: "helper",
|
|
||||||
2: "moderator",
|
|
||||||
3: "admin",
|
|
||||||
4: "superadmin",
|
|
||||||
5: "root",
|
|
||||||
};
|
|
||||||
|
|
||||||
class AccessLevel {
|
|
||||||
static validate (value) {
|
|
||||||
const access = Object.fromEntries(Object.keys(ACCESS_LEVELS)
|
|
||||||
.map(level => [level, true]));
|
|
||||||
return (
|
|
||||||
Number.isInteger(value) &&
|
|
||||||
access["" + value] === true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor (accessLevel) {
|
|
||||||
if (!this.__proto__.constructor.validate(accessLevel))
|
|
||||||
throw new Error(
|
|
||||||
`Invalid access level: ${accessLevel}`,
|
|
||||||
);
|
|
||||||
this.code = accessLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
get accessName () {
|
|
||||||
return ACCESS_LEVELS[""+this.code]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class User {
|
class User {
|
||||||
constructor (name, accessLevel) {
|
constructor(name, accessLevel) {
|
||||||
|
if (
|
||||||
|
typeof accessLevel !== "number" ||
|
||||||
|
{
|
||||||
|
"-1": true,
|
||||||
|
0: true,
|
||||||
|
1: true,
|
||||||
|
2: true,
|
||||||
|
3: true,
|
||||||
|
4: true,
|
||||||
|
5: true,
|
||||||
|
}["" + accessLevel] !== true
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
`Invalid access level: ${accessLevel} (must be ranged from -1 to 5)`,
|
||||||
|
);
|
||||||
|
}
|
||||||
this.name = name;
|
this.name = name;
|
||||||
if (accessLevel instanceof AccessLevel)
|
|
||||||
this.accessLevel = accessLevel;
|
this.accessLevel = accessLevel;
|
||||||
else
|
|
||||||
this.accessLevel = new AccessLevel(accessLevel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getUserByName(name) {
|
static async getUserByName(name) {
|
||||||
@ -61,15 +41,8 @@ class User {
|
|||||||
User.cache[name] = user;
|
User.cache[name] = user;
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
// TODO: Потом добавить чтение из БД
|
// Потом добавить чтение из БД
|
||||||
//return null;
|
|
||||||
|
|
||||||
const userdata = await global.database.getUserRowByName(name);
|
|
||||||
if (!userdata)
|
|
||||||
return null;
|
return null;
|
||||||
const user = new User(userdata.username, userdata.accessLevel);
|
|
||||||
User.cache[name] = user;
|
|
||||||
return user;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static getByToken(token) {
|
static getByToken(token) {
|
||||||
@ -77,27 +50,13 @@ class User {
|
|||||||
return user ?? null;
|
return user ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getUserForAPI(getFullInfo = false, fields = "*") {
|
getUserForAPI(getFullInfo = false) {
|
||||||
const getted = {
|
return {
|
||||||
username: this.name,
|
username: this.name,
|
||||||
accessLevel: this.accessLevel.code,
|
accessLevel: this.accessLevel,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (fields !== "*") {
|
|
||||||
const selected = Object.fromEntries(fields.split(/\s*,\s*/g).map(field => [field, true]));
|
|
||||||
for (let field in getted) {
|
|
||||||
if (!selected[field])
|
|
||||||
getted[field] = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return getted;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getCharacters () {
|
|
||||||
return [];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
User.cache = {};
|
User.cache = {};
|
||||||
|
|
||||||
module.exports = { User, AccessLevel };
|
module.exports = { User };
|
||||||
|
@ -18,8 +18,7 @@
|
|||||||
},
|
},
|
||||||
"database": {
|
"database": {
|
||||||
"dialect": "sqlite",
|
"dialect": "sqlite",
|
||||||
"storage": "./data.db",
|
"path": "./data"
|
||||||
"logging": false
|
|
||||||
},
|
},
|
||||||
"ai": {
|
"ai": {
|
||||||
"apiType": "kobold"
|
"apiType": "kobold"
|
||||||
|
@ -1,124 +0,0 @@
|
|||||||
const { DataTypes } = require("sequelize");
|
|
||||||
const { AccessLevel } = require("../api/utils/user");
|
|
||||||
const logger = require("../logger");
|
|
||||||
const bcrypt = require("bcrypt");
|
|
||||||
|
|
||||||
/*const SALT_ROUNDS = Math.round(Math.r) + 6;
|
|
||||||
const BCRYPT_SALT = bcrypt.genSaltSync(SALT_ROUNDS);*/
|
|
||||||
function getSalt () {
|
|
||||||
//return bcrypt.genSaltSync(10);
|
|
||||||
return bcrypt.genSaltSync(Math.round(Math.random() * 4) + 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = async function (sequelize, dbObj) {
|
|
||||||
const dbModel = {
|
|
||||||
users: sequelize.define("users", {
|
|
||||||
id: {
|
|
||||||
type: DataTypes.INTEGER,
|
|
||||||
allowNull: false,
|
|
||||||
autoIncrement: true,
|
|
||||||
primaryKey: true,
|
|
||||||
comment: "ID of user",
|
|
||||||
},
|
|
||||||
username: {
|
|
||||||
type: DataTypes.STRING,
|
|
||||||
allowNull: false,
|
|
||||||
unique: true,
|
|
||||||
comment: "Username of user",
|
|
||||||
validate: {
|
|
||||||
not: /null/iu,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
accessLevel: {
|
|
||||||
type: DataTypes.INTEGER,
|
|
||||||
allowNull: false,
|
|
||||||
defaultValue: false,
|
|
||||||
comment: "access level of user",
|
|
||||||
get: function () {
|
|
||||||
return new AccessLevel(this.getDataValue("accessLevel"));
|
|
||||||
},
|
|
||||||
set: function (value) {
|
|
||||||
if (value instanceof AccessLevel)
|
|
||||||
return this.setDataValue("accessLevel", value.code);
|
|
||||||
if (Number.isInteger(value)) {
|
|
||||||
if (AccessLevel.validate(value))
|
|
||||||
return this.setDataValue("accessLevel", value);
|
|
||||||
}
|
|
||||||
throw new TypeError("accessLevel must be AccessLevel object or integer");
|
|
||||||
//this.setDataValue('inventory', JSON.stringify(value));
|
|
||||||
},
|
|
||||||
},
|
|
||||||
password: {
|
|
||||||
type: DataTypes.STRING,
|
|
||||||
allowNull: false,
|
|
||||||
unique: false,
|
|
||||||
comment: "Hash code of password",
|
|
||||||
set: function (value) {
|
|
||||||
const hash = bcrypt.hashSync(value, getSalt());
|
|
||||||
this.setDataValue('password', hash);
|
|
||||||
},
|
|
||||||
get: function () {
|
|
||||||
const checkHash = this.getDataValue("password");
|
|
||||||
return async function (password) {
|
|
||||||
return await new Promise((rs, rj) => {
|
|
||||||
bcrypt.compare(password, checkHash, (err, res) => {
|
|
||||||
if (err)
|
|
||||||
return rs(false);
|
|
||||||
logger.debug("isRes is", res);
|
|
||||||
rs(res);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
isActivate: {
|
|
||||||
type: DataTypes.BOOLEAN,
|
|
||||||
allowNull: false,
|
|
||||||
defaultValue: true,
|
|
||||||
comment: "Is user activated",
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
|
|
||||||
userCharacters: sequelize.define("userCharacters", {
|
|
||||||
id: {
|
|
||||||
type: DataTypes.INTEGER,
|
|
||||||
allowNull: false,
|
|
||||||
autoIncrement: true,
|
|
||||||
primaryKey: true,
|
|
||||||
comment: "ID of character",
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: DataTypes.STRING,
|
|
||||||
allowNull: false,
|
|
||||||
unique: true,
|
|
||||||
comment: "Name of character",
|
|
||||||
},
|
|
||||||
ownerId: {
|
|
||||||
type: DataTypes.INTEGER,
|
|
||||||
allowNull: false,
|
|
||||||
comment: "ID of character's owner",
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
dbModel.userCharacters.belongsTo(dbModel.users, {
|
|
||||||
foreignKey: "ownerId", // field in userCharacters
|
|
||||||
targetKey: "id", // field in users
|
|
||||||
onDelete: "CASCADE", // If delete user, characters will be deleted
|
|
||||||
onUpdate: "CASCADE",
|
|
||||||
});
|
|
||||||
dbModel.users.hasMany(dbModel.userCharacters, {
|
|
||||||
foreignKey: "ownerId", // field ownerId
|
|
||||||
sourceKey: "id",
|
|
||||||
});
|
|
||||||
|
|
||||||
await sequelize.authenticate();
|
|
||||||
const initPromise = Object.entries(dbModel).map(async ([tableName, table]) => {
|
|
||||||
logger.log(`* init table: ${tableName}`);
|
|
||||||
await table.sync({ alter: true });
|
|
||||||
const rows = await table.findAll();
|
|
||||||
await dbObj.cache[tableName]?.(rows, table);
|
|
||||||
logger.log(`* inited: ${tableName}`);
|
|
||||||
});
|
|
||||||
await Promise.all(initPromise);
|
|
||||||
};
|
|
@ -1,89 +0,0 @@
|
|||||||
const { Sequelize } = require("sequelize");
|
|
||||||
const dbModel = require("./dbModel");
|
|
||||||
const logger = require("../logger");
|
|
||||||
|
|
||||||
class DatabaseCache {
|
|
||||||
async users (rows, table) {
|
|
||||||
// Add root user only where root user is activated
|
|
||||||
if (global.config.rootuser.activated) {
|
|
||||||
const isRootHasOnDB = rows.find(x => x.username === global.config.rootuser.login);
|
|
||||||
const rootLevel = 5; // TODO: replace it to AccessLevel.root in future
|
|
||||||
|
|
||||||
if (!isRootHasOnDB) {
|
|
||||||
await table.create({
|
|
||||||
username: global.config.rootuser.login,
|
|
||||||
accessLevel: rootLevel,
|
|
||||||
password: "",
|
|
||||||
});
|
|
||||||
return await this.users((
|
|
||||||
await table.findAll()
|
|
||||||
), table);
|
|
||||||
} else if (!rows.find(x => x.username === global.config.rootuser.login && x.accessLevel.code === rootLevel)) {
|
|
||||||
throw new Error(`Username ${
|
|
||||||
global.config.rootuser.login
|
|
||||||
} is busy`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.users.rows = rows;
|
|
||||||
this.users.byUsername = new Object();
|
|
||||||
this.users.byId = new Object();
|
|
||||||
|
|
||||||
for (let row of rows) {
|
|
||||||
this.users.byUsername[row.username] = row;
|
|
||||||
this.users.byId[row.id] = row;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add user info
|
|
||||||
this.users.add = async (row, isCreate = false) => {
|
|
||||||
if (isCreate)
|
|
||||||
await table.create(row);
|
|
||||||
|
|
||||||
let addedData = null;
|
|
||||||
if (row.username)
|
|
||||||
addedData = await table.findAll({
|
|
||||||
where: {
|
|
||||||
username: row.username
|
|
||||||
}
|
|
||||||
})[0];
|
|
||||||
else if (row.id)
|
|
||||||
addedData = await table.findAll({
|
|
||||||
where: {
|
|
||||||
id: row.id
|
|
||||||
}
|
|
||||||
})[0];
|
|
||||||
|
|
||||||
if (!addedData) throw new Error("Invalid row information");
|
|
||||||
this.users.rows.push(addedData);
|
|
||||||
// By keys
|
|
||||||
this.users.byUsername[addedData.username] = addedData;
|
|
||||||
this.users.byId[addedData.id] = addedData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Database {
|
|
||||||
constructor(sequlizeData) {
|
|
||||||
this.sequlize = new Sequelize(sequlizeData);
|
|
||||||
}
|
|
||||||
|
|
||||||
async init(fromHandler = false) {
|
|
||||||
global.server.toggleLock(true);
|
|
||||||
this.cache = new DatabaseCache();
|
|
||||||
await dbModel(this.sequlize, this);
|
|
||||||
global.server.toggleLock(false);
|
|
||||||
if (!fromHandler)
|
|
||||||
global.server.on("config.update", async ({
|
|
||||||
oldConfig, newConfig
|
|
||||||
}) => {
|
|
||||||
if (JSON.stringify(oldConfig.database) !== JSON.stringify(newConfig.database))
|
|
||||||
await this.init(true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return ROW of 'users' table by username
|
|
||||||
async getUserRowByName (name) {
|
|
||||||
return this.cache.users.byUsername[name];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Database;
|
|
@ -32,4 +32,4 @@ module.exports = function (req, res) {
|
|||||||
<script src="/js/connect/main.js" type="module" defer></script>
|
<script src="/js/connect/main.js" type="module" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>`);
|
</html>`);
|
||||||
};
|
}
|
@ -5,19 +5,16 @@ const router = express.Router();
|
|||||||
|
|
||||||
router.get("/", (req, res) => {
|
router.get("/", (req, res) => {
|
||||||
res.send(
|
res.send(
|
||||||
fs.readFileSync(path.join(__dirname, "pages/index.html")).toString("utf-8"),
|
fs.readFileSync(path.join(__dirname, "pages/index.html")).toString("utf-8")
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/connect", (req, res) => {
|
router.get("/connect", (req, res) => {
|
||||||
res.send(
|
res.send(
|
||||||
fs.readFileSync(path.join(__dirname, "pages/connect.html"))
|
fs.readFileSync(path.join(__dirname, "pages/connect.html")).toString("utf-8")
|
||||||
.toString("utf-8"),
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/*", (req, res) =>
|
router.get("/*", (req, res) => res.status(404).send("<p><b>404:</b> Not finded.</p>"));
|
||||||
res.status(404).send("<p><b>404:</b> Not finded.</p>"),
|
|
||||||
);
|
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
@ -12,10 +12,6 @@ module.exports = (address, port) => {
|
|||||||
|
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
// logger.log("HTTP headers:", req.headers);
|
// logger.log("HTTP headers:", req.headers);
|
||||||
if (global.server.isLocked) {
|
|
||||||
res.set("Content-Type", "plain/text");
|
|
||||||
return res.send("Server temporary on reload");
|
|
||||||
}
|
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -51,7 +47,8 @@ module.exports = (address, port) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.listen(port, address, (err) => {
|
app.listen(port, address, (err) => {
|
||||||
if (err) throw err;
|
if (err)
|
||||||
|
throw err;
|
||||||
logger.log(`HTTP page successful runned at port ${port}`);
|
logger.log(`HTTP page successful runned at port ${port}`);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -83,12 +83,6 @@ class ApiMethods extends ProtoApiMethods {
|
|||||||
gtf
|
gtf
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async characters () {
|
|
||||||
return await super._protoMethod({
|
|
||||||
method: "characters"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ApiHTML {
|
class ApiHTML {
|
||||||
@ -108,9 +102,6 @@ class ApiHTML {
|
|||||||
|
|
||||||
document.getElementById("server.area").innerHTML = '';
|
document.getElementById("server.area").innerHTML = '';
|
||||||
$(document.getElementById("server.area")).append(ServerAuth.mainMenuForm);
|
$(document.getElementById("server.area")).append(ServerAuth.mainMenuForm);
|
||||||
// JS-Events
|
|
||||||
const charactersEvents = await this.api.methods.characters();
|
|
||||||
console.debug({ charactersEvents });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async renderAuth (authMode, bgUrl = null, favicon = null) {
|
async renderAuth (authMode, bgUrl = null, favicon = null) {
|
||||||
|
@ -1,29 +1,26 @@
|
|||||||
// cookie parser function
|
// cookie parser function
|
||||||
function getCookies() {
|
function getCookies () {
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(document.cookie.split(/\s{0,};\s{0,}/gmiu).map(i => i.split(/\s{0,}=\s{0,}/gmiu)));
|
||||||
document.cookie
|
|
||||||
.split(/\s{0,};\s{0,}/gimu)
|
|
||||||
.map((i) => i.split(/\s{0,}=\s{0,}/gimu)),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// translate function
|
// translate function
|
||||||
function translate(page) {
|
function translate (page) {
|
||||||
[...document.getElementsByClassName("translate")].forEach((translateEl) => {
|
[...document.getElementsByClassName("translate")].forEach(translateEl => {
|
||||||
// console.log(translateEl.id);
|
// console.log(translateEl.id);
|
||||||
const result = tr(page, translateEl.id);
|
const result = tr(page, translateEl.id);
|
||||||
// console.log(result);
|
// console.log(result);
|
||||||
if (result) translateEl.innerHTML = result;
|
if (result)
|
||||||
|
translateEl.innerHTML = result;
|
||||||
});
|
});
|
||||||
|
|
||||||
[...document.getElementsByClassName("linked-btn")].forEach((el) => {
|
[...document.getElementsByClassName("linked-btn")].forEach(el => {
|
||||||
el.style.cursor = "pointer";
|
el.style.cursor = "pointer";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get random integer number function
|
// Get random integer number function
|
||||||
function randint(min, max) {
|
function randint(min, max) {
|
||||||
return Math.ceil(Math.random() * (max - min) + min);
|
return Math.ceil((Math.random() * (max - min)) + min);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get traceId for requests to server
|
// Get traceId for requests to server
|
||||||
@ -35,12 +32,10 @@ function getTraceId() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// load styles
|
// load styles
|
||||||
document.head.innerHTML +=
|
document.head.innerHTML += '\\n <link href="/bootstrap/bootstrap.min.css" rel="stylesheet">';
|
||||||
'\\n <link href="/bootstrap/bootstrap.min.css" rel="stylesheet">';
|
|
||||||
document.head.innerHTML += '\\n <link href="/css/main.css" rel="stylesheet">';
|
document.head.innerHTML += '\\n <link href="/css/main.css" rel="stylesheet">';
|
||||||
document.head.innerHTML +=
|
document.head.innerHTML += '\\n <link href="/font-awesome/css/all.css" rel="stylesheet">';
|
||||||
'\\n <link href="/font-awesome/css/all.css" rel="stylesheet">';
|
document.head.innerHTML += '\\n <link rel="icon" href="/favicon.png">'
|
||||||
document.head.innerHTML += '\\n <link rel="icon" href="/favicon.png">';
|
|
||||||
|
|
||||||
// load main page
|
// load main page
|
||||||
document.body.innerHTML = `<nav id="navbar-main" class="navbar navbar-expand-lg navbar-dark bg-dark"><div class="container-fluid">
|
document.body.innerHTML = `<nav id="navbar-main" class="navbar navbar-expand-lg navbar-dark bg-dark"><div class="container-fluid">
|
||||||
@ -64,12 +59,9 @@ document.body.innerHTML = `<nav id="navbar-main" class="navbar navbar-expand-lg
|
|||||||
|
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
function checkContentWarning(successfulHandler) {
|
function checkContentWarning (successfulHandler) {
|
||||||
const warnModal = new bootstrap.Modal(
|
const warnModal = new bootstrap.Modal(document.getElementById('warningModal'), {});
|
||||||
document.getElementById("warningModal"),
|
document.getElementById('accept-denial-btn').onclick = () => {
|
||||||
{},
|
|
||||||
);
|
|
||||||
document.getElementById("accept-denial-btn").onclick = () => {
|
|
||||||
document.cookie = "cw=1";
|
document.cookie = "cw=1";
|
||||||
successfulHandler?.();
|
successfulHandler?.();
|
||||||
};
|
};
|
||||||
@ -86,7 +78,7 @@ function checkContentWarning(successfulHandler) {
|
|||||||
fl.bindLoad("/connect", () => {
|
fl.bindLoad("/connect", () => {
|
||||||
translate("/connect");
|
translate("/connect");
|
||||||
|
|
||||||
function initNewConnectionOpen() {
|
function initNewConnectionOpen () {
|
||||||
if (initNewConnectionOpen.interval)
|
if (initNewConnectionOpen.interval)
|
||||||
clearInterval(initNewConnectionOpen.interval);
|
clearInterval(initNewConnectionOpen.interval);
|
||||||
document.getElementById("custom-conn-address").value = "";
|
document.getElementById("custom-conn-address").value = "";
|
||||||
@ -114,14 +106,8 @@ fl.bindLoad("/connect", () => {
|
|||||||
isWSS.checked = false;
|
isWSS.checked = false;
|
||||||
}
|
}
|
||||||
if (address.value.match(/:[0-9]{1,5}$/)) {
|
if (address.value.match(/:[0-9]{1,5}$/)) {
|
||||||
const portSelected = +address.value
|
const portSelected = +(address.value.match(/:[0-9]{1,5}$/).at(0).slice(1,));
|
||||||
.match(/:[0-9]{1,5}$/)
|
address.value = address.value.slice(0, -1 * (":"+portSelected).length);
|
||||||
.at(0)
|
|
||||||
.slice(1);
|
|
||||||
address.value = address.value.slice(
|
|
||||||
0,
|
|
||||||
-1 * (":" + portSelected).length,
|
|
||||||
);
|
|
||||||
port.value = portSelected;
|
port.value = portSelected;
|
||||||
document.getElementById("custom-conn-port").focus();
|
document.getElementById("custom-conn-port").focus();
|
||||||
}
|
}
|
||||||
@ -129,15 +115,16 @@ fl.bindLoad("/connect", () => {
|
|||||||
if (!port.value) {
|
if (!port.value) {
|
||||||
port.value = 80;
|
port.value = 80;
|
||||||
}
|
}
|
||||||
if (+port.value < 0) port.value = 0;
|
if (+port.value < 0)
|
||||||
if (+port.value > 65535) port.value = 65535;
|
port.value = 0;
|
||||||
|
if (+port.value > 65535)
|
||||||
|
port.value = 65535;
|
||||||
|
|
||||||
connButton.disabled = !address.value;
|
connButton.disabled = !address.value;
|
||||||
}, 50);
|
}, 50);
|
||||||
|
|
||||||
connButton.onclick = () => {
|
connButton.onclick = () => {
|
||||||
const serverlist =
|
const serverlist = JSON.parse(localStorage.getItem("savedServers") ?? "null") ?? [];
|
||||||
JSON.parse(localStorage.getItem("savedServers") ?? "null") ?? [];
|
|
||||||
serverlist.push({
|
serverlist.push({
|
||||||
address: document.getElementById("custom-conn-address").value,
|
address: document.getElementById("custom-conn-address").value,
|
||||||
port: +document.getElementById("custom-conn-port").value,
|
port: +document.getElementById("custom-conn-port").value,
|
||||||
@ -145,12 +132,11 @@ fl.bindLoad("/connect", () => {
|
|||||||
});
|
});
|
||||||
localStorage.setItem("savedServers", JSON.stringify(serverlist));
|
localStorage.setItem("savedServers", JSON.stringify(serverlist));
|
||||||
connectServers();
|
connectServers();
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function connectServers() {
|
function connectServers () {
|
||||||
document.getElementById("loading-servers").innerHTML =
|
document.getElementById("loading-servers").innerHTML = `<center><p>${tr("/connect", "loading") ?? "Loading..."}</p></center>`;
|
||||||
`<center><p>${tr("/connect", "loading") ?? "Loading..."}</p></center>`;
|
|
||||||
|
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
xhr.open("GET", "/api/active-servers.json", true);
|
xhr.open("GET", "/api/active-servers.json", true);
|
||||||
@ -160,39 +146,26 @@ fl.bindLoad("/connect", () => {
|
|||||||
<h3>${tr("/connect", "connection-failed") ?? "Connection failed"}</h3><hr>
|
<h3>${tr("/connect", "connection-failed") ?? "Connection failed"}</h3><hr>
|
||||||
<button type="button" class="btn btn-primary translate" id="retry-connecting">${tr("/connect", "retry-connecting") ?? "Retry"}</button>
|
<button type="button" class="btn btn-primary translate" id="retry-connecting">${tr("/connect", "retry-connecting") ?? "Retry"}</button>
|
||||||
</center>`;
|
</center>`;
|
||||||
document.getElementById("retry-connecting").onclick = () =>
|
document.getElementById("retry-connecting").onclick = () => connectServers();
|
||||||
connectServers();
|
}
|
||||||
};
|
|
||||||
|
|
||||||
xhr.onload = async () => {
|
xhr.onload = async () => {
|
||||||
if (xhr.status === 200) {
|
if (xhr.status === 200) {
|
||||||
const localServers = (
|
const localServers = (JSON.parse(localStorage.getItem("savedServers") ?? "null") ?? []).map(x => { x.fromLocal = true; return x });
|
||||||
JSON.parse(localStorage.getItem("savedServers") ?? "null") ?? []
|
|
||||||
).map((x) => {
|
|
||||||
x.fromLocal = true;
|
|
||||||
return x;
|
|
||||||
});
|
|
||||||
|
|
||||||
const servers = localServers.concat(
|
const servers = localServers.concat(JSON.parse(xhr.response).result.map(x => { x.fromLocal = false; return x }));
|
||||||
JSON.parse(xhr.response).result.map((x) => {
|
|
||||||
x.fromLocal = false;
|
|
||||||
return x;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
const serversByItemID = new Object();
|
const serversByItemID = new Object();
|
||||||
const serversByAddress = new Object();
|
const serversByAddress = new Object();
|
||||||
const items = [];
|
const items = [];
|
||||||
const itemIDs = [];
|
const itemIDs = [];
|
||||||
const mp = servers.map(async (serverItem) => {
|
const mp = servers.map(async serverItem => {
|
||||||
const promise = new Promise((resolve, reject) => {
|
const promise = new Promise((resolve, reject) => {
|
||||||
console.log("serverItem:", serverItem);
|
console.log("serverItem:", serverItem);
|
||||||
const trace_id = getTraceId();
|
const trace_id = getTraceId();
|
||||||
const socket = new WebSocket(
|
const socket = new WebSocket(`${!serverItem.tls ? "ws" : "wss"}://${serverItem.address}:${serverItem.port}`);
|
||||||
`${!serverItem.tls ? "ws" : "wss"}://${serverItem.address}:${serverItem.port}`,
|
|
||||||
);
|
|
||||||
let ping = new Date();
|
let ping = new Date();
|
||||||
socket.onopen = () => {
|
socket.onopen = () => {
|
||||||
ping = new Date() - ping;
|
ping = (new Date()) - ping;
|
||||||
socket.onmessage = (ev) => {
|
socket.onmessage = (ev) => {
|
||||||
const data = JSON.parse(ev.data).result;
|
const data = JSON.parse(ev.data).result;
|
||||||
console.debug("data:", data);
|
console.debug("data:", data);
|
||||||
@ -203,31 +176,22 @@ fl.bindLoad("/connect", () => {
|
|||||||
deleteID: `remove-con:${serverItem.address}:${serverItem.port}`,
|
deleteID: `remove-con:${serverItem.address}:${serverItem.port}`,
|
||||||
address: `${serverItem.address}:${serverItem.port}`,
|
address: `${serverItem.address}:${serverItem.port}`,
|
||||||
});
|
});
|
||||||
serversByItemID[
|
serversByItemID[`con-btn-to:${data.tag}:${serverItem.address}:${serverItem.port}`] = { data, serverItem };
|
||||||
`con-btn-to:${data.tag}:${serverItem.address}:${serverItem.port}`
|
serversByAddress[`${serverItem.address}:${serverItem.port}`] = { data, serverItem };
|
||||||
] = { data, serverItem };
|
resolve(items.push(`<tr${!data.tls ? ' style="background-color: #7F6003"' : ""}>
|
||||||
serversByAddress[`${serverItem.address}:${serverItem.port}`] = {
|
|
||||||
data,
|
|
||||||
serverItem,
|
|
||||||
};
|
|
||||||
resolve(
|
|
||||||
items.push(`<tr${!data.tls ? ' style="background-color: #7F6003"' : ""}>
|
|
||||||
<td${!data.tls ? ' style="background-color: #7F6003"' : ""}>${data.name}</td>
|
<td${!data.tls ? ' style="background-color: #7F6003"' : ""}>${data.name}</td>
|
||||||
<td${!data.tls ? ' style="background-color: #7F6003"' : ""}>${serverItem.address}:${serverItem.port}</td>
|
<td${!data.tls ? ' style="background-color: #7F6003"' : ""}>${serverItem.address}:${serverItem.port}</td>
|
||||||
<td${!data.tls ? ' style="background-color: #7F6003"' : ""}>${!data.tls ? '<font color="red"><i class="fas fa-exclamation-triangle"></i></font>' : '<font color="green"><i class="fas fa-lock"></i></font>'}</td>
|
<td${!data.tls ? ' style="background-color: #7F6003"' : ""}>${!data.tls ? '<font color="red"><i class="fas fa-exclamation-triangle"></i></font>' : '<font color="green"><i class="fas fa-lock"></i></font>'}</td>
|
||||||
<td${!data.tls ? ' style="background-color: #7F6003"' : ""}>${ping}</td>
|
<td${!data.tls ? ' style="background-color: #7F6003"' : ""}>${ping}</td>
|
||||||
<td${!data.tls ? ' style="background-color: #7F6003"' : ""}><button class="btn btn-secondary" id="con-btn-to:${data.tag}:${serverItem.address}:${serverItem.port}">${tr("/connect", "connect-to-server-btn") ?? "Connect"}</button></td>
|
<td${!data.tls ? ' style="background-color: #7F6003"' : ""}><button class="btn btn-secondary" id="con-btn-to:${data.tag}:${serverItem.address}:${serverItem.port}">${tr("/connect", "connect-to-server-btn") ?? "Connect"}</button></td>
|
||||||
<td${!data.tls ? ' style="background-color: #7F6003"' : ""}><button class="btn btn-outline-danger" id="remove-con:${serverItem.address}:${serverItem.port}"${!serverItem.fromLocal ? " disabled" : ""}><i class="fas fa-trash"></i></button></td>
|
<td${!data.tls ? ' style="background-color: #7F6003"' : ""}><button class="btn btn-outline-danger" id="remove-con:${serverItem.address}:${serverItem.port}"${!serverItem.fromLocal ? ' disabled' : ''}><i class="fas fa-trash"></i></button></td>
|
||||||
</tr>`),
|
</tr>`));
|
||||||
);
|
|
||||||
};
|
};
|
||||||
socket.send(
|
socket.send(JSON.stringify({
|
||||||
JSON.stringify({
|
|
||||||
method: "info",
|
method: "info",
|
||||||
fields: "name,tag",
|
fields: "name,tag",
|
||||||
trace_id,
|
trace_id
|
||||||
}),
|
}));
|
||||||
);
|
|
||||||
};
|
};
|
||||||
socket.onerror = (err) => {
|
socket.onerror = (err) => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
@ -235,30 +199,23 @@ fl.bindLoad("/connect", () => {
|
|||||||
deleteID: `remove-con:${serverItem.address}:${serverItem.port}`,
|
deleteID: `remove-con:${serverItem.address}:${serverItem.port}`,
|
||||||
address: `${serverItem.address}:${serverItem.port}`,
|
address: `${serverItem.address}:${serverItem.port}`,
|
||||||
});
|
});
|
||||||
serversByAddress[`${serverItem.address}:${serverItem.port}`] = {
|
serversByAddress[`${serverItem.address}:${serverItem.port}`] = { serverItem };
|
||||||
serverItem,
|
resolve(items.push(`<tr>
|
||||||
};
|
|
||||||
resolve(
|
|
||||||
items.push(`<tr>
|
|
||||||
<td style="background-color: #7F0000"><font color="red">${tr("/connect", "offline-server") ?? "Offline"}</font></td>
|
<td style="background-color: #7F0000"><font color="red">${tr("/connect", "offline-server") ?? "Offline"}</font></td>
|
||||||
<td style="background-color: #7F0000">${serverItem.address}:${serverItem.port}</td>
|
<td style="background-color: #7F0000">${serverItem.address}:${serverItem.port}</td>
|
||||||
<td style="background-color: #7F0000"><font color="red"><i class="fas fa-exclamation-triangle"></i></font></td>
|
<td style="background-color: #7F0000"><font color="red"><i class="fas fa-exclamation-triangle"></i></font></td>
|
||||||
<td style="background-color: #7F0000"><font color="red">${tr("/connect", "offline-server") ?? "Offline"}</font></td>
|
<td style="background-color: #7F0000"><font color="red">${tr("/connect", "offline-server") ?? "Offline"}</font></td>
|
||||||
<td style="background-color: #7F0000"><button class="btn btn-danger" disabled>${tr("/connect", "offline-server") ?? "Offline"}</button></td>
|
<td style="background-color: #7F0000"><button class="btn btn-danger" disabled>${tr("/connect", "offline-server") ?? "Offline"}</button></td>
|
||||||
<td style="background-color: #7F0000"><button class="btn btn-outline-danger" id="remove-con:${serverItem.address}:${serverItem.port}"${!serverItem.fromLocal ? " disabled" : ""}><i class="fas fa-trash"></i></button></td>
|
<td style="background-color: #7F0000"><button class="btn btn-outline-danger" id="remove-con:${serverItem.address}:${serverItem.port}"${!serverItem.fromLocal ? ' disabled' : ''}><i class="fas fa-trash"></i></button></td>
|
||||||
</tr>`),
|
</tr>`));
|
||||||
);
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
return await promise;
|
return await promise;
|
||||||
});
|
});
|
||||||
|
|
||||||
await Promise.all(mp);
|
await Promise.all(mp);
|
||||||
items.push(
|
items.push(`<tr><td colspan="6"><center><button class="btn btn-primary" id="conn-to-custom">${tr("/connect", "add-custon-server") ?? "Connect to custom server"}</button></center></td></tr>`);
|
||||||
`<tr><td colspan="6"><center><button class="btn btn-primary" id="conn-to-custom">${tr("/connect", "add-custon-server") ?? "Connect to custom server"}</button></center></td></tr>`,
|
document.getElementById("loading-servers").innerHTML = `<table class="table table-dark">
|
||||||
);
|
|
||||||
document.getElementById("loading-servers").innerHTML =
|
|
||||||
`<table class="table table-dark">
|
|
||||||
<thead>
|
<thead>
|
||||||
<th scope="col">${tr("/connect", "server-name-tbl") ?? "Name"}</th>
|
<th scope="col">${tr("/connect", "server-name-tbl") ?? "Name"}</th>
|
||||||
<th scope="col">${tr("/connect", "server-address") ?? "Address"}</th>
|
<th scope="col">${tr("/connect", "server-address") ?? "Address"}</th>
|
||||||
@ -272,28 +229,22 @@ fl.bindLoad("/connect", () => {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>`;
|
</table>`;
|
||||||
|
|
||||||
itemIDs.forEach((btn) => {
|
itemIDs.forEach(btn => {
|
||||||
//console.log("btn:", btn);
|
//console.log("btn:", btn);
|
||||||
if (btn.itemID)
|
if (btn.itemID)
|
||||||
document.getElementById(btn.itemID).onclick = () => {
|
document.getElementById(btn.itemID).onclick = () => {
|
||||||
checkContentWarning(() => {
|
checkContentWarning(() => {
|
||||||
const serverInfo = serversByItemID[btn.itemID].serverItem;
|
const serverInfo = serversByItemID[btn.itemID].serverItem;
|
||||||
window.open(
|
window.open(`/connect-area?server=${serverInfo.address}:${serverInfo.port}&encrypted=${serverInfo.tls ? "true" : "false"}`);
|
||||||
`/connect-area?server=${serverInfo.address}:${serverInfo.port}&encrypted=${serverInfo.tls ? "true" : "false"}`,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
document.getElementById(btn.deleteID).onclick = () => {
|
document.getElementById(btn.deleteID).onclick = () => {
|
||||||
const serverInfo = serversByAddress[btn.address].serverItem;
|
const serverInfo = serversByAddress[btn.address].serverItem;
|
||||||
console.log("serverInfo:", serverInfo);
|
console.log("serverInfo:", serverInfo);
|
||||||
const savedServers = (
|
const savedServers = (JSON.parse(localStorage.getItem("savedServers") ?? "null") ?? [])
|
||||||
JSON.parse(localStorage.getItem("savedServers") ?? "null") ?? []
|
.filter(server => {
|
||||||
).filter((server) => {
|
return !(server.address === serverInfo.address && server.port === serverInfo.port)
|
||||||
return !(
|
|
||||||
server.address === serverInfo.address &&
|
|
||||||
server.port === serverInfo.port
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
localStorage.setItem("savedServers", JSON.stringify(savedServers));
|
localStorage.setItem("savedServers", JSON.stringify(savedServers));
|
||||||
connectServers();
|
connectServers();
|
||||||
@ -301,13 +252,10 @@ fl.bindLoad("/connect", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById("conn-to-custom").onclick = function () {
|
document.getElementById("conn-to-custom").onclick = function () {
|
||||||
const connectModal = new bootstrap.Modal(
|
const connectModal = new bootstrap.Modal(document.getElementById('connectModal'), {});
|
||||||
document.getElementById("connectModal"),
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
initNewConnectionOpen();
|
initNewConnectionOpen();
|
||||||
connectModal.show();
|
connectModal.show();
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -316,10 +264,7 @@ fl.bindLoad("/connect", () => {
|
|||||||
|
|
||||||
const cookies = getCookies();
|
const cookies = getCookies();
|
||||||
console.log(cookies);
|
console.log(cookies);
|
||||||
const cookiesModal = new bootstrap.Modal(
|
const cookiesModal = new bootstrap.Modal(document.getElementById('cookiesModal'), {});
|
||||||
document.getElementById("cookiesModal"),
|
|
||||||
{},
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!cookies.accept_cookies) {
|
if (!cookies.accept_cookies) {
|
||||||
cookiesModal.show();
|
cookiesModal.show();
|
||||||
@ -341,41 +286,34 @@ fl.bindLoad("", () => {
|
|||||||
translate("/");
|
translate("/");
|
||||||
// carousel
|
// carousel
|
||||||
|
|
||||||
function onMouse(condition) {
|
function onMouse (condition) {
|
||||||
return;
|
return;
|
||||||
[...document.getElementsByClassName("card-img")].forEach((img) => {
|
[...document.getElementsByClassName("card-img")].forEach(img => {
|
||||||
img.style.filter = `blur(${!condition ? 5 : 0}px)`;
|
img.style.filter = `blur(${!condition ? 5 : 0}px)`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onMouse(false);
|
onMouse(false);
|
||||||
|
|
||||||
[...document.getElementsByClassName("carousel-item")].forEach((item) => {
|
[...document.getElementsByClassName("carousel-item")].forEach(item => {
|
||||||
item.onmouseout = () => onMouse(false);
|
item.onmouseout = () => onMouse(false);
|
||||||
item.onmouseover = () => onMouse(true);
|
item.onmouseover = () => onMouse(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
[...document.getElementsByClassName("btn-card")].forEach((item) => {
|
[...document.getElementsByClassName("btn-card")].forEach(item => {
|
||||||
item.onmouseout = () => onMouse(true);
|
item.onmouseout = () => onMouse(true);
|
||||||
item.onmouseover = () => onMouse(false);
|
item.onmouseover = () => onMouse(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
const navbarHeight = +document.getElementById("navbar-main")?.offsetHeight;
|
const navbarHeight = +(document.getElementById("navbar-main")?.offsetHeight);
|
||||||
/*if (document.getElementById('main_img1')?.src) {
|
/*if (document.getElementById('main_img1')?.src) {
|
||||||
document.getElementById('main_img1').src = window.screen.availWidth / window.screen.availHeight > 1.45 ? "/assets/hello/1.png" : "/assets/hello/m/1.png";
|
document.getElementById('main_img1').src = window.screen.availWidth / window.screen.availHeight > 1.45 ? "/assets/hello/1.png" : "/assets/hello/m/1.png";
|
||||||
document.getElementById('main_img2').src = window.screen.availWidth / window.screen.availHeight > 1.45 ? "/assets/hello/2.png" : "/assets/hello/m/2.png";
|
document.getElementById('main_img2').src = window.screen.availWidth / window.screen.availHeight > 1.45 ? "/assets/hello/2.png" : "/assets/hello/m/2.png";
|
||||||
document.getElementById('main_img3').src = window.screen.availWidth / window.screen.availHeight > 1.45 ? "/assets/hello/3.png" : "/assets/hello/m/3.png";
|
document.getElementById('main_img3').src = window.screen.availWidth / window.screen.availHeight > 1.45 ? "/assets/hello/3.png" : "/assets/hello/m/3.png";
|
||||||
}*/
|
}*/
|
||||||
const selectedCSS = Object.entries(document.styleSheets).filter(
|
const selectedCSS = Object.entries(document.styleSheets).filter(([key, cssFileObject]) => cssFileObject.href === `${location.origin}/css/main.css`)[0][1];
|
||||||
([key, cssFileObject]) =>
|
Object.entries(selectedCSS.rules).filter(([key, rule]) => rule.selectorText == '.carousel > .carousel-inner > .carousel-item > img')[0][1].style.height = `calc(100vh - ${navbarHeight}px)`;
|
||||||
cssFileObject.href === `${location.origin}/css/main.css`,
|
|
||||||
)[0][1];
|
|
||||||
Object.entries(selectedCSS.rules).filter(
|
|
||||||
([key, rule]) =>
|
|
||||||
rule.selectorText ==
|
|
||||||
".carousel > .carousel-inner > .carousel-item > img",
|
|
||||||
)[0][1].style.height = `calc(100vh - ${navbarHeight}px)`;
|
|
||||||
// console.log('ok');
|
// console.log('ok');
|
||||||
}, 1);
|
}, 1);
|
||||||
|
|
||||||
|
@ -15,64 +15,52 @@ const translationTree = {
|
|||||||
"/": {
|
"/": {
|
||||||
"en-US": {
|
"en-US": {
|
||||||
"intro-1": "Welcome to Adventure AI Labs!",
|
"intro-1": "Welcome to Adventure AI Labs!",
|
||||||
"description-1":
|
"description-1": "Embark on an unforgettable adventure with your favorite characters and friends! Visit familiar and unfamiliar locations..",
|
||||||
"Embark on an unforgettable adventure with your favorite characters and friends! Visit familiar and unfamiliar locations..",
|
|
||||||
"button-1": '<i class="fas fa-users"></i> Start the adventure',
|
"button-1": '<i class="fas fa-users"></i> Start the adventure',
|
||||||
|
|
||||||
"intro-2": "Become a gamemaster",
|
"intro-2": "Become a gamemaster",
|
||||||
"description-2":
|
"description-2": "Or you can become the gamemaster of the party and lead the plot of the characters and players like a creator god who tells an amazing story.",
|
||||||
"Or you can become the gamemaster of the party and lead the plot of the characters and players like a creator god who tells an amazing story.",
|
|
||||||
"button-2": '<i class="fas fa-book"></i> Documentation',
|
"button-2": '<i class="fas fa-book"></i> Documentation',
|
||||||
|
|
||||||
"intro-3": "Needed a more gold..",
|
"intro-3": "Needed a more gold..",
|
||||||
"description-3":
|
"description-3": "The project exists purely on a voluntary basis and the enthusiasm of the author, so if you want to help financially, then it will be very cool (:",
|
||||||
"The project exists purely on a voluntary basis and the enthusiasm of the author, so if you want to help financially, then it will be very cool (:",
|
|
||||||
"button-3": '<i class="fab fa-bitcoin"></i> Donate',
|
"button-3": '<i class="fab fa-bitcoin"></i> Donate',
|
||||||
},
|
},
|
||||||
"ru-RU": {
|
"ru-RU": {
|
||||||
"intro-1": "Добро пожаловать в Adventure AI Labs!",
|
"intro-1": "Добро пожаловать в Adventure AI Labs!",
|
||||||
"description-1":
|
"description-1": "Отправьтесь в незабываемое приключение вместе со своими любимыми персонажами и друзьями! Посетите знакомые и незнакомые вам локации..",
|
||||||
"Отправьтесь в незабываемое приключение вместе со своими любимыми персонажами и друзьями! Посетите знакомые и незнакомые вам локации..",
|
|
||||||
"button-1": '<i class="fas fa-users"></i> Начать приключение',
|
"button-1": '<i class="fas fa-users"></i> Начать приключение',
|
||||||
|
|
||||||
"intro-2": "Стань мастером партии",
|
"intro-2": "Стань мастером партии",
|
||||||
"description-2":
|
"description-2": "Либо же ты можешь стать мастером партии и вести сюжет персонажей и игроков словно бог-творец, который рассказывает удивительную историю.",
|
||||||
"Либо же ты можешь стать мастером партии и вести сюжет персонажей и игроков словно бог-творец, который рассказывает удивительную историю.",
|
|
||||||
"button-2": '<i class="fas fa-book"></i> Документация',
|
"button-2": '<i class="fas fa-book"></i> Документация',
|
||||||
|
|
||||||
"intro-3": "Нужно больше золота..",
|
"intro-3": "Нужно больше золота..",
|
||||||
"description-3":
|
"description-3": "Проект существует сугубо на добровольном начале и энтузиазме автора, поэтому если вы захотите помочь финансово, то это будет очень круто (:",
|
||||||
"Проект существует сугубо на добровольном начале и энтузиазме автора, поэтому если вы захотите помочь финансово, то это будет очень круто (:",
|
|
||||||
"button-3": '<i class="fab fa-bitcoin"></i> Задонатить',
|
"button-3": '<i class="fab fa-bitcoin"></i> Задонатить',
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
"/connect": {
|
"/connect": {
|
||||||
"en-US": {
|
"en-US": {
|
||||||
title: "Server list",
|
title: "Server list",
|
||||||
"enable-cookie-cnt": "Enable Cookie!",
|
"enable-cookie-cnt": "Enable Cookie!",
|
||||||
"enable-cookie-text-cnt":
|
"enable-cookie-text-cnt": "To work with servers, accept the cookie agreement",
|
||||||
"To work with servers, accept the cookie agreement",
|
|
||||||
"accept-cookie-show-btn": "Show",
|
"accept-cookie-show-btn": "Show",
|
||||||
|
|
||||||
exampleModalLabel: '<i class="fas fa-cookie"></i> Accept cookies',
|
exampleModalLabel: "<i class=\"fas fa-cookie\"></i> Accept cookies",
|
||||||
cookiesText:
|
cookiesText: "We are obliged to warn you that the AI Adventure Labs protocol client uses cookies for: authorization, storage of session keys and user consent to the use of cookies",
|
||||||
"We are obliged to warn you that the AI Adventure Labs protocol client uses cookies for: authorization, storage of session keys and user consent to the use of cookies",
|
|
||||||
"accept-cookie-btn": "Accept",
|
"accept-cookie-btn": "Accept",
|
||||||
loading: "Loading of servers...",
|
loading: "Loading of servers...",
|
||||||
|
|
||||||
titleDenial:
|
titleDenial: '<i class="fas fa-exclamation-triangle"></i> Denial of responsibility',
|
||||||
'<i class="fas fa-exclamation-triangle"></i> Denial of responsibility',
|
denialText: 'The server data does not belong to a centrally moderated foundation, community or individual, as well as to the developer of the software, program interface (protocol client), or protocol developer. Any content posted on servers is posted by the server administrator and can be absolutely anything. <b>You assume all risks for the content and connect to the server at your own peril and risk!</b>',
|
||||||
denialText:
|
|
||||||
"The server data does not belong to a centrally moderated foundation, community or individual, as well as to the developer of the software, program interface (protocol client), or protocol developer. Any content posted on servers is posted by the server administrator and can be absolutely anything. <b>You assume all risks for the content and connect to the server at your own peril and risk!</b>",
|
|
||||||
"accept-denial-btn": "Accept",
|
"accept-denial-btn": "Accept",
|
||||||
|
|
||||||
titleCustomServerConnect:
|
titleCustomServerConnect: '<i class="fas fa-wifi"></i> Connect to remote server',
|
||||||
'<i class="fas fa-wifi"></i> Connect to remote server',
|
titleCustomConnect: 'Connection Data',
|
||||||
titleCustomConnect: "Connection Data",
|
addressCustomConnect: 'Enter address of server (without ws(s):// and port)',
|
||||||
addressCustomConnect:
|
portCustomConnect: 'Enter port of server',
|
||||||
"Enter address of server (without ws(s):// and port)",
|
tlsCustomConnect: 'Check this if server use TLS (wss:// connection):',
|
||||||
portCustomConnect: "Enter port of server",
|
|
||||||
tlsCustomConnect: "Check this if server use TLS (wss:// connection):",
|
|
||||||
"custom-connect-btn": "Connect",
|
"custom-connect-btn": "Connect",
|
||||||
|
|
||||||
"server-name-tbl": "Name",
|
"server-name-tbl": "Name",
|
||||||
@ -89,28 +77,23 @@ const translationTree = {
|
|||||||
"ru-RU": {
|
"ru-RU": {
|
||||||
title: "Список серверов",
|
title: "Список серверов",
|
||||||
"enable-cookie-cnt": "Примите соглашение о Cookie!",
|
"enable-cookie-cnt": "Примите соглашение о Cookie!",
|
||||||
"enable-cookie-text-cnt":
|
"enable-cookie-text-cnt": "Для работы с серверами, примите соглашение об использовании файлов Cookie",
|
||||||
"Для работы с серверами, примите соглашение об использовании файлов Cookie",
|
|
||||||
"accept-cookie-show-btn": "Посмотреть",
|
"accept-cookie-show-btn": "Посмотреть",
|
||||||
|
|
||||||
exampleModalLabel: '<i class="fas fa-cookie"></i> Вопрос с печеньками',
|
exampleModalLabel: "<i class=\"fas fa-cookie\"></i> Вопрос с печеньками",
|
||||||
cookiesText:
|
cookiesText: "Мы обязаны вас предупредить, что клиент протокола AI Adventure Labs использует файлы куки для: авторизации, хранения ключей сессии и согласия пользователя на использование куки",
|
||||||
"Мы обязаны вас предупредить, что клиент протокола AI Adventure Labs использует файлы куки для: авторизации, хранения ключей сессии и согласия пользователя на использование куки",
|
|
||||||
"accept-cookie-btn": "Принять",
|
"accept-cookie-btn": "Принять",
|
||||||
loading: "Загрузка серверов...",
|
loading: "Загрузка серверов...",
|
||||||
|
|
||||||
titleDenial:
|
titleDenial: '<i class="fas fa-exclamation-triangle"></i> Отказ от отвественности',
|
||||||
'<i class="fas fa-exclamation-triangle"></i> Отказ от отвественности',
|
denialText: 'Данные сервера не принадлежат централизованно-модерируемому фонду, сообществу или частному лицу, а также разработчику программного обеспечения, програмного интерфейса (клиента протокола), разработчику протокола. Любой размещённый на серверах контент размещается администратором сервера и может быть абсолютно любым. <b>Все риски за контент вы берёте полностью на себя и подключаетесь к серверу на свой страх и риск!</b>',
|
||||||
denialText:
|
|
||||||
"Данные сервера не принадлежат централизованно-модерируемому фонду, сообществу или частному лицу, а также разработчику программного обеспечения, програмного интерфейса (клиента протокола), разработчику протокола. Любой размещённый на серверах контент размещается администратором сервера и может быть абсолютно любым. <b>Все риски за контент вы берёте полностью на себя и подключаетесь к серверу на свой страх и риск!</b>",
|
|
||||||
"accept-denial-btn": "Принять",
|
"accept-denial-btn": "Принять",
|
||||||
|
|
||||||
titleCustomServerConnect:
|
titleCustomServerConnect: '<i class="fas fa-wifi"></i> Прямое подключение к серверу',
|
||||||
'<i class="fas fa-wifi"></i> Прямое подключение к серверу',
|
titleCustomConnect: 'Данные подключения',
|
||||||
titleCustomConnect: "Данные подключения",
|
addressCustomConnect: 'Адрес сервера (без порта и ws(s)://)',
|
||||||
addressCustomConnect: "Адрес сервера (без порта и ws(s)://)",
|
portCustomConnect: 'Порт сервера',
|
||||||
portCustomConnect: "Порт сервера",
|
tlsCustomConnect: 'Использовать защищённое соединение (TLS) (wss://):',
|
||||||
tlsCustomConnect: "Использовать защищённое соединение (TLS) (wss://):",
|
|
||||||
"custom-connect-btn": "Подключиться",
|
"custom-connect-btn": "Подключиться",
|
||||||
|
|
||||||
"server-name-tbl": "Имя сервера",
|
"server-name-tbl": "Имя сервера",
|
||||||
@ -123,40 +106,33 @@ const translationTree = {
|
|||||||
"connect-to-server-btn": "Подключиться",
|
"connect-to-server-btn": "Подключиться",
|
||||||
"connection-failed": "Соединение оборвалось",
|
"connection-failed": "Соединение оборвалось",
|
||||||
"retry-connecting": "Повторить попытку",
|
"retry-connecting": "Повторить попытку",
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
"/connect-area": {
|
"/connect-area": {
|
||||||
"en-US": {},
|
"en-US": {},
|
||||||
"ru-RU": {},
|
"ru-RU": {}
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
Object.entries(translationTree).forEach(([page, langs]) => {
|
Object.entries(translationTree).forEach(([page, langs]) => {
|
||||||
Object.entries(langs).forEach(([lang]) => {
|
Object.entries(langs).forEach(([lang]) => {
|
||||||
translationTree[page][lang] = {
|
translationTree[page][lang] = {...translationTree[page][lang], ...metaTranslate[lang]};
|
||||||
...translationTree[page][lang],
|
|
||||||
...metaTranslate[lang],
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(translationTree);
|
console.log(translationTree);
|
||||||
|
|
||||||
const simpleLangTags = {
|
const simpleLangTags = {
|
||||||
ru: "ru-RU",
|
"ru": "ru-RU",
|
||||||
en: "en-US",
|
"en": "en-US",
|
||||||
};
|
};
|
||||||
|
|
||||||
function tr(page, id) {
|
function tr (page, id) {
|
||||||
let lang = navigator.language || navigator.userLanguage;
|
let lang = navigator.language || navigator.userLanguage;
|
||||||
lang =
|
lang = lang.split("-").length === 1 ? (simpleLangTags[lang] ?? "en-US") : lang;
|
||||||
lang.split("-").length === 1 ? (simpleLangTags[lang] ?? "en-US") : lang;
|
|
||||||
// Britains sucks, Sir Keir Starmer (Sir Dicksucker) is faggot, he is sucking a muslim's dicks.
|
// Britains sucks, Sir Keir Starmer (Sir Dicksucker) is faggot, he is sucking a muslim's dicks.
|
||||||
if (lang === "en-UK") lang = "en-US";
|
if (lang === "en-UK")
|
||||||
|
lang = "en-US";
|
||||||
// console.log("lang is", lang);
|
// console.log("lang is", lang);
|
||||||
return (
|
return (translationTree[page]?.[lang]?.[id] ?? translationTree[page]?.["en-US"][id]) ?? null
|
||||||
translationTree[page]?.[lang]?.[id] ??
|
|
||||||
translationTree[page]?.["en-US"][id] ??
|
|
||||||
null
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,6 @@
|
|||||||
class Logger {
|
class Logger {
|
||||||
static log (...p) {
|
static log (...p) {
|
||||||
console.log("[", new Date(), "]:", ...p);
|
console.log("[", new Date(), "]:", ...p);
|
||||||
}
|
|
||||||
static warn (...p) {
|
|
||||||
console.warn("[", new Date(), "]:", ...p);
|
|
||||||
}
|
|
||||||
static debug (...p) {
|
|
||||||
console.debug("[", new Date(), "]:", ...p);
|
|
||||||
}
|
}
|
||||||
static wslog (...p) {
|
static wslog (...p) {
|
||||||
// return;
|
// return;
|
||||||
|
1597
server/package-lock.json
generated
1597
server/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,17 +1,13 @@
|
|||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"aes-js": "^3.1.2",
|
"aes-js": "^3.1.2",
|
||||||
"bcrypt": "^6.0.0",
|
|
||||||
"crypto": "^1.0.1",
|
"crypto": "^1.0.1",
|
||||||
"express": "^4.19.2",
|
"express": "^4.19.2",
|
||||||
"fs": "^0.0.1-security",
|
"fs": "^0.0.1-security",
|
||||||
"https": "^1.0.0",
|
"https": "^1.0.0",
|
||||||
"md5": "^2.3.0",
|
|
||||||
"net": "^1.0.2",
|
"net": "^1.0.2",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"selfsigned": "^2.4.1",
|
"selfsigned": "^2.4.1",
|
||||||
"sequelize": "^6.37.7",
|
|
||||||
"sqlite3": "^5.1.7",
|
|
||||||
"ws": "^7.5.10"
|
"ws": "^7.5.10"
|
||||||
},
|
},
|
||||||
"name": "ai-adventure-labs-server",
|
"name": "ai-adventure-labs-server",
|
||||||
|
@ -4,8 +4,6 @@ const parser = require("./packet-parser");
|
|||||||
const frontend = require("./frontend");
|
const frontend = require("./frontend");
|
||||||
const websocket = require("./ws-handler");
|
const websocket = require("./ws-handler");
|
||||||
const crypt = require("./crypt");
|
const crypt = require("./crypt");
|
||||||
const Database = require("./database");
|
|
||||||
|
|
||||||
const EventEmitter = require("events");
|
const EventEmitter = require("events");
|
||||||
const net = require("net");
|
const net = require("net");
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
@ -13,29 +11,10 @@ const path = require("path");
|
|||||||
|
|
||||||
const { API } = require("./api");
|
const { API } = require("./api");
|
||||||
|
|
||||||
// So.. what part of the WeakMap/Server class made you think
|
|
||||||
// Oh! Developer of this AI client agrees that
|
|
||||||
// [Object Server].isLocked can be writeable?
|
|
||||||
const isLockedServer = new WeakMap();
|
|
||||||
|
|
||||||
class Server extends EventEmitter {
|
class Server extends EventEmitter {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.on("config.update", ({ oldConfig, newConfig }) => {});
|
this.on("config.update", ({ oldConfig, newConfig }) => {});
|
||||||
//this.isLocked = false;
|
|
||||||
isLockedServer.set(this, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
set isLocked (_) {
|
|
||||||
throw new Error("isLocked not writeable");
|
|
||||||
}
|
|
||||||
|
|
||||||
get isLocked () {
|
|
||||||
return isLockedServer.get(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleLock (mode = true) {
|
|
||||||
isLockedServer.set(this, !!mode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
run(address, port) {
|
run(address, port) {
|
||||||
@ -206,8 +185,7 @@ const defaultConfigData = {
|
|||||||
},
|
},
|
||||||
database: {
|
database: {
|
||||||
dialect: "sqlite",
|
dialect: "sqlite",
|
||||||
storage: "./data.db",
|
path: "./data",
|
||||||
logging: false
|
|
||||||
},
|
},
|
||||||
ai: {
|
ai: {
|
||||||
apiType: "kobold",
|
apiType: "kobold",
|
||||||
@ -221,12 +199,63 @@ const defaultConfigData = {
|
|||||||
|
|
||||||
global.server = new Server();
|
global.server = new Server();
|
||||||
cfgHandle(global.server, defaultConfigData, logger);
|
cfgHandle(global.server, defaultConfigData, logger);
|
||||||
global.database = new Database(global.config.database);
|
|
||||||
global.database.init()
|
logger.log("Current config:", global.config);
|
||||||
.then(() => {
|
|
||||||
logger.log("Current config:", global.config);
|
global.server.run(global.config.server.address, global.config.server.port);
|
||||||
global.server.run(global.config.server.address, global.config.server.port);
|
|
||||||
})
|
/*
|
||||||
.catch((err) => {
|
if (global.config.server.secureMode) {
|
||||||
throw err;
|
async function initEncryption () {
|
||||||
|
const datadir = global.config.server["ssl-path"] ?? path.join(__dirname, "/encryption");
|
||||||
|
|
||||||
|
function generate () {
|
||||||
|
logger.log("Generate new keys...");
|
||||||
|
global.openSSH = crypt.generateOpenSSH({
|
||||||
|
commonName: global.config.server.info.name
|
||||||
|
}, {
|
||||||
|
days: 365,
|
||||||
|
pkcs7: true,
|
||||||
|
clientCertificate: true,
|
||||||
|
clientCertificateCN: "AI Adventure Labs",
|
||||||
|
algorithm: 'sha256',
|
||||||
|
keySize: 2048,
|
||||||
});
|
});
|
||||||
|
fs.writeFileSync(path.join(datadir, "public.pem"), global.openSSH.public_key.bytes);
|
||||||
|
fs.writeFileSync(path.join(datadir, "private.pem"), global.openSSH.private_key.bytes);
|
||||||
|
fs.writeFileSync(path.join(datadir, "cert.crt"), global.openSSH.cert);
|
||||||
|
}
|
||||||
|
|
||||||
|
const readFilePromise = new Promise((resolve, reject) => {
|
||||||
|
fs.readFile(path.join(datadir, "public.pem"), (err, public_key) => {
|
||||||
|
if (err) {
|
||||||
|
return resolve(generate());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
public_key = new crypt.CryptKey(public_key);
|
||||||
|
const private_key = new crypt.CryptKey(fs.readFileSync(path.join(datadir, "private.pem")));
|
||||||
|
const cert = fs.readFileSync(path.join(datadir, "cert.crt"));
|
||||||
|
|
||||||
|
global.openSSH = new crypt.OpenSSH({
|
||||||
|
public_key, private_key, cert
|
||||||
|
});
|
||||||
|
resolve();
|
||||||
|
} catch (_) {
|
||||||
|
resolve(generate());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await readFilePromise;
|
||||||
|
|
||||||
|
logger.log(`loaded from ${datadir}`);
|
||||||
|
};
|
||||||
|
initEncryption().then(() => {
|
||||||
|
server.run(global.config.server.address, global.config.server.port);
|
||||||
|
}).catch(e => {
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
server.run(global.config.server.address, global.config.server.port);
|
||||||
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user