diff --git a/app/app.ts b/app/app.ts index 7414b02f..75e58ca8 100644 --- a/app/app.ts +++ b/app/app.ts @@ -60,6 +60,8 @@ class App { // Create a new express application instance and add middleware this.app = express(); + this.app.set('views', path.join(__dirname, 'views')); + this.app.set('view engine', 'ejs'); this.app.use(helmet.contentSecurityPolicy({ directives: { diff --git a/app/public/images/site-preview-large-square.png b/app/public/images/site-preview-large-square.png new file mode 100644 index 00000000..8db8e6a6 Binary files /dev/null and b/app/public/images/site-preview-large-square.png differ diff --git a/app/public/images/site-preview-large-wide.png b/app/public/images/site-preview-large-wide.png new file mode 100644 index 00000000..89cc143f Binary files /dev/null and b/app/public/images/site-preview-large-wide.png differ diff --git a/app/routes.ts b/app/routes.ts index da18fa1b..c04bc11b 100644 --- a/app/routes.ts +++ b/app/routes.ts @@ -1,15 +1,16 @@ -import { Request, Response, Application } from "express"; -import express from 'express'; import path from 'path'; +import { Application } from "express"; +import express from 'express'; import expressJwt from 'express-jwt' +import { StaticController } from './static-controller'; import { LoginController } from './api/login-controller'; import { RegisterController } from "./api/register-controller"; import { DuplicateCheckController } from "./api/duplicate-check-controller"; import { BlueprintController } from "./api/blueprint-controller"; var Recaptcha = require('express-recaptcha').RecaptchaV3; - export class Routes { + public staticController = new StaticController(); public loginController = new LoginController(); public registerController = new RegisterController(); public duplicateCheckController = new DuplicateCheckController(); @@ -18,10 +19,10 @@ export class Routes { public routes(app: Application): void { // Initialize authentication middleware //let auth = expressJwt({secret: process.env.JWT_SECRET as string, userProperty: 'tokenPayload' }); - let auth = expressJwt({secret: process.env.JWT_SECRET as string }); + let auth = expressJwt({ secret: process.env.JWT_SECRET as string }); let recaptcha = new Recaptcha(process.env.CAPTCHA_SITE as string, process.env.CAPTCHA_SECRET as string); - + if (process.env.ENV_NAME == 'development') { console.log('Initializing routes without recaptcha verification'); app.route("/api/login").post(this.loginController.login); @@ -32,7 +33,7 @@ export class Routes { app.route("/api/login").post(recaptcha.middleware.verify, this.loginController.login); app.route("/api/register").post(recaptcha.middleware.verify, this.registerController.register); } - + // Anonymous access app.route("/api/checkusername").get(this.duplicateCheckController.checkUsername); app.route("/api/getblueprint/:id").get(this.uploadBlueprintController.getBlueprint); @@ -46,9 +47,8 @@ export class Routes { app.route("/api/likeblueprint").post(auth, this.uploadBlueprintController.likeBlueprint); app.route("/api/deleteblueprint").post(auth, this.uploadBlueprintController.deleteBlueprint); + app.get('/', this.staticController.serveHtml); app.use(express.static(path.join(__dirname, "public"))); - app.get('*', function(req, res){ - res.sendFile(path.join(__dirname, 'public', 'index.html')); - }); + app.get('*', this.staticController.serveHtml); } -} \ No newline at end of file +} diff --git a/app/static-controller.ts b/app/static-controller.ts new file mode 100644 index 00000000..188af409 --- /dev/null +++ b/app/static-controller.ts @@ -0,0 +1,13 @@ +import path from 'path'; +import { Request, Response } from "express"; +import isbot from 'isbot' + +export class StaticController { + public serveHtml(req: Request, res: Response) { + if (isbot(req.get('user-agent'))) { + res.render('index-robots'); + } else { + res.sendFile(path.join(__dirname, 'public', 'index.html')); + } + } +} diff --git a/app/views/index-robots.ejs b/app/views/index-robots.ejs new file mode 100644 index 00000000..52a6f39f --- /dev/null +++ b/app/views/index-robots.ejs @@ -0,0 +1,24 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/package-lock.json b/package-lock.json index 37156a9e..bb55954c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1044,6 +1044,14 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, "any-base": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz", @@ -1238,6 +1246,15 @@ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, "chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", @@ -1248,6 +1265,19 @@ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -1493,6 +1523,14 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "ejs": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", + "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "requires": { + "jake": "^10.8.5" + } + }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -1646,10 +1684,36 @@ "resolved": "https://registry.npmjs.org/file-type/-/file-type-9.0.0.tgz", "integrity": "sha512-Qe/5NJrgIOlwijpq3B7BEpzPFcgzggOTagZmkXQY4LA6bsXKTUstK7Wp12lEJ/mLKTpvIZxmIuRcLYWT6ov9lw==" }, + "filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "requires": { + "minimatch": "^5.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, "filewatcher": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/filewatcher/-/filewatcher-3.0.1.tgz", - "integrity": "sha1-9KGVc1Xdr0Q8zXiolfPVXiPIoDQ=", + "integrity": "sha512-Fro8py2B8EJupSP37Kyd4kjKZLr+5ksFq7Vbw8A392Z15Unq8016SPUDvO/AsDj5V6bbPk98PTAinpc5YhPbJw==", "requires": { "debounce": "^1.0.0" } @@ -1798,6 +1862,11 @@ "har-schema": "^2.0.0" } }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -1962,10 +2031,15 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, + "isbot": { + "version": "3.6.8", + "resolved": "https://registry.npmjs.org/isbot/-/isbot-3.6.8.tgz", + "integrity": "sha512-V8XUXN0/UYxfgrui4o38pmOve2eO/1KjluxR1G8Qnu5gqlUiNrvtX06t1W5n8vFtrtKfHj96iFYuPL39jXUe8g==" + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "ismobilejs": { "version": "1.1.1", @@ -1977,6 +2051,24 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, + "jake": { + "version": "10.8.5", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", + "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "requires": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + }, + "dependencies": { + "async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + } + } + }, "jimp": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.14.0.tgz", @@ -2692,7 +2784,7 @@ "path-type": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "integrity": "sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==", "requires": { "graceful-fs": "^4.1.2", "pify": "^2.0.0", @@ -3298,6 +3390,14 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + }, "symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", diff --git a/package.json b/package.json index af93f93f..6a333ae8 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,17 @@ "scripts": { "tsc": "tsc -b", "dev": "ts-node-dev --respawn --transpileOnly ./app/server.ts", - "prod": "tsc -b && node ./build/server.js", + "prebuild": "rm -rf build/", + "build": "npm run build:backend && npm run build:lib && npm run build:frontend", + "build:backend": "tsc -b", + "build:lib": "(cd lib && tsc -b)", + "build:frontend": "(cd frontend && npm run build)", + "postbuild": "npm run copy:libjs && npm run copy:views && npm run copy:public && npm run copy:frontend", + "copy:libjs": "rsync -amv --include='*.js' --include='*/' --exclude='*' lib/ build/lib", + "copy:views": "rsync -zarv --prune-empty-dirs --include '*/' --include='*.ejs' --exclude='*' 'app' 'build'", + "copy:frontend": "rsync -amv frontend/dist/blueprintnotincluded/ build/app/public", + "copy:public": "rsync -amv app/public/ build/app/public", + "serve:prod": "node ./build/app/server.js", "updateBasedOn": "tsc && node ./build/api/batch/update-based-on.js", "updatePositionCorrection": "tsc && node ./build/api/batch/update-position-correction.js", "updateThumbnail": "ts-node-dev --expose_gc --max_old_space_size=4096 --transpileOnly ./app/api/batch/update-thumbnail.ts", @@ -27,10 +37,12 @@ "crypto-js": "^3.1.9-1", "csharp-binary-stream": "^1.0.2", "dotenv": "^8.2.0", + "ejs": "^3.1.9", "express": "^4.17.1", "express-jwt": "^5.3.1", "express-recaptcha": "^5.0.1", "helmet": "^4.1.0", + "isbot": "^3.6.8", "jimp": "^0.14.0", "jsdom": "^16.3.0", "jsdom-global": "^3.0.2",