notherbase-fs 4.4.6 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/notherbase-fs.js +39 -32
- package/package.json +1 -1
- package/public/js/base.js +1 -41
- package/server/creation.js +117 -0
- package/server/spirit-world.js +147 -0
- package/server/spirit.js +83 -0
- package/{controllers → server}/user.js +36 -37
- package/test/coast/tall-beach/nono-cove/index.ejs +3 -3
- package/test/the-front/add-gold.js +4 -12
- package/test/the-front/index.ejs +12 -6
- package/test2/the-front/index.ejs +0 -99
- package/views/explorer.ejs +1 -1
- package/views/footer.ejs +1 -1
- package/controllers/creation.js +0 -181
- package/controllers/spirit-world.js +0 -247
- package/models/index.js +0 -31
- package/models/spirit.js +0 -235
- package/test2/the-front/add-gold.js +0 -14
- package/test2/the-front/check/check.css +0 -3
- package/test2/the-front/check/emailTime.js +0 -10
- package/test2/the-front/check/flip.js +0 -10
- package/test2/the-front/check/index.ejs +0 -55
- package/test2/the-front/check/save-input.js +0 -8
- package/test2/the-front/keeper/clipboards.js +0 -134
- package/test2/the-front/keeper/index.ejs +0 -81
- package/test2/the-front/keeper/keeper.css +0 -158
- package/test2/the-front/keeper/keeper.js +0 -140
- package/test2/the-front/test-page/emailTime.js +0 -9
- package/test2/the-front/test-page/index.ejs +0 -74
- /package/{models → server}/send-mail.js +0 -0
- /package/{controllers → server}/util.js +0 -0
- /package/test/{the-front/void → void}/index.ejs +0 -0
- /package/test/{the-front/void → void}/void.css +0 -0
package/notherbase-fs.js
CHANGED
|
@@ -1,30 +1,35 @@
|
|
|
1
1
|
import dotenv from "dotenv";
|
|
2
2
|
dotenv.config();
|
|
3
|
-
import
|
|
3
|
+
import Spirit from "./server/spirit.js";
|
|
4
|
+
import SendMail from "./server/send-mail.js";
|
|
4
5
|
import { Server } from "socket.io";
|
|
5
6
|
import express from "express";
|
|
6
7
|
import http from 'http';
|
|
7
8
|
import { fileURLToPath } from 'node:url';
|
|
8
9
|
const __dirname = fileURLToPath(new URL('./', import.meta.url));
|
|
9
|
-
import Creation from "./
|
|
10
|
-
import SpiritWorld from "./
|
|
10
|
+
import Creation from "./server/creation.js";
|
|
11
|
+
import SpiritWorld from "./server/spirit-world.js";
|
|
11
12
|
import favicon from 'serve-favicon';
|
|
12
13
|
import session from 'express-session';
|
|
13
14
|
import MongoStore from 'connect-mongo';
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
|
-
* The engine that runs
|
|
18
|
+
* The engine that runs nother bases.
|
|
18
19
|
*/
|
|
19
20
|
class NotherBaseFS {
|
|
20
21
|
constructor(globals = {}, bases = {}) {
|
|
21
22
|
this.bases = bases;
|
|
23
|
+
|
|
24
|
+
this.initServer();
|
|
25
|
+
}
|
|
22
26
|
|
|
27
|
+
initServer() {
|
|
23
28
|
this.app = express();
|
|
24
29
|
this.server = http.createServer(this.app);
|
|
25
30
|
this.io = new Server(this.server);
|
|
26
31
|
this.spiritWorld = new SpiritWorld(this.io);
|
|
27
|
-
this.creation = new Creation(bases);
|
|
32
|
+
this.creation = new Creation(this.bases);
|
|
28
33
|
|
|
29
34
|
//set view engine
|
|
30
35
|
this.app.set("view engine", "ejs");
|
|
@@ -43,11 +48,12 @@ class NotherBaseFS {
|
|
|
43
48
|
//be safe, needs to be before session
|
|
44
49
|
if (process.env.PRODUCTION == "true") this.app.set('trust proxy', 2);
|
|
45
50
|
|
|
46
|
-
|
|
51
|
+
//provide multiple base support
|
|
47
52
|
let store = MongoStore.create({ mongoUrl: process.env.MONGODB_URI });
|
|
53
|
+
let baseKeys = Object.keys(this.bases);
|
|
48
54
|
for (let i = 0; i < baseKeys.length; i++) {
|
|
49
55
|
this.bases[baseKeys[i]].static = express.static(this.bases[baseKeys[i]].directory + "/public");
|
|
50
|
-
this.bases[baseKeys[i]].favicon = favicon(this.bases[baseKeys[i]].directory + this.bases[baseKeys[i]].icon);
|
|
56
|
+
if (this.bases[baseKeys[i]].icon) this.bases[baseKeys[i]].favicon = favicon(this.bases[baseKeys[i]].directory + this.bases[baseKeys[i]].icon);
|
|
51
57
|
this.bases[baseKeys[i]].session = session({
|
|
52
58
|
store: store,
|
|
53
59
|
secret: process.env.SECRET,
|
|
@@ -61,39 +67,40 @@ class NotherBaseFS {
|
|
|
61
67
|
});
|
|
62
68
|
}
|
|
63
69
|
|
|
64
|
-
|
|
70
|
+
|
|
65
71
|
this.app.use((req, res, next) => {
|
|
66
72
|
let split = req.hostname.split(".");
|
|
67
73
|
if (split.length > 2) {
|
|
68
74
|
if (split[split.length - 2].length < 3) req.hosting = split[split.length - 3] + split[split.length - 2];
|
|
69
75
|
else req.hosting = split[split.length - 2];
|
|
70
76
|
}
|
|
71
|
-
else req.hosting = split[0];
|
|
77
|
+
else req.hosting = split[0];
|
|
72
78
|
|
|
73
79
|
req.contentPath = this.bases[req.hosting].directory;
|
|
74
80
|
next();
|
|
75
81
|
});
|
|
76
82
|
|
|
83
|
+
// allows us to get static files like css
|
|
84
|
+
this.app.use((req, res, next) => {
|
|
85
|
+
this.bases[req.hosting].static(req, res, next);
|
|
86
|
+
});
|
|
87
|
+
this.app.use(express.static(`${__dirname}/public`));
|
|
88
|
+
|
|
77
89
|
this.app.use((req, res, next) => {
|
|
78
|
-
this.bases[req.hosting].favicon(req, res, next);
|
|
90
|
+
if (this.bases[req.hosting].favicon) this.bases[req.hosting].favicon(req, res, next);
|
|
91
|
+
else next();
|
|
79
92
|
});
|
|
80
93
|
|
|
81
94
|
//enable cookies
|
|
82
95
|
this.app.use((req, res, next) => {
|
|
83
96
|
this.bases[req.hosting].session(req, res, next);
|
|
84
97
|
});
|
|
85
|
-
|
|
86
|
-
// allows us to get static files like css
|
|
87
|
-
this.app.use((req, res, next) => {
|
|
88
|
-
this.bases[req.hosting].static(req, res, next);
|
|
89
|
-
});
|
|
90
|
-
this.app.use(express.static(`${__dirname}/public`));
|
|
91
98
|
|
|
92
99
|
//provide database access and etc to use in routes
|
|
93
100
|
this.app.use(async (req, res, next) => {
|
|
94
|
-
req.
|
|
95
|
-
req.
|
|
96
|
-
req.user = req.session?.currentUser ? await req.
|
|
101
|
+
req.Spirit = Spirit;
|
|
102
|
+
req.SendMail = SendMail;
|
|
103
|
+
req.user = req.session?.currentUser ? await req.Spirit.findOne({ service: "user", username: req.session.currentUser }) : null;
|
|
97
104
|
req.lock = false;
|
|
98
105
|
// enables sessions only if the protocol is https and we are in production, or if we are in development
|
|
99
106
|
req.sessionsEnabled = (process.env.PRODUCTION == "true" && req.secure) || process.env.PRODUCTION == "false";
|
|
@@ -101,20 +108,20 @@ class NotherBaseFS {
|
|
|
101
108
|
});
|
|
102
109
|
|
|
103
110
|
//destroy session if it is not authorized
|
|
104
|
-
this.app.use(async (req, res, next) => {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
// this.app.use(async (req, res, next) => {
|
|
112
|
+
// if (req.session.currentUser) {
|
|
113
|
+
// if (req.user?.memory?.data?.sessions?.[req.session.id]) {
|
|
114
|
+
// if (req.user.memory.data.sessions[req.session.id] < Date.now()) {
|
|
115
|
+
// req.session.regenerate(() => {});
|
|
116
|
+
// delete req.user.memory.data.sessions[req.session.id];
|
|
117
|
+
// await req.user.commit();
|
|
118
|
+
// }
|
|
119
|
+
// }
|
|
120
|
+
// else req.session.regenerate(() => {});
|
|
121
|
+
// }
|
|
115
122
|
|
|
116
|
-
|
|
117
|
-
});
|
|
123
|
+
// next();
|
|
124
|
+
// });
|
|
118
125
|
|
|
119
126
|
//spirit world routes
|
|
120
127
|
this.app.use("/s", this.spiritWorld.router);
|
package/package.json
CHANGED
package/public/js/base.js
CHANGED
|
@@ -108,52 +108,12 @@ class Base {
|
|
|
108
108
|
* @param {ObjectID} id The id of the spirit to load.
|
|
109
109
|
* @returns Spirit world response.
|
|
110
110
|
*/
|
|
111
|
-
|
|
112
|
-
let response = await $.post("/s/loadAll", JSON.stringify({ service, scope, data, id }));
|
|
113
|
-
|
|
114
|
-
return response;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Loads a single spirit of a certain service.
|
|
119
|
-
* @param {String} service The name of the spirit to load.
|
|
120
|
-
* @param {String} scope Defaults to local, else global.
|
|
121
|
-
* @param {Object} data Data to filter the spirits by.
|
|
122
|
-
* @param {ObjectID} id The id of the spirit to load.
|
|
123
|
-
* @returns Spirit world response.
|
|
124
|
-
*/
|
|
125
|
-
load = async (service, scope = "local", data = {}, id = null) => {
|
|
111
|
+
load = async (service, scope = "local", data = null, id = null) => {
|
|
126
112
|
let response = await $.post("/s/load", JSON.stringify({ service, scope, data, id }));
|
|
127
113
|
|
|
128
114
|
return response;
|
|
129
115
|
}
|
|
130
116
|
|
|
131
|
-
/**
|
|
132
|
-
* Saves a spirit of a certain service.
|
|
133
|
-
* @param {String} service The name of the spirit to save.
|
|
134
|
-
* @param {String} scope Defaults to local, else global.
|
|
135
|
-
* @param {Object} data Data to save the spirit with.
|
|
136
|
-
* @param {ObjectID} id The id of the spirit to save.
|
|
137
|
-
* @returns Spirit world response.
|
|
138
|
-
*/
|
|
139
|
-
save = async (service, scope = "local", data = {}, id = null) => {
|
|
140
|
-
let response = await $.post("/s/save", JSON.stringify({ service, scope, data, id }));
|
|
141
|
-
return response;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Deletes a spirit of a certain service.
|
|
146
|
-
* @param {String} service The name of the spirit to delete.
|
|
147
|
-
* @param {String} scope Defaults to local, else global.
|
|
148
|
-
* @param {Object} data Data to filter the spirits by.
|
|
149
|
-
* @param {ObjectID} id The id of the spirit to delete.
|
|
150
|
-
* @returns Spirit world response.
|
|
151
|
-
*/
|
|
152
|
-
delete = async (service, scope = "local", data = {}, id = null) => {
|
|
153
|
-
let response = await $.post("/s/delete", JSON.stringify({ service, scope, data, id }));
|
|
154
|
-
return response;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
117
|
/**
|
|
158
118
|
* Appends html to the head.
|
|
159
119
|
* @param {String} html The html to append.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Creation is all the renedered pages in a base.
|
|
6
|
+
*/
|
|
7
|
+
export default class Creation {
|
|
8
|
+
constructor(bases = {}) {
|
|
9
|
+
this.bases = bases;
|
|
10
|
+
this.router = express.Router();
|
|
11
|
+
|
|
12
|
+
this.router.get("/", this.explore);
|
|
13
|
+
this.router.get("/the-front", this.explore);
|
|
14
|
+
this.router.get(`/:region`, this.lock, this.explore);
|
|
15
|
+
this.router.get(`/:region/:area`, this.lock, this.explore);
|
|
16
|
+
this.router.get(`/:region/:area/:poi`, this.lock, this.explore);
|
|
17
|
+
this.router.get(`/:region/:area/:poi/:detail`, this.lock, this.explore);
|
|
18
|
+
|
|
19
|
+
//void
|
|
20
|
+
this.router.use(function(req, res) {
|
|
21
|
+
res.redirect("/void");
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* This middleware requires a user to login to access affected routes.
|
|
27
|
+
* @param {Object} req An Express.js request.
|
|
28
|
+
* @param {Object} res An Express.js response.
|
|
29
|
+
* @param {Function} next next()
|
|
30
|
+
*/
|
|
31
|
+
lock = (req, res, next) => {
|
|
32
|
+
req.lock = true;
|
|
33
|
+
next();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* This route renders a page and sends it to the client.
|
|
38
|
+
* @param {Object} req An Express.js request.
|
|
39
|
+
* @param {Object} res An Express.js response.
|
|
40
|
+
* @param {Function} next next()
|
|
41
|
+
*/
|
|
42
|
+
explore = async (req, res, next) => {
|
|
43
|
+
try {
|
|
44
|
+
req.main = `index`;
|
|
45
|
+
if (req.params.detail) req.main = req.params.detail + "\\" + req.main;
|
|
46
|
+
if (req.params.poi) req.main = req.params.poi + "\\" + req.main;
|
|
47
|
+
if (req.params.area) req.main = req.params.area + "\\" + req.main;
|
|
48
|
+
if (req.params.region) req.main = req.params.region + "\\" + req.main;
|
|
49
|
+
else req.main = "the-front\\" + req.main;
|
|
50
|
+
req.main = req.contentPath + "\\" + req.main;
|
|
51
|
+
req.preprocess = "_preprocess";
|
|
52
|
+
if (req.params.detail) req.preprocess = req.params.detail + "\\" + req.preprocess;
|
|
53
|
+
if (req.params.poi) req.preprocess = req.params.poi + "\\" + req.preprocess;
|
|
54
|
+
if (req.params.area) req.preprocess = req.params.area + "\\" + req.preprocess;
|
|
55
|
+
if (req.params.region) req.preprocess = req.params.region + "\\" + req.preprocess;
|
|
56
|
+
else req.preprocess = "the-front\\" + req.preprocess;
|
|
57
|
+
req.preprocess = req.contentPath + "\\" + req.preprocess;
|
|
58
|
+
req.siteTitle = `${this.bases[req.hosting].title} - ${req.params.detail || req.params.poi || req.params.area || req.params.region || "Home"}`;
|
|
59
|
+
req.toRender = "explorer";
|
|
60
|
+
|
|
61
|
+
if (fs.existsSync(req.main + ".ejs")) {
|
|
62
|
+
let stats = await req.Spirit.findOne({ service: "stats", data: { route: req.path } });
|
|
63
|
+
if (stats == null) stats = new req.Spirit({ service: "stats", data: { route: req.path, visits: 0 } });
|
|
64
|
+
if (stats.data.visits) stats.data.visits++;
|
|
65
|
+
else stats.data.visits = 1;
|
|
66
|
+
await stats.commit();
|
|
67
|
+
|
|
68
|
+
let context = {
|
|
69
|
+
user: null,
|
|
70
|
+
siteTitle: req.siteTitle,
|
|
71
|
+
main: req.main,
|
|
72
|
+
query: req.query,
|
|
73
|
+
route: req.path,
|
|
74
|
+
requireUser: req.lock,
|
|
75
|
+
preprocessed: {}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (req.session.currentUser) {
|
|
79
|
+
let user = req.user;
|
|
80
|
+
context.user = {
|
|
81
|
+
data: user.data,
|
|
82
|
+
backups: user.backups,
|
|
83
|
+
_id: user._id,
|
|
84
|
+
parent: user.parent,
|
|
85
|
+
service: user.service,
|
|
86
|
+
_lastUpdate: user._lastUpdate
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
//preprocess
|
|
91
|
+
let preprocessScripts = fs.existsSync(req.preprocess) ? fs.readdirSync(req.preprocess) : [];
|
|
92
|
+
for (let preprocessScript of preprocessScripts) {
|
|
93
|
+
try {
|
|
94
|
+
let scriptPath = `${req.preprocess}/${preprocessScript}`;
|
|
95
|
+
|
|
96
|
+
if (fs.existsSync(scriptPath)) {
|
|
97
|
+
let script = await import(process.env.WINDOWS == "true" ? `file://${scriptPath}` : scriptPath);
|
|
98
|
+
let result = await script.default(req, context.user, this.io);
|
|
99
|
+
context.preprocessed[preprocessScript.split(".")[0]] = result;
|
|
100
|
+
}
|
|
101
|
+
else context.preprocessed[preprocessScript.split(".")[0]] = `Error: Script Not Found`;
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.log(error);
|
|
104
|
+
context.preprocessed[preprocessScript.split(".")[0]] = `Error: Server Error`;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
res.render(req.toRender, context);
|
|
109
|
+
}
|
|
110
|
+
else next();
|
|
111
|
+
}
|
|
112
|
+
catch(err) {
|
|
113
|
+
console.log(err);
|
|
114
|
+
res.status(500).send("Server Error");
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import { stripHtml } from "string-strip-html";
|
|
3
|
+
import { success, fail } from "./util.js";
|
|
4
|
+
import User from "./user.js";
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The spirit world is the API of a base.
|
|
9
|
+
*/
|
|
10
|
+
export default class SpiritWorld {
|
|
11
|
+
/**
|
|
12
|
+
* Sets up the spirit world.
|
|
13
|
+
* @param {Server} io
|
|
14
|
+
*/
|
|
15
|
+
constructor(io) {
|
|
16
|
+
this.io = io;
|
|
17
|
+
this.rooms = {};
|
|
18
|
+
this.io.on('connection', this.setupChat);
|
|
19
|
+
|
|
20
|
+
this.user = new User();
|
|
21
|
+
this.router = express.Router();
|
|
22
|
+
this.router.post("/load", this.load);
|
|
23
|
+
this.router.post("/serve", this.serve);
|
|
24
|
+
this.router.use("/user", this.user.router);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Sets up socket.io for instant messaging and etc.
|
|
29
|
+
* @param {*} socket
|
|
30
|
+
*/
|
|
31
|
+
setupChat = (socket) => {
|
|
32
|
+
try {
|
|
33
|
+
let roomName = socket.handshake.query.room;
|
|
34
|
+
socket.join(roomName);
|
|
35
|
+
let room = this.rooms[roomName];
|
|
36
|
+
if (room) room.users.push(socket.handshake.query.name);
|
|
37
|
+
else {
|
|
38
|
+
this.rooms[roomName] = {
|
|
39
|
+
users: [ socket.handshake.query.name ]
|
|
40
|
+
}
|
|
41
|
+
room = this.rooms[roomName];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this.io.to(roomName).emit("chat message", {
|
|
45
|
+
name: "Server",
|
|
46
|
+
time: Date.now(),
|
|
47
|
+
text: `${socket.handshake.query.name} has joined the room.`
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
this.io.to(roomName).emit("chat info", {
|
|
51
|
+
name: socket.handshake.query.room,
|
|
52
|
+
time: Date.now(),
|
|
53
|
+
data: {
|
|
54
|
+
users: room.users
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
socket.on("chat message", (msg) => {
|
|
59
|
+
this.io.to(roomName).emit("chat message", {
|
|
60
|
+
name: msg.name,
|
|
61
|
+
time: msg.time,
|
|
62
|
+
text: stripHtml(msg.text).result
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
socket.on('disconnect', () => {
|
|
67
|
+
room.users.splice(room.users.indexOf(socket.handshake.query.name));
|
|
68
|
+
|
|
69
|
+
this.io.to(roomName).emit("chat message", {
|
|
70
|
+
name: "Server",
|
|
71
|
+
time: Date.now(),
|
|
72
|
+
text: `${socket.handshake.query.name} has left the room.`
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
this.io.to(roomName).emit("chat info", {
|
|
76
|
+
name: socket.handshake.query.room,
|
|
77
|
+
time: Date.now(),
|
|
78
|
+
data: {
|
|
79
|
+
users: room.users
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
if (room.users.length < 1) delete this.rooms[roomName];
|
|
84
|
+
});
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.log(error);
|
|
87
|
+
fail(res, "Server error: Sockets");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* This API route requests all spirits of a kind from the database.
|
|
93
|
+
* @param {Object} req
|
|
94
|
+
* @param {Object} res
|
|
95
|
+
* @returns {Object} The requested spirits.
|
|
96
|
+
*/
|
|
97
|
+
load = async (req, res) => {
|
|
98
|
+
try {
|
|
99
|
+
let query = {
|
|
100
|
+
service: req.body.service,
|
|
101
|
+
parent: null
|
|
102
|
+
};
|
|
103
|
+
if (req.body.data) query.data = req.body.data;
|
|
104
|
+
if (req.body.id) query._id = req.body.id;
|
|
105
|
+
|
|
106
|
+
// if the scope is local, the parent is the user's id
|
|
107
|
+
if (req.body.scope === "local") {
|
|
108
|
+
if (req.user?._id) query.parent = req.user._id;
|
|
109
|
+
else {
|
|
110
|
+
fail(res, "User had no id on load()");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// recall all spirits with the given service name and parent
|
|
116
|
+
let spirits = await req.Spirit.find(query);
|
|
117
|
+
|
|
118
|
+
res.send(spirits);
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.log(error);
|
|
121
|
+
fail(res, "Server error");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* This API route runs a script on the server. Responds with the result.
|
|
127
|
+
* @param {Object} req
|
|
128
|
+
* @param {Object} res
|
|
129
|
+
*/
|
|
130
|
+
serve = async (req, res) => {
|
|
131
|
+
try {
|
|
132
|
+
let scriptPath = `${req.contentPath}${req.body.route}/${req.body.script}.js`;
|
|
133
|
+
|
|
134
|
+
let script, result = null;
|
|
135
|
+
|
|
136
|
+
if (fs.existsSync(scriptPath)) {
|
|
137
|
+
script = await import(process.env.WINDOWS == "true" ? `file://${scriptPath}` : scriptPath);
|
|
138
|
+
result = await script.default(req, req.user, this.io);
|
|
139
|
+
success(res, "Served.", result);
|
|
140
|
+
}
|
|
141
|
+
else fail(res, `Script not found: ${req.body.script} at ${scriptPath}`);
|
|
142
|
+
} catch (error) {
|
|
143
|
+
console.log(error);
|
|
144
|
+
fail(res, "Server error");
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
package/server/spirit.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import mongoose from "mongoose";
|
|
2
|
+
import dotenv from "dotenv";
|
|
3
|
+
dotenv.config();
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
// DB setup
|
|
7
|
+
mongoose.set('strictQuery', true);
|
|
8
|
+
mongoose.connection.on('connected', (err) => {
|
|
9
|
+
console.log(`Mongoose connected to db`);
|
|
10
|
+
});
|
|
11
|
+
mongoose.connection.on('error', (err) => {
|
|
12
|
+
console.log(`Mongoose ${err}`);
|
|
13
|
+
});
|
|
14
|
+
mongoose.connection.on('disconnected', () => {
|
|
15
|
+
console.log('Mongoose disconnected');
|
|
16
|
+
});
|
|
17
|
+
try {
|
|
18
|
+
mongoose.connect(process.env.MONGODB_URI);
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
console.log(`Mongoose on connect: ${err}`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Curated Mongoose.js documents for use in bases.
|
|
26
|
+
*/
|
|
27
|
+
let spiritSchema = new mongoose.Schema({
|
|
28
|
+
_lastUpdate: Number,
|
|
29
|
+
service: String,
|
|
30
|
+
parent: {
|
|
31
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
32
|
+
ref: "spirits",
|
|
33
|
+
required: false
|
|
34
|
+
},
|
|
35
|
+
data: {},
|
|
36
|
+
backups: []
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Commits new data to a spirit.
|
|
41
|
+
* @param {Object} data Data to save.
|
|
42
|
+
* @returns Updated
|
|
43
|
+
*/
|
|
44
|
+
spiritSchema.method("commit", async function(data = this.data, maxBackups = 5) {
|
|
45
|
+
this._lastUpdate = Date.now();
|
|
46
|
+
|
|
47
|
+
this.backups.unshift({
|
|
48
|
+
_lastUpdate: Date.now(),
|
|
49
|
+
data: this.data
|
|
50
|
+
});
|
|
51
|
+
if (maxBackups > 1) {
|
|
52
|
+
while (this.backups.length > maxBackups) this.backups.pop();
|
|
53
|
+
}
|
|
54
|
+
this.data = data;
|
|
55
|
+
this.markModified("backups");
|
|
56
|
+
this.markModified("data");
|
|
57
|
+
await this.save();
|
|
58
|
+
|
|
59
|
+
return "Updated";
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Restores a backup by its index.
|
|
64
|
+
* @param {Number} backupIndex The index of the backup to restore.
|
|
65
|
+
* @returns Status message.
|
|
66
|
+
*/
|
|
67
|
+
spiritSchema.method("restoreBackup", async function(backupIndex) {
|
|
68
|
+
if (backupIndex < 0 || backupIndex >= this.backups.length) return "Invalid backup index";
|
|
69
|
+
let backup = this.backups[backupIndex];
|
|
70
|
+
this.backups.unshift({
|
|
71
|
+
_lastUpdate: Date.now(),
|
|
72
|
+
data: this.data
|
|
73
|
+
});
|
|
74
|
+
this.data = backup.data;
|
|
75
|
+
this.markModified("backups");
|
|
76
|
+
this.markModified("data");
|
|
77
|
+
await this.save();
|
|
78
|
+
return "Restored";
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
let Spirit = mongoose.model('spirits', spiritSchema);
|
|
82
|
+
|
|
83
|
+
export default Spirit;
|