mirror of https://github.com/Budibase/budibase.git
19 changed files with 1538 additions and 61 deletions
@ -0,0 +1 @@ |
|||
../packages/deployment/ |
|||
@ -0,0 +1,2 @@ |
|||
node_modules/ |
|||
.env |
|||
@ -0,0 +1,15 @@ |
|||
FROM node:12-alpine |
|||
|
|||
WORKDIR /app |
|||
|
|||
# copy files and install dependencies |
|||
COPY . ./ |
|||
RUN yarn |
|||
|
|||
EXPOSE 4001 |
|||
|
|||
# have to add node environment production after install |
|||
# due to this causing yarn to stop installing dev dependencies |
|||
# which are actually needed to get this environment up and running |
|||
ENV NODE_ENV=production |
|||
CMD ["yarn", "run:docker"] |
|||
@ -0,0 +1,34 @@ |
|||
{ |
|||
"name": "@budibase/deployment", |
|||
"email": "hi@budibase.com", |
|||
"version": "0.3.8", |
|||
"description": "Budibase Deployment Server", |
|||
"main": "src/index.js", |
|||
"repository": { |
|||
"type": "git", |
|||
"url": "https://github.com/Budibase/budibase.git" |
|||
}, |
|||
"keywords": [ |
|||
"budibase" |
|||
], |
|||
"scripts": { |
|||
"run:docker": "node src/index.js" |
|||
}, |
|||
"author": "Budibase", |
|||
"license": "AGPL-3.0-or-later", |
|||
"dependencies": { |
|||
"@koa/router": "^8.0.0", |
|||
"aws-sdk": "^2.811.0", |
|||
"got": "^11.8.1", |
|||
"joi": "^17.2.1", |
|||
"koa": "^2.7.0", |
|||
"koa-body": "^4.2.0", |
|||
"koa-compress": "^4.0.1", |
|||
"koa-pino-logger": "^3.0.0", |
|||
"koa-send": "^5.0.0", |
|||
"koa-session": "^5.12.0", |
|||
"koa-static": "^5.0.0", |
|||
"pino-pretty": "^4.0.0", |
|||
"server-destroy": "^1.0.1" |
|||
} |
|||
} |
|||
@ -0,0 +1,86 @@ |
|||
const env = require("../../environment") |
|||
const got = require("got") |
|||
const AWS = require("aws-sdk") |
|||
|
|||
const APP_BUCKET = "app-assets" |
|||
// this doesn't matter in self host
|
|||
const REGION = "eu-west-1" |
|||
|
|||
async function getCouchSession() { |
|||
// fetch session token for the api user
|
|||
const session = await got.post(`${env.RAW_COUCH_DB_URL}/_session`, { |
|||
responseType: "json", |
|||
json: { |
|||
username: env.COUCH_DB_USERNAME, |
|||
password: env.COUCH_DB_PASSWORD, |
|||
} |
|||
}) |
|||
|
|||
const cookie = session.headers["set-cookie"][0] |
|||
// Get the session cookie value only
|
|||
return cookie.split(";")[0] |
|||
} |
|||
|
|||
async function getMinioSession() { |
|||
AWS.config.update({ |
|||
accessKeyId: env.MINIO_ACCESS_KEY, |
|||
secretAccessKey: env.MINIO_SECRET_KEY, |
|||
}) |
|||
|
|||
// make sure the bucket exists
|
|||
const objClient = new AWS.S3({ |
|||
endpoint: env.RAW_MINIO_URL, |
|||
region: REGION, |
|||
s3ForcePathStyle: true, // needed with minio?
|
|||
params: { |
|||
Bucket: APP_BUCKET, |
|||
}, |
|||
}) |
|||
// make sure the bucket exists
|
|||
try { |
|||
await objClient.headBucket({ Bucket: APP_BUCKET }).promise() |
|||
} catch (err) { |
|||
// bucket doesn't exist create it
|
|||
if (err.statusCode === 404) { |
|||
await objClient.createBucket({ Bucket: APP_BUCKET }).promise() |
|||
} else { |
|||
throw err |
|||
} |
|||
} |
|||
// TODO: this doesn't seem to work get an error
|
|||
// TODO: Generating temporary credentials not allowed for this request.
|
|||
// TODO: this should work based on minio documentation
|
|||
// const sts = new AWS.STS({
|
|||
// endpoint: env.RAW_MINIO_URL,
|
|||
// region: REGION,
|
|||
// s3ForcePathStyle: true,
|
|||
// })
|
|||
// // NOTE: In the following commands RoleArn and RoleSessionName are not meaningful for MinIO
|
|||
// const params = {
|
|||
// DurationSeconds: 3600,
|
|||
// ExternalId: "123ABC",
|
|||
// Policy: '{"Version":"2012-10-17","Statement":[{"Sid":"Stmt1","Effect":"Allow","Action":"s3:*","Resource":"arn:aws:s3:::*"}]}',
|
|||
// RoleArn: 'arn:xxx:xxx:xxx:xxxx',
|
|||
// RoleSessionName: 'anything',
|
|||
// };
|
|||
// const assumedRole = await sts.assumeRole(params).promise();
|
|||
// if (!assumedRole) {
|
|||
// throw "Unable to get access to object store."
|
|||
// }
|
|||
// return assumedRole.Credentials
|
|||
// TODO: need to do something better than this
|
|||
return { |
|||
accessKeyId: env.MINIO_ACCESS_KEY, |
|||
secretAccessKey: env.MINIO_SECRET_KEY, |
|||
} |
|||
} |
|||
|
|||
exports.deploy = async ctx => { |
|||
ctx.body = { |
|||
couchDbSession: await getCouchSession(), |
|||
bucket: APP_BUCKET, |
|||
objectStoreSession: await getMinioSession(), |
|||
couchDbUrl: env.RAW_COUCH_DB_URL, |
|||
objectStoreUrl: env.RAW_MINIO_URL, |
|||
} |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
const Router = require("@koa/router") |
|||
const compress = require("koa-compress") |
|||
const zlib = require("zlib") |
|||
const { routes } = require("./routes") |
|||
|
|||
const router = new Router() |
|||
|
|||
router |
|||
.use( |
|||
compress({ |
|||
threshold: 2048, |
|||
gzip: { |
|||
flush: zlib.Z_SYNC_FLUSH, |
|||
}, |
|||
deflate: { |
|||
flush: zlib.Z_SYNC_FLUSH, |
|||
}, |
|||
br: false, |
|||
}) |
|||
) |
|||
.use("/health", ctx => (ctx.status = 200)) |
|||
|
|||
// error handling middleware
|
|||
router.use(async (ctx, next) => { |
|||
try { |
|||
await next() |
|||
} catch (err) { |
|||
ctx.log.error(err) |
|||
ctx.status = err.status || err.statusCode || 500 |
|||
ctx.body = { |
|||
message: err.message, |
|||
status: ctx.status, |
|||
} |
|||
} |
|||
}) |
|||
|
|||
router.get("/health", ctx => (ctx.status = 200)) |
|||
|
|||
// authenticated routes
|
|||
for (let route of routes) { |
|||
router.use(route.routes()) |
|||
router.use(route.allowedMethods()) |
|||
} |
|||
|
|||
module.exports = router |
|||
@ -0,0 +1,10 @@ |
|||
const Router = require("@koa/router") |
|||
const controller = require("../controllers/deploy") |
|||
const checkKey = require("../../middleware/check-key") |
|||
|
|||
const router = Router() |
|||
|
|||
router |
|||
.post("/api/deploy", checkKey, controller.deploy) |
|||
|
|||
module.exports = router |
|||
@ -0,0 +1,5 @@ |
|||
const deployRoutes = require("./deploy") |
|||
|
|||
exports.routes = [ |
|||
deployRoutes, |
|||
] |
|||
@ -0,0 +1,15 @@ |
|||
module.exports = { |
|||
SELF_HOSTED: process.env.SELF_HOSTED, |
|||
DEPLOYMENT_API_KEY: process.env.DEPLOYMENT_API_KEY, |
|||
PORT: process.env.PORT, |
|||
MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY, |
|||
MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY, |
|||
RAW_MINIO_URL: process.env.RAW_MINIO_URL, |
|||
COUCH_DB_USERNAME: process.env.COUCH_DB_USERNAME, |
|||
COUCH_DB_PASSWORD: process.env.COUCH_DB_PASSWORD, |
|||
RAW_COUCH_DB_URL: process.env.RAW_COUCH_DB_URL, |
|||
_set(key, value) { |
|||
process.env[key] = value |
|||
module.exports[key] = value |
|||
}, |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
const Koa = require("koa") |
|||
const destroyable = require("server-destroy") |
|||
const koaBody = require("koa-body") |
|||
const logger = require("koa-pino-logger") |
|||
const http = require("http") |
|||
const api = require("./api") |
|||
const env = require("./environment") |
|||
|
|||
const app = new Koa() |
|||
|
|||
if (!env.SELF_HOSTED) { |
|||
throw "Currently this service only supports use in self hosting" |
|||
} |
|||
|
|||
// set up top level koa middleware
|
|||
app.use(koaBody({ multipart: true })) |
|||
|
|||
app.use( |
|||
logger({ |
|||
prettyPrint: { |
|||
levelFirst: true, |
|||
}, |
|||
level: env.LOG_LEVEL || "error", |
|||
}) |
|||
) |
|||
|
|||
// api routes
|
|||
app.use(api.routes()) |
|||
|
|||
const server = http.createServer(app.callback()) |
|||
destroyable(server) |
|||
|
|||
server.on("close", () => console.log("Server Closed")) |
|||
|
|||
module.exports = server.listen(env.PORT || 4002, async () => { |
|||
console.log(`Deployment running on ${JSON.stringify(server.address())}`) |
|||
}) |
|||
|
|||
process.on("uncaughtException", err => { |
|||
console.error(err) |
|||
server.close() |
|||
server.destroy() |
|||
}) |
|||
|
|||
process.on("SIGTERM", () => { |
|||
server.close() |
|||
server.destroy() |
|||
}) |
|||
@ -0,0 +1,4 @@ |
|||
module.exports = async (ctx, next) => { |
|||
// TODO: need to check the API key provided in the header
|
|||
await next() |
|||
} |
|||
File diff suppressed because it is too large
Loading…
Reference in new issue