1st commit
This commit is contained in:
commit
159683d7ba
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.vscode
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "ui"]
|
||||||
|
path = ui
|
||||||
|
url = https://github.com/mydraft-cc/ui.git
|
1
.prettierignore
Normal file
1
.prettierignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
ui/*
|
10
package.json
Normal file
10
package.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"name": "mydraftcc-nodejs-server",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "NodeJs Server For mydraftcc/ui",
|
||||||
|
"scripts": {
|
||||||
|
"populatepublicserver": "rsync -ravH ui/build/* server/public/"
|
||||||
|
},
|
||||||
|
"author": "damien HENRY - https://www.mytinydc.com",
|
||||||
|
"license": "MIT Licence"
|
||||||
|
}
|
49
patch.sh
Executable file
49
patch.sh
Executable file
@ -0,0 +1,49 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
###############
|
||||||
|
## D.HENRY (https://www.mytindydc.com)
|
||||||
|
# Patchs are apply to be API nodejs compliant
|
||||||
|
# UserReport because i don't want third party in the running app
|
||||||
|
################################
|
||||||
|
|
||||||
|
echo "[INFO]Starting applying patchs..."
|
||||||
|
|
||||||
|
## Usefull for nodejs-server
|
||||||
|
match="(const API_URL.*)(https:\/\/api.mydraft.cc)(.*)$"
|
||||||
|
file="ui/src/wireframes/model/actions/loading.ts"
|
||||||
|
echo "[Patching]$match-$file"
|
||||||
|
# i replace https://api.mydraft.cc with ''
|
||||||
|
sed -i -E "s,$match,\1\3," "$file"
|
||||||
|
echo " [INFO]done"
|
||||||
|
|
||||||
|
## Usefull for nodejs-server
|
||||||
|
echo "[Patching]Replacing load route / with /get"
|
||||||
|
sed -i 's,${API_URL}/${args.tokenToRead},${API_URL}/get/${args.tokenToRead},' "$file"
|
||||||
|
if [ "$?" != "0" ];then exit 1;fi
|
||||||
|
|
||||||
|
echo " [INFO]done"
|
||||||
|
|
||||||
|
## Usefull for nodejs-server
|
||||||
|
## ContentType ERR Content-Type and application/json (needed for express body-parser)
|
||||||
|
echo "[Patching]ContentType-application/json"
|
||||||
|
sed -i -E "s,ContentType.*text\/json(.*)$,'Content-Type': 'application\/json\1," "$file"
|
||||||
|
if [ "$?" != "0" ];then exit 1;fi
|
||||||
|
echo " [INFO]done"
|
||||||
|
|
||||||
|
## Usefull for ... me :)
|
||||||
|
echo "[Patching]Removing UserReport from application"
|
||||||
|
## Remove UserReport support
|
||||||
|
if [ -f "ui/src/core/react/UserReport.tsx" ];then
|
||||||
|
rm -r ui/src/core/react/UserReport.tsx
|
||||||
|
if [ "$?" != "0" ];then exit 1;fi
|
||||||
|
fi
|
||||||
|
## Patch tsx
|
||||||
|
files=$(grep -r "UserReport" ui/src | awk -F ":" '{print $1}' |uniq |xargs)
|
||||||
|
for f in $files
|
||||||
|
do
|
||||||
|
echo " [INFO]Patching UserReport in $f"
|
||||||
|
sed -i -E 's/.*UserReport.*//' "$f"
|
||||||
|
if [ "$?" != "0" ];then exit 1;fi
|
||||||
|
done
|
||||||
|
echo " [INFO]done"
|
||||||
|
|
24
server/.gitignore
vendored
Normal file
24
server/.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
/data/*
|
||||||
|
/logs/*
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
/logs/*
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
9
server/README.md
Normal file
9
server/README.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# minimalist Nodejs server for mydraft application (https:/mydraft.cc)
|
||||||
|
|
||||||
|
## Swagger
|
||||||
|
|
||||||
|
## Installation (Docker)
|
||||||
|
|
||||||
|
## Execute
|
||||||
|
|
||||||
|
pay **ATTENTION**
|
138
server/app.js
Normal file
138
server/app.js
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
require("include-path")(["./lib"]);
|
||||||
|
const express = require("express");
|
||||||
|
//to parser body as json fetch must have parameter
|
||||||
|
//headers: {
|
||||||
|
// 'Content-Type': 'application/json',
|
||||||
|
//},
|
||||||
|
const bodyParser = require("body-parser");
|
||||||
|
const path = require("path");
|
||||||
|
const cookieParser = require("cookie-parser");
|
||||||
|
const http = require("http");
|
||||||
|
//Helpers
|
||||||
|
const HelperXhr = require("HelperXhr");
|
||||||
|
//Logs nodejs
|
||||||
|
//--> http: morgan
|
||||||
|
const morgan = require("morgan");
|
||||||
|
const logger = require("logger");
|
||||||
|
//cors
|
||||||
|
const cors = require("cors");
|
||||||
|
//framework express
|
||||||
|
const app = express();
|
||||||
|
//Body parser
|
||||||
|
app.use(bodyParser.urlencoded({ extended: true }));
|
||||||
|
app.use(bodyParser.json());
|
||||||
|
|
||||||
|
//API routes
|
||||||
|
const routes = require("./routes.js");
|
||||||
|
|
||||||
|
//Swagger UI
|
||||||
|
const swaggerUi = require("swagger-ui-express");
|
||||||
|
const YAML = require("yamljs");
|
||||||
|
const swaggerDocumentCore = YAML.load("./openapi.yaml");
|
||||||
|
const swaggeroptions = {
|
||||||
|
explorer: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
//cors options - no options
|
||||||
|
app.use(cors({}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* explainations : https://dev.to/p0oker/why-is-my-browser-sending-an-options-http-request-instead-of-post-5621
|
||||||
|
*/
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
res.header("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS");
|
||||||
|
res.header(
|
||||||
|
"Access-Control-Allow-Headers",
|
||||||
|
"Content-Type, InstantKey, Content-Length, X-Requested-With"
|
||||||
|
);
|
||||||
|
//OPTIONS methods interception
|
||||||
|
if ("OPTIONS" === req.method) {
|
||||||
|
res.sendStatus(200);
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// listening port server
|
||||||
|
app.set("PORT", process.env.PORT || "4000");
|
||||||
|
// listening IP address server
|
||||||
|
app.set("IPADDRESS", process.env.IPADDRESS || "0.0.0.0");
|
||||||
|
// Max size allowed to upload Json files (Default is 2Mo)
|
||||||
|
app.set("JSONMAXSIZE", process.env.JSONMAXSIZE || 2 * 1024 * 1024);
|
||||||
|
|
||||||
|
//logger become global to controller via req.app.get('logger')
|
||||||
|
app.set("logger", logger);
|
||||||
|
|
||||||
|
//Application logs - format see wiki about morgan logs
|
||||||
|
let loghttpformat = "combined";
|
||||||
|
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(express.urlencoded({ extended: false }));
|
||||||
|
app.use(cookieParser());
|
||||||
|
app.use(express.static(path.join(__dirname, "public")));
|
||||||
|
|
||||||
|
//set log http
|
||||||
|
app.use(morgan(loghttpformat));
|
||||||
|
|
||||||
|
// Routes
|
||||||
|
try {
|
||||||
|
// route / load public content
|
||||||
|
app.use("/", express.static(path.join(__dirname, "public")));
|
||||||
|
// route /api-docs load Swagger UI
|
||||||
|
app.use(
|
||||||
|
"/api-docs",
|
||||||
|
swaggerUi.serve,
|
||||||
|
swaggerUi.setup(swaggerDocumentCore, swaggeroptions)
|
||||||
|
);
|
||||||
|
// route /xxx-xxx-xxxx where xxx-xxx-xxx is a diagram Id
|
||||||
|
app.use(
|
||||||
|
"/:diagramId",
|
||||||
|
express.static(path.join(__dirname, "public", "index.html"))
|
||||||
|
);
|
||||||
|
// API Routes
|
||||||
|
app.use(routes);
|
||||||
|
} catch (err) {
|
||||||
|
// logger.error(err.toString(), err);
|
||||||
|
logger.error(err.toString(), err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// resource not found - 404 and forward to error handler
|
||||||
|
app.use(function (req, res, next) {
|
||||||
|
console.log("Resource is missing");
|
||||||
|
req.statusCode = 404;
|
||||||
|
next(new Error(), req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Global error handler
|
||||||
|
app.use(function (err, req, res, next) {
|
||||||
|
let statuscode = req.statusCode; //404 received ???
|
||||||
|
if (!statuscode) statuscode = 500;
|
||||||
|
if (statuscode !== 404) {
|
||||||
|
logger.error(err.toString(), err);
|
||||||
|
message = "Internal Error - see logs";
|
||||||
|
} else {
|
||||||
|
message = "404-Resource Not Found";
|
||||||
|
}
|
||||||
|
if (HelperXhr.isXhrRequest(req)) {
|
||||||
|
res.status(statuscode).json({ error: message });
|
||||||
|
} else {
|
||||||
|
res.status(statuscode).send("<html><h1>" + message + "</h1></html>");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const httpServer = http.createServer(app);
|
||||||
|
|
||||||
|
//catch httpServer Errors type uncaughtException :EADDRNOTAVAIL EADDRINUSE
|
||||||
|
process.on("uncaughtException", function (err) {
|
||||||
|
logger.error(err.toString(), err);
|
||||||
|
});
|
||||||
|
|
||||||
|
//start server
|
||||||
|
httpServer.listen(app.get("PORT"), app.get("IPADDRESS"), function () {
|
||||||
|
logger.info({
|
||||||
|
listeningonipaddress: app.get("IPADDRESS"),
|
||||||
|
listeningonport: app.get("PORT"),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = app;
|
55
server/lib/HelperXhr.js
Normal file
55
server/lib/HelperXhr.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
"use strict";
|
||||||
|
const Libsecurity = require("Libsecurity");
|
||||||
|
|
||||||
|
class HelperXhr {
|
||||||
|
/**
|
||||||
|
* Return req.xhr state to determine if request is xhr type
|
||||||
|
* @param {object} req - req Express var
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
static isXhrRequest(req) {
|
||||||
|
if (req && req.headers && req.headers.accept) {
|
||||||
|
return req.headers.accept.indexOf("json") > -1;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Push received Data on object
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
static setSettings(req) {
|
||||||
|
let obj = {};
|
||||||
|
console.log(req);
|
||||||
|
try {
|
||||||
|
if (req.method.match(/POST|PUT/i)) {
|
||||||
|
obj = Object.keys(req.body).length > 0 ? req.body : {};
|
||||||
|
} else if (req.method.match(/GET/i)) {
|
||||||
|
//no body with GET method !! use query.data
|
||||||
|
obj = req.query && req.query.data ? JSON.parse(req.query.data) : {};
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
"HelperXhr::setSettings-req.method not supported-" + req.method
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// // Parsing req.body API receive only json so string is JSON
|
||||||
|
// if (typeof obj === "string") {
|
||||||
|
// obj = JSON.parse(obj);
|
||||||
|
// }
|
||||||
|
//check size
|
||||||
|
if (typeof obj === "object") {
|
||||||
|
Libsecurity.jsonSizeIsAcceptable(obj, req.app.get("JSONMAXSIZE"));
|
||||||
|
}
|
||||||
|
//url paramèters are also put in object
|
||||||
|
if (req.params && typeof req.params === "object") {
|
||||||
|
for (let param in req.params) {
|
||||||
|
obj[param] = req.params[param];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
} catch (error) {
|
||||||
|
//if called from controller to call express error handler
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module.exports = HelperXhr;
|
36
server/lib/Libsecurity.js
Normal file
36
server/lib/Libsecurity.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
class Libsecurity {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {object} obj
|
||||||
|
* @param {number} maxsize
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
static jsonSizeIsAcceptable(obj, maxsize) {
|
||||||
|
const size = JSON.stringify(obj).length;
|
||||||
|
if (typeof obj === "object" && size > maxsize)
|
||||||
|
throw new Error(
|
||||||
|
"Warning Date received exceed defined max size - Libsecurity",
|
||||||
|
"size received:",
|
||||||
|
obj.length,
|
||||||
|
"acceptable",
|
||||||
|
size
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} str - filename to sanitize
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
static sanitizeFileName(str) {
|
||||||
|
return str
|
||||||
|
.replace(/(.*\/)|(\/.*)/g, "")
|
||||||
|
.replace(/\.\./g, "")
|
||||||
|
.replace(/;/g, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Libsecurity;
|
47
server/lib/logger.js
Normal file
47
server/lib/logger.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
const { createLogger, format, transports, config } = require("winston");
|
||||||
|
|
||||||
|
//exception log filename
|
||||||
|
const exceptionlogfile = __dirname + "/../logs/exceptions.log";
|
||||||
|
const rejectionslogfile = __dirname + "/../logs/rejections.log";
|
||||||
|
const errorslogfile = __dirname + "/../logs/errors.log";
|
||||||
|
const debugslogfile = __dirname + "/../logs/debug.log";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Levels winston
|
||||||
|
* {
|
||||||
|
error: 0,
|
||||||
|
warn: 1,
|
||||||
|
info: 2,
|
||||||
|
http: 3,
|
||||||
|
verbose: 4,
|
||||||
|
debug: 5,
|
||||||
|
silly: 6
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
const logger = createLogger({
|
||||||
|
transports: [
|
||||||
|
new transports.Console({
|
||||||
|
level: "info",
|
||||||
|
format: format.combine(
|
||||||
|
// format.colorize(),
|
||||||
|
format.timestamp(),
|
||||||
|
format.json()
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
new transports.File({
|
||||||
|
level: "error",
|
||||||
|
format: format.combine(format.timestamp(), format.json()),
|
||||||
|
filename: errorslogfile,
|
||||||
|
}),
|
||||||
|
new transports.File({
|
||||||
|
level: "debug",
|
||||||
|
format: format.combine(format.timestamp(), format.json()),
|
||||||
|
filename: debugslogfile,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
exceptionHandlers: [new transports.File({ filename: exceptionlogfile })],
|
||||||
|
rejectionHandlers: [new transports.File({ filename: rejectionslogfile })],
|
||||||
|
//see winston documentation https://www.npmjs.com/package/winston#logging-levels
|
||||||
|
exitOnError: false,
|
||||||
|
});
|
||||||
|
module.exports = logger;
|
167
server/openapi.yaml
Normal file
167
server/openapi.yaml
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
openapi: 3.0.0
|
||||||
|
info:
|
||||||
|
version: 3.0.0
|
||||||
|
description: "Nodejs API server for https://mydraft.cc - UI"
|
||||||
|
title: "Mydraft Nodejs server"
|
||||||
|
contact:
|
||||||
|
website: www.mytinydc.com
|
||||||
|
license:
|
||||||
|
name: "MIT Licence"
|
||||||
|
url: "https://mit-license.org/"
|
||||||
|
|
||||||
|
tags:
|
||||||
|
- name: "manage"
|
||||||
|
description: "Load/Save draft"
|
||||||
|
|
||||||
|
servers:
|
||||||
|
- url: 'http://localhost:4000'
|
||||||
|
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
serverresponsestore:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
writeToken:
|
||||||
|
type: string
|
||||||
|
readToken:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
internalerror:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
error:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
diagrammestructure:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
|
||||||
|
paths:
|
||||||
|
/:
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- "manage"
|
||||||
|
summary: "Store new draft document"
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/diagrammestructure'
|
||||||
|
examples:
|
||||||
|
JsonOK:
|
||||||
|
value: [{"type":"diagram/add","payload":{"diagramId":"f9e48fc9-da3d-aa39-1ab5-0641b0a0f59a","timestamp":1654256184083}},{"type":"items/addVisual","payload":{"diagramId":"f9e48fc9-da3d-aa39-1ab5-0641b0a0f59a","timestamp":1654256186400,"shapeId":"82170ec5-2cc5-c0f2-1414-38e8e477dd01","renderer":"Button","position":{"x":199.5,"y":119.30000305175781}}}]
|
||||||
|
badFormat:
|
||||||
|
value: 'This is not JSON format'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: "successful operation"
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/serverresponsestore'
|
||||||
|
|
||||||
|
"500":
|
||||||
|
description: "Server Internal ERROR"
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/internalerror'
|
||||||
|
security: [] # no authentication
|
||||||
|
|
||||||
|
/{tokenToRead}/{tokenToWrite}:
|
||||||
|
put:
|
||||||
|
tags:
|
||||||
|
- "manage"
|
||||||
|
summary: "Update draft document"
|
||||||
|
parameters:
|
||||||
|
- name: "tokenToRead"
|
||||||
|
in: "path"
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: "string"
|
||||||
|
examples:
|
||||||
|
GoodId:
|
||||||
|
value: 'f9e48fc9-da3d-aa39-1ab5-0641b0a0f59a'
|
||||||
|
WronId:
|
||||||
|
value: 'not the good id'
|
||||||
|
- name: "tokenToWrite"
|
||||||
|
in: "path"
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: "string"
|
||||||
|
examples:
|
||||||
|
GoodId:
|
||||||
|
value: 'f9e48fc9-da3d-aa39-1ab5-0641b0a0f59a'
|
||||||
|
WronId:
|
||||||
|
value: 'not the good id'
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/diagrammestructure'
|
||||||
|
examples:
|
||||||
|
JsonOK:
|
||||||
|
value: [{"type":"diagram/add","payload":{"diagramId":"f9e48fc9-da3d-aa39-1ab5-0641b0a0f59a","timestamp":1654256184083}},{"type":"items/addVisual","payload":{"diagramId":"f9e48fc9-da3d-aa39-1ab5-0641b0a0f59a","timestamp":1654256186400,"shapeId":"82170ec5-2cc5-c0f2-1414-38e8e477dd01","renderer":"Button","position":{"x":199.5,"y":119.30000305175781}}}]
|
||||||
|
badFormat:
|
||||||
|
value: 'This is not JSON format'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: "successful operation"
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/serverresponsestore'
|
||||||
|
"400":
|
||||||
|
description: "probleme with body content JSON structure"
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/internalerror'
|
||||||
|
"500":
|
||||||
|
description: "Server Internal ERROR"
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/internalerror'
|
||||||
|
security: [] # no authentication
|
||||||
|
|
||||||
|
/get/{diagramID}:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- "manage"
|
||||||
|
summary: "return JSON Object"
|
||||||
|
parameters:
|
||||||
|
- name: "diagramID"
|
||||||
|
in: "path"
|
||||||
|
description: "diagramme ID"
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: "string"
|
||||||
|
examples:
|
||||||
|
GoodId:
|
||||||
|
value: 'f9e48fc9-da3d-aa39-1ab5-0641b0a0f59a'
|
||||||
|
WronId:
|
||||||
|
value: 'not the good id'
|
||||||
|
RunDir:
|
||||||
|
value: '../f9e48fc9-da3d-aa39-1ab5-0641b0a0f59a'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: "successful operation"
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/diagrammestructure'
|
||||||
|
"500":
|
||||||
|
description: "Server Internal ERROR"
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/internalerror'
|
||||||
|
security: [] # no authentication
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
2493
server/package-lock.json
generated
Normal file
2493
server/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
29
server/package.json
Normal file
29
server/package.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "mydraftcc-nodejs-server",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "built by https://wwww.mytinydc.com - Mydraftcc - NodesJs server",
|
||||||
|
"author": "Damien HENRY - https://www.mytinydc.com",
|
||||||
|
"license": "MIT License",
|
||||||
|
"main": "app.js",
|
||||||
|
"directories": {
|
||||||
|
"lib": "lib"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"debug-startserver": "IPADDRESS=0.0.0.0 PORT=4000 JSONMAXSIZE=2097152 nodemon -w ./ app.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^0.21.4",
|
||||||
|
"body-parser": "^1.20.0",
|
||||||
|
"cookie-parser": "~1.4.4",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"ejs": "^3.1.7",
|
||||||
|
"express": "~4.17.1",
|
||||||
|
"express-session": "^1.17.1",
|
||||||
|
"http-errors": "~1.6.3",
|
||||||
|
"include-path": "^0.4.7",
|
||||||
|
"morgan": "~1.9.1",
|
||||||
|
"swagger-ui-express": "^4.4.0",
|
||||||
|
"winston": "^3.3.3",
|
||||||
|
"yamljs": "^0.3.0"
|
||||||
|
}
|
||||||
|
}
|
95
server/routes.js
Normal file
95
server/routes.js
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not use logger here use try catch and next(Error) in catch bloc to call the global error handler
|
||||||
|
*/
|
||||||
|
const express = require("express");
|
||||||
|
const router = express.Router();
|
||||||
|
const HelperXhr = require("HelperXhr");
|
||||||
|
const Libsecurity = require("Libsecurity");
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
//Dir storage
|
||||||
|
const datadir = __dirname + "/data";
|
||||||
|
let response = "";
|
||||||
|
let codestatus = 500;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* see swagger model
|
||||||
|
* Store json file
|
||||||
|
*/
|
||||||
|
router.post("/", async function (req, res, next) {
|
||||||
|
try {
|
||||||
|
//check data received
|
||||||
|
let xhr = HelperXhr.setSettings(req);
|
||||||
|
console.log(xhr);
|
||||||
|
if (Array.isArray(xhr) && xhr[0].payload.diagramId) {
|
||||||
|
const id = xhr[0].payload.diagramId;
|
||||||
|
if (id) {
|
||||||
|
if (fs.existsSync(datadir)) {
|
||||||
|
fs.writeFileSync(datadir + "/" + id, JSON.stringify(xhr));
|
||||||
|
codestatus = 200;
|
||||||
|
response = { writeToken: id, readToken: id };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error("Json structure is wrong");
|
||||||
|
}
|
||||||
|
res.status(codestatus).json(response);
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* see swagger model
|
||||||
|
*/
|
||||||
|
router.put("/:tokenWrite/:tokenRead", async function (req, res, next) {
|
||||||
|
try {
|
||||||
|
const tokenWrite = req.params.tokenWrite;
|
||||||
|
const tokenRead = req.params.tokenRead;
|
||||||
|
//check data received
|
||||||
|
let xhr = HelperXhr.setSettings(req);
|
||||||
|
|
||||||
|
if (
|
||||||
|
Array.isArray(xhr) &&
|
||||||
|
xhr[0] &&
|
||||||
|
xhr[0].payload &&
|
||||||
|
xhr[0].payload.diagramId &&
|
||||||
|
tokenWrite &&
|
||||||
|
tokenWrite === tokenRead &&
|
||||||
|
tokenWrite === xhr[0].payload.diagramId
|
||||||
|
) {
|
||||||
|
const id = xhr[0].payload.diagramId;
|
||||||
|
if (id) {
|
||||||
|
fs.writeFileSync(datadir + "/" + id, JSON.stringify(xhr));
|
||||||
|
codestatus = 200;
|
||||||
|
response = { writeToken: id, readToken: id };
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error("Json structure is wrong");
|
||||||
|
}
|
||||||
|
res.status(codestatus).json(response);
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 2 cases
|
||||||
|
* - isXhr request return json
|
||||||
|
* - else public/index.html
|
||||||
|
*/
|
||||||
|
router.get("/get/:diagramId", function (req, res, next) {
|
||||||
|
try {
|
||||||
|
const id = Libsecurity.sanitizeFileName(req.params.diagramId);
|
||||||
|
response = JSON.parse(fs.readFileSync(datadir + "/" + id, "utf8"));
|
||||||
|
codestatus = 200;
|
||||||
|
res.status(codestatus).json(response);
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
382
webpack.config.js-forlocaldeveloppementwithserver
Normal file
382
webpack.config.js-forlocaldeveloppementwithserver
Normal file
@ -0,0 +1,382 @@
|
|||||||
|
/* eslint-disable prefer-spread */
|
||||||
|
/* eslint-disable prefer-rest-params */
|
||||||
|
/* eslint-disable global-require */
|
||||||
|
|
||||||
|
const webpack = require('webpack');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const appRoot = path.resolve(__dirname, '..');
|
||||||
|
|
||||||
|
function root() {
|
||||||
|
const newArgs = Array.prototype.slice.call(arguments, 0);
|
||||||
|
|
||||||
|
return path.join.apply(path, [appRoot].concat(newArgs));
|
||||||
|
}
|
||||||
|
|
||||||
|
const plugins = {
|
||||||
|
// https://github.com/webpack-contrib/mini-css-extract-plugin
|
||||||
|
MiniCssExtractPlugin: require('mini-css-extract-plugin'),
|
||||||
|
// https://github.com/dividab/tsconfig-paths-webpack-plugin
|
||||||
|
TsconfigPathsPlugin: require('tsconfig-paths-webpack-plugin'),
|
||||||
|
// https://github.com/aackerman/circular-dependency-plugin
|
||||||
|
CircularDependencyPlugin: require('circular-dependency-plugin'),
|
||||||
|
// https://github.com/jantimon/html-webpack-plugin
|
||||||
|
HtmlWebpackPlugin: require('html-webpack-plugin'),
|
||||||
|
// https://webpack.js.org/plugins/terser-webpack-plugin/
|
||||||
|
TerserPlugin: require('terser-webpack-plugin'),
|
||||||
|
// https://github.com/NMFR/optimize-css-assets-webpack-plugin
|
||||||
|
CssMinimizerPlugin: require('css-minimizer-webpack-plugin'),
|
||||||
|
// https://webpack.js.org/plugins/eslint-webpack-plugin/
|
||||||
|
ESLintPlugin: require('eslint-webpack-plugin'),
|
||||||
|
// https://github.com/webpack-contrib/stylelint-webpack-plugin
|
||||||
|
StylelintPlugin: require('stylelint-webpack-plugin'),
|
||||||
|
// https://www.npmjs.com/package/webpack-bundle-analyzer
|
||||||
|
BundleAnalyzerPlugin: require('webpack-bundle-analyzer').BundleAnalyzerPlugin,
|
||||||
|
// https://github.com/jantimon/favicons-webpack-plugin
|
||||||
|
FaviconsWebpackPlugin: require('favicons-webpack-plugin'),
|
||||||
|
// https://github.com/GoogleChrome/workbox/tree/master/packages/workbox-webpack-plugin
|
||||||
|
GenerateSW: require('workbox-webpack-plugin').GenerateSW,
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = function configure(env) {
|
||||||
|
const isProduction = env && env.production;
|
||||||
|
const isTests = env && env.target === 'tests';
|
||||||
|
const isTestCoverage = env && env.coverage;
|
||||||
|
const isAnalyzing = isProduction && env.analyze;
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
mode: isProduction ? 'production' : 'development',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Source map for Karma from the help of karma-sourcemap-loader & karma-webpack.
|
||||||
|
*
|
||||||
|
* See: https://webpack.js.org/configuration/devtool/
|
||||||
|
*/
|
||||||
|
devtool: isProduction ? false : 'inline-source-map',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options affecting the resolving of modules.
|
||||||
|
*
|
||||||
|
* See: https://webpack.js.org/configuration/resolve/
|
||||||
|
*/
|
||||||
|
resolve: {
|
||||||
|
/**
|
||||||
|
* An array of extensions that should be used to resolve modules.
|
||||||
|
*
|
||||||
|
* See: https://webpack.js.org/configuration/resolve/#resolve-extensions
|
||||||
|
*/
|
||||||
|
extensions: ['.ts', '.tsx', '.js', '.mjs', '.css', '.scss'],
|
||||||
|
modules: [
|
||||||
|
root('src'),
|
||||||
|
root('src', 'style'),
|
||||||
|
root('node_modules'),
|
||||||
|
],
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
new plugins.TsconfigPathsPlugin({
|
||||||
|
configFile: 'tsconfig.json',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options affecting the normal modules.
|
||||||
|
*
|
||||||
|
* See: https://webpack.js.org/configuration/module/
|
||||||
|
*/
|
||||||
|
module: {
|
||||||
|
/**
|
||||||
|
* An array of Rules which are matched to requests when modules are created.
|
||||||
|
*
|
||||||
|
* See: https://webpack.js.org/configuration/module/#module-rules
|
||||||
|
*/
|
||||||
|
rules: [{
|
||||||
|
test: /\.html$/,
|
||||||
|
use: [{
|
||||||
|
loader: 'raw-loader',
|
||||||
|
}],
|
||||||
|
}, {
|
||||||
|
test: /\.d\.ts?$/,
|
||||||
|
use: [{
|
||||||
|
loader: 'ignore-loader',
|
||||||
|
}],
|
||||||
|
include: [/node_modules/],
|
||||||
|
}, {
|
||||||
|
test: /\.(png|jpe?g|gif|svg|ico)(\?.*$|$)/,
|
||||||
|
use: [{
|
||||||
|
loader: 'file-loader',
|
||||||
|
options: {
|
||||||
|
name: '[name].[contenthash].[ext]',
|
||||||
|
|
||||||
|
// Store the assets in custom path because of fonts need relative urls.
|
||||||
|
outputPath: 'assets',
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
}, {
|
||||||
|
test: /\.css$/,
|
||||||
|
use: [{
|
||||||
|
loader: plugins.MiniCssExtractPlugin.loader,
|
||||||
|
}, {
|
||||||
|
loader: 'css-loader',
|
||||||
|
}, {
|
||||||
|
loader: 'postcss-loader',
|
||||||
|
}],
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
/**
|
||||||
|
* Puts each bundle into a file without the hash.
|
||||||
|
*
|
||||||
|
* See: https://github.com/webpack-contrib/mini-css-extract-plugin
|
||||||
|
*/
|
||||||
|
new plugins.MiniCssExtractPlugin({
|
||||||
|
filename: '[name].css',
|
||||||
|
}),
|
||||||
|
|
||||||
|
new webpack.LoaderOptionsPlugin({
|
||||||
|
options: {
|
||||||
|
htmlLoader: {
|
||||||
|
/**
|
||||||
|
* Define the root for images, so that we can use absolute urls.
|
||||||
|
*
|
||||||
|
* See: https://github.com/webpack/html-loader#Advanced_Options
|
||||||
|
*/
|
||||||
|
root: root('src', 'images'),
|
||||||
|
},
|
||||||
|
context: '/',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
new plugins.FaviconsWebpackPlugin({
|
||||||
|
// Favicon source logo
|
||||||
|
logo: 'src/images/logo-square.png',
|
||||||
|
// Favicon app title
|
||||||
|
title: 'MyDraft',
|
||||||
|
favicons: {
|
||||||
|
appName: 'mydraft.cc',
|
||||||
|
appDescription: 'Open Source Wireframe Editor',
|
||||||
|
developerName: 'Sebastian Stehle',
|
||||||
|
developerUrl: 'https://sstehle.com',
|
||||||
|
start_url: '/',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
new plugins.StylelintPlugin({
|
||||||
|
files: '**/*.scss',
|
||||||
|
}),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect circular dependencies in app.
|
||||||
|
*
|
||||||
|
* See: https://github.com/aackerman/circular-dependency-plugin
|
||||||
|
*/
|
||||||
|
new plugins.CircularDependencyPlugin({
|
||||||
|
exclude: /([\\/]node_modules[\\/])/,
|
||||||
|
// Add errors to webpack instead of warnings
|
||||||
|
failOnError: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
|
||||||
|
devServer: {
|
||||||
|
headers: {
|
||||||
|
"Access-Control-Allow-Origin": "*",
|
||||||
|
},
|
||||||
|
historyApiFallback: true,
|
||||||
|
proxy: {
|
||||||
|
context: () => true,
|
||||||
|
target: "http://localhost:4000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!isTests) {
|
||||||
|
/**
|
||||||
|
* The entry point for the bundle. Our React app.
|
||||||
|
*
|
||||||
|
* See: https://webpack.js.org/configuration/entry-context/
|
||||||
|
*/
|
||||||
|
config.entry = {
|
||||||
|
src: './src/index.tsx',
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isProduction) {
|
||||||
|
config.output = {
|
||||||
|
/**
|
||||||
|
* The output directory as absolute path (required).
|
||||||
|
*
|
||||||
|
* See: https://webpack.js.org/configuration/output/#output-path
|
||||||
|
*/
|
||||||
|
path: root('/build/'),
|
||||||
|
|
||||||
|
publicPath: './',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the name of each output file on disk.
|
||||||
|
*
|
||||||
|
* Do NOT append hash to service worker in development mode, so we can load them directly.
|
||||||
|
*
|
||||||
|
* See: https://webpack.js.org/configuration/output/#output-filename
|
||||||
|
*/
|
||||||
|
filename: (pathData) => {
|
||||||
|
return pathData.chunk.name === 'src' ? '[name].[contenthash:8].js' : '[name].js';
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The filename of non-entry chunks as relative path inside the output.path directory.
|
||||||
|
*
|
||||||
|
* See: https://webpack.js.org/configuration/output/#output-chunkfilename
|
||||||
|
*/
|
||||||
|
chunkFilename: '[id].[contenthash].chunk.js',
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
config.output = {
|
||||||
|
filename: '[name].[contenthash].js',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the public path, because we are running the website from another port (5000).
|
||||||
|
*/
|
||||||
|
publicPath: 'https://localhost:3002/',
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fix a bug with webpack dev server.
|
||||||
|
*
|
||||||
|
* See: https://github.com/webpack-contrib/worker-loader/issues/174
|
||||||
|
*/
|
||||||
|
globalObject: 'this',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
config.plugins.push(
|
||||||
|
new plugins.HtmlWebpackPlugin({
|
||||||
|
hash: true,
|
||||||
|
chunks: ['src'],
|
||||||
|
chunksSortMode: 'manual',
|
||||||
|
template: 'src/index.html',
|
||||||
|
}),
|
||||||
|
new plugins.HtmlWebpackPlugin({
|
||||||
|
hash: true,
|
||||||
|
chunks: ['src'],
|
||||||
|
chunksSortMode: 'manual',
|
||||||
|
template: 'src/index.html',
|
||||||
|
filename: '404.html',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
config.plugins.push(
|
||||||
|
new plugins.ESLintPlugin({
|
||||||
|
files: [
|
||||||
|
'./src/**/*.ts',
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isProduction) {
|
||||||
|
config.optimization = {
|
||||||
|
minimizer: [
|
||||||
|
new plugins.TerserPlugin({
|
||||||
|
terserOptions: {
|
||||||
|
compress: true,
|
||||||
|
ecma: 5,
|
||||||
|
mangle: true,
|
||||||
|
output: {
|
||||||
|
comments: false,
|
||||||
|
},
|
||||||
|
safari10: true,
|
||||||
|
},
|
||||||
|
extractComments: true,
|
||||||
|
}),
|
||||||
|
|
||||||
|
new plugins.CssMinimizerPlugin({}),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
config.performance = {
|
||||||
|
hints: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTestCoverage) {
|
||||||
|
// Do not instrument tests.
|
||||||
|
config.module.rules.push({
|
||||||
|
test: /\.ts[x]?$/,
|
||||||
|
use: [{
|
||||||
|
loader: 'ts-loader',
|
||||||
|
}],
|
||||||
|
include: [/\.(e2e|spec)\.ts$/],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Use instrument loader for all normal files.
|
||||||
|
config.module.rules.push({
|
||||||
|
test: /\.ts[x]?$/,
|
||||||
|
use: [{
|
||||||
|
loader: '@jsdevtools/coverage-istanbul-loader?esModules=true',
|
||||||
|
}, {
|
||||||
|
loader: 'ts-loader',
|
||||||
|
}],
|
||||||
|
exclude: [/\.(e2e|spec)\.ts$/],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
config.module.rules.push({
|
||||||
|
test: /\.ts[x]?$/,
|
||||||
|
use: [{
|
||||||
|
loader: 'ts-loader',
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isProduction) {
|
||||||
|
config.plugins.push(new plugins.GenerateSW({
|
||||||
|
swDest: 'service-worker2.js',
|
||||||
|
|
||||||
|
// Do not wait for activation
|
||||||
|
skipWaiting: true,
|
||||||
|
|
||||||
|
// Cache until 5MB
|
||||||
|
maximumFileSizeToCacheInBytes: 5000000000,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isProduction) {
|
||||||
|
config.module.rules.push({
|
||||||
|
test: /\.scss$/,
|
||||||
|
/*
|
||||||
|
* Extract the content from a bundle to a file.
|
||||||
|
*
|
||||||
|
* See: https://github.com/webpack-contrib/extract-text-webpack-plugin
|
||||||
|
*/
|
||||||
|
use: [
|
||||||
|
plugins.MiniCssExtractPlugin.loader,
|
||||||
|
{
|
||||||
|
loader: 'css-loader',
|
||||||
|
}, {
|
||||||
|
loader: 'postcss-loader',
|
||||||
|
}, {
|
||||||
|
loader: 'sass-loader',
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
config.module.rules.push({
|
||||||
|
test: /\.scss$/,
|
||||||
|
use: [{
|
||||||
|
loader: 'style-loader',
|
||||||
|
}, {
|
||||||
|
loader: 'css-loader',
|
||||||
|
}, {
|
||||||
|
loader: 'postcss-loader',
|
||||||
|
}, {
|
||||||
|
loader: 'sass-loader',
|
||||||
|
options: {
|
||||||
|
sourceMap: true,
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAnalyzing) {
|
||||||
|
config.plugins.push(new plugins.BundleAnalyzerPlugin());
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
};
|
Reference in New Issue
Block a user