diff --git a/hosting/docker-compose.yaml b/hosting/docker-compose.yaml index ada377f1f6..bab7f61df1 100644 --- a/hosting/docker-compose.yaml +++ b/hosting/docker-compose.yaml @@ -5,6 +5,7 @@ version: "3" services: app-service: restart: always + #build: ./build/server image: budibase/budibase-apps ports: - "${APP_PORT}:4002" @@ -22,6 +23,7 @@ services: worker-service: restart: always + #build: ./build/worker image: budibase/budibase-worker ports: - "${WORKER_PORT}:4003" diff --git a/hosting/envoy.yaml b/hosting/envoy.yaml index 3c816cb1ca..11f5c81b99 100644 --- a/hosting/envoy.yaml +++ b/hosting/envoy.yaml @@ -20,6 +20,11 @@ static_resources: route: cluster: app-service prefix_rewrite: "/" + + # special case for presenting our static self hosting page + - match: { path: "/" } + route: + cluster: app-service # special case for when API requests are made, can just forward, not to minio - match: { prefix: "/api/" } diff --git a/packages/server/src/api/controllers/auth.js b/packages/server/src/api/controllers/auth.js index f9c3ef945f..4020e0c492 100644 --- a/packages/server/src/api/controllers/auth.js +++ b/packages/server/src/api/controllers/auth.js @@ -45,7 +45,7 @@ exports.authenticate = async ctx => { expiresIn: "1 day", }) - setCookie(ctx, appId, token) + setCookie(ctx, token, appId) delete dbUser.password ctx.body = { diff --git a/packages/server/src/api/controllers/static/index.js b/packages/server/src/api/controllers/static/index.js index 4f3d8d1611..9686a4c390 100644 --- a/packages/server/src/api/controllers/static/index.js +++ b/packages/server/src/api/controllers/static/index.js @@ -49,6 +49,18 @@ exports.serveBuilder = async function(ctx) { await send(ctx, ctx.file, { root: ctx.devPath || builderPath }) } +exports.serveSelfHostPage = async function(ctx) { + const logo = fs.readFileSync(resolve(__dirname, "selfhost/logo.svg"), "utf8") + const hostingHbs = fs.readFileSync( + resolve(__dirname, "selfhost/index.hbs"), + "utf8" + ) + ctx.body = await processString(hostingHbs, { + logo, + key: env.HOSTING_KEY, + }) +} + exports.uploadFile = async function(ctx) { let files files = @@ -177,7 +189,6 @@ exports.serveApp = async function(ctx) { }) const appHbs = fs.readFileSync(`${__dirname}/templates/app.hbs`, "utf8") - console.log(appHbs) ctx.body = await processString(appHbs, { head, body: html, diff --git a/packages/server/src/api/controllers/static/selfhost/index.hbs b/packages/server/src/api/controllers/static/selfhost/index.hbs new file mode 100644 index 0000000000..9e47389efa --- /dev/null +++ b/packages/server/src/api/controllers/static/selfhost/index.hbs @@ -0,0 +1,161 @@ + + + + + Budibase self hosting️ + + + +
+ +

Get started with Budibase Self Hosting

+

Use the Hosting Key and the address Hosting URL in your Builder

+
+
+

📚Documentation

+

+ Find out more about your self hosted platform. +

+ + Documentation + +
+
+

💻Next steps

+

+ Find out how to make use of your self hosted Budibase platform. +

+ + Next steps + +
+
+
+

Your Hosting Key: {{key}}

+

Your Hosting URL:

+
+
+ + + \ No newline at end of file diff --git a/packages/server/src/api/controllers/static/selfhost/logo.svg b/packages/server/src/api/controllers/static/selfhost/logo.svg new file mode 100644 index 0000000000..37bfb4ff83 --- /dev/null +++ b/packages/server/src/api/controllers/static/selfhost/logo.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + diff --git a/packages/server/src/api/index.js b/packages/server/src/api/index.js index c76eb82643..48afb1eb90 100644 --- a/packages/server/src/api/index.js +++ b/packages/server/src/api/index.js @@ -63,6 +63,8 @@ for (let route of mainRoutes) { router.use(staticRoutes.routes()) router.use(staticRoutes.allowedMethods()) -router.redirect("/", "/_builder") +if (!env.SELF_HOSTED && !env.CLOUD) { + router.redirect("/", "/_builder") +} module.exports = router diff --git a/packages/server/src/api/routes/static.js b/packages/server/src/api/routes/static.js index a519a63781..2b0c9b36fc 100644 --- a/packages/server/src/api/routes/static.js +++ b/packages/server/src/api/routes/static.js @@ -23,6 +23,10 @@ if (env.NODE_ENV !== "production") { router.get("/_builder/:file*", controller.serveBuilder) } +if (env.SELF_HOSTED) { + router.get("/", controller.serveSelfHostPage) +} + router .post( "/api/attachments/process", diff --git a/packages/server/src/middleware/authenticated.js b/packages/server/src/middleware/authenticated.js index 27b3f52725..e2061c8eb5 100644 --- a/packages/server/src/middleware/authenticated.js +++ b/packages/server/src/middleware/authenticated.js @@ -2,7 +2,13 @@ const jwt = require("jsonwebtoken") const STATUS_CODES = require("../utilities/statusCodes") const { getRole, BUILTIN_ROLES } = require("../utilities/security/roles") const { AuthTypes } = require("../constants") -const { getAppId, getCookieName, setCookie, isClient } = require("../utilities") +const { + getAppId, + getCookieName, + clearCookie, + setCookie, + isClient, +} = require("../utilities") module.exports = async (ctx, next) => { if (ctx.path === "/_builder") { @@ -15,16 +21,17 @@ module.exports = async (ctx, next) => { let appId = getAppId(ctx) const cookieAppId = ctx.cookies.get(getCookieName("currentapp")) if (appId && cookieAppId !== appId) { - setCookie(ctx, "currentapp", appId) + setCookie(ctx, appId, "currentapp") } else if (cookieAppId) { appId = cookieAppId } - - let token = ctx.cookies.get(getCookieName(appId)) - let authType = AuthTypes.APP - if (!token && !isClient(ctx)) { - authType = AuthTypes.BUILDER + let token, authType + if (!isClient(ctx)) { token = ctx.cookies.get(getCookieName()) + authType = AuthTypes.BUILDER + } else if (appId) { + token = ctx.cookies.get(getCookieName(appId)) + authType = AuthTypes.APP } if (!token) { @@ -49,9 +56,13 @@ module.exports = async (ctx, next) => { role: await getRole(appId, jwtPayload.roleId), } } catch (err) { - // TODO - this can happen if the JWT secret is changed and can never login - // TODO: wipe cookies if they exist - ctx.throw(err.status || STATUS_CODES.FORBIDDEN, err.text) + if (authType === AuthTypes.BUILDER) { + clearCookie(ctx) + ctx.status = 200 + return + } else { + ctx.throw(err.status || STATUS_CODES.FORBIDDEN, err.text) + } } await next() diff --git a/packages/server/src/utilities/builder/setBuilderToken.js b/packages/server/src/utilities/builder/setBuilderToken.js index 93863cee63..42730fd2ea 100644 --- a/packages/server/src/utilities/builder/setBuilderToken.js +++ b/packages/server/src/utilities/builder/setBuilderToken.js @@ -3,7 +3,7 @@ const env = require("../../environment") const CouchDB = require("../../db") const jwt = require("jsonwebtoken") const { DocumentTypes, SEPARATOR } = require("../../db/utils") -const { setCookie } = require("../index") +const { setCookie, clearCookie } = require("../index") const APP_PREFIX = DocumentTypes.APP + SEPARATOR module.exports = async (ctx, appId, version) => { @@ -20,13 +20,13 @@ module.exports = async (ctx, appId, version) => { }) // set the builder token - setCookie(ctx, "builder", token) - setCookie(ctx, "currentapp", appId) + setCookie(ctx, token, "builder") + setCookie(ctx, appId, "currentapp") // need to clear all app tokens or else unable to use the app in the builder let allDbNames = await CouchDB.allDbs() allDbNames.map(dbName => { if (dbName.startsWith(APP_PREFIX)) { - setCookie(ctx, dbName, "") + clearCookie(ctx, dbName) } }) } diff --git a/packages/server/src/utilities/index.js b/packages/server/src/utilities/index.js index fd51bcdaf0..9da2937a4d 100644 --- a/packages/server/src/utilities/index.js +++ b/packages/server/src/utilities/index.js @@ -111,16 +111,28 @@ exports.getCookieName = (name = "builder") => { * @param {string} name The name of the cookie to set. * @param {string|object} value The value of cookie which will be set. */ -exports.setCookie = (ctx, name, value) => { +exports.setCookie = (ctx, value, name = "builder") => { const expires = new Date() expires.setDate(expires.getDate() + 1) - ctx.cookies.set(exports.getCookieName(name), value, { - expires, - path: "/", - httpOnly: false, - overwrite: true, - }) + const cookieName = exports.getCookieName(name) + if (!value) { + ctx.cookies.set(cookieName) + } else { + ctx.cookies.set(cookieName, value, { + expires, + path: "/", + httpOnly: false, + overwrite: true, + }) + } +} + +/** + * Utility function, simply calls setCookie with an empty string for value + */ +exports.clearCookie = (ctx, name) => { + exports.setCookie(ctx, "", name) } exports.isClient = ctx => { diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 55450d3e0c..36efad1747 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -947,6 +947,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.8.tgz#fe2012f2355e4ce08bca44aeb3abbb21cf88d33f" integrity sha512-KPcKqKm5UKDkaYPTuXSx8wEP7vE9GnuaXIZKijwRYcePpZFDVuy2a57LarFKiORbHOuTOOwYzxVxcUzsh2P2Pw== +"@types/node@>=13.13.4": + version "14.14.22" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.22.tgz#0d29f382472c4ccf3bd96ff0ce47daf5b7b84b18" + integrity sha512-g+f/qj/cNcqKkc3tFqlXOYjrmZA+jNBiDzbP3kH+B+otKFqAdPgVTGP1IeKRdMml/aE69as5S4FqtxAbl+LaMw== + "@types/node@>=8.0.0 <15": version "14.14.20" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.20.tgz#f7974863edd21d1f8a494a73e8e2b3658615c340" @@ -1278,6 +1283,17 @@ app-builder-lib@22.9.1: semver "^7.3.2" temp-file "^3.3.7" +arangojs@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/arangojs/-/arangojs-7.2.0.tgz#e576926b4b3469c5a130cceba45fada8b5f015d1" + integrity sha512-9mQRCcttaG0lckapNF9TA71ZU7H2ATXK2a1w+0fj+Y4TlTP1bNDMIz3ZN+EnaSgEtwVu0rb6N6Ac97Yd56GmkQ== + dependencies: + "@types/node" ">=13.13.4" + es6-error "^4.0.1" + multi-part "^3.0.0" + x3-linkedlist "1.2.0" + xhr "^2.4.1" + archive-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/archive-type/-/archive-type-4.0.0.tgz#f92e72233056dfc6969472749c267bdb046b1d70" @@ -2759,7 +2775,7 @@ es3ify@^0.2.2: jstransform "~11.0.0" through "~2.3.4" -es6-error@^4.1.1: +es6-error@^4.0.1, es6-error@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== @@ -3173,6 +3189,11 @@ file-type@^11.1.0: resolved "https://registry.yarnpkg.com/file-type/-/file-type-11.1.0.tgz#93780f3fed98b599755d846b99a1617a2ad063b8" integrity sha512-rM0UO7Qm9K7TWTtA6AShI/t7H5BPjDeGVDaNyg9BjHAj3PysKy7+8C8D137R88jnR3rFJZQB/tFgydl5sN5m7g== +file-type@^12.1.0: + version "12.4.2" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-12.4.2.tgz#a344ea5664a1d01447ee7fb1b635f72feb6169d9" + integrity sha512-UssQP5ZgIOKelfsaB5CuGAL+Y+q7EmONuiwF3N5HAH0t27rvrttgi6Ra9k/+DVaY9UF6+ybxu5pOXLUdA8N7Vg== + file-type@^3.8.0: version "3.9.0" resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9" @@ -3524,6 +3545,14 @@ global@~4.3.0: min-document "^2.19.0" process "~0.5.1" +global@~4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" + integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== + dependencies: + min-document "^2.19.0" + process "^0.11.10" + globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -5441,11 +5470,19 @@ mime-db@1.44.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== -"mime-db@>= 1.43.0 < 2", mime-db@^1.28.0: +mime-db@1.45.0, "mime-db@>= 1.43.0 < 2", mime-db@^1.28.0: version "1.45.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea" integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w== +mime-kind@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mime-kind/-/mime-kind-3.0.0.tgz#23bb3aba03ed6a1ea8c4f6093a9c7ab7121a9cb2" + integrity sha512-sx9lClVP7GXY2mO3aVDWTQLhfvAdDvNhGi3o3g7+ae3aKaoybeGbEIlnreoRKjrbDpvlPltlkIryxOtatojeXQ== + dependencies: + file-type "^12.1.0" + mime-types "^2.1.24" + mime-types@^2.1.12, mime-types@^2.1.18, mime-types@~2.1.19, mime-types@~2.1.24: version "2.1.27" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" @@ -5453,6 +5490,13 @@ mime-types@^2.1.12, mime-types@^2.1.18, mime-types@~2.1.19, mime-types@~2.1.24: dependencies: mime-db "1.44.0" +mime-types@^2.1.24: + version "2.1.28" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.28.tgz#1160c4757eab2c5363888e005273ecf79d2a0ecd" + integrity sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ== + dependencies: + mime-db "1.45.0" + mime@^1.3.4, mime@^1.4.1: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" @@ -5549,6 +5593,19 @@ mssql@^6.2.3: tarn "^1.1.5" tedious "^6.6.2" +multi-part-lite@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/multi-part-lite/-/multi-part-lite-1.0.0.tgz#7b86baf8ff83ef20ca13f1269a0f35aec42b9000" + integrity sha512-KxIRbBZZ45hoKX1ROD/19wJr0ql1bef1rE8Y1PCwD3PuNXV42pp7Wo8lEHYuAajoT4vfAFcd3rPjlkyEEyt1nw== + +multi-part@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/multi-part/-/multi-part-3.0.0.tgz#2bde386e8c1dcc9f15a2277267a7f5ed13aa0cc0" + integrity sha512-pDbdYQ6DLDxAsD83w9R7r7rlW56cETL7hIB5bCWX7FJYw0K+kL5JwHr0I8tRk9lGeFcAzf+2OEzXWlG/4wCnFw== + dependencies: + mime-kind "^3.0.0" + multi-part-lite "^1.0.0" + mute-stream@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" @@ -6452,6 +6509,11 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= + process@~0.5.1: version "0.5.2" resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf" @@ -8264,6 +8326,11 @@ ws@^5.2.0: dependencies: async-limiter "~1.0.0" +x3-linkedlist@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/x3-linkedlist/-/x3-linkedlist-1.2.0.tgz#c70467559b7c748595f0f79222af1d709402699e" + integrity sha512-mH/YwxpYSKNa8bDNF1yOuZCMuV+K80LtDN8vcLDUAwNazCxptDNsYt+zA/EJeYiGbdtKposhKLZjErGVOR8mag== + xdg-basedir@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" @@ -8279,6 +8346,16 @@ xhr@^2.0.1: parse-headers "^2.0.0" xtend "^4.0.0" +xhr@^2.4.1: + version "2.6.0" + resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.6.0.tgz#b69d4395e792b4173d6b7df077f0fc5e4e2b249d" + integrity sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA== + dependencies: + global "~4.4.0" + is-function "^1.0.1" + parse-headers "^2.0.0" + xtend "^4.0.0" + xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"