Skip to content

Commit

Permalink
feat(runtime): proxy cloud storage & website hosting request to minio (
Browse files Browse the repository at this point in the history
…#1560)

* feat(runtime): proxy cloud storage to minio

* fix(runtime): fix minio proxy

* feat(runtime): add request_id for function required

* chore: expose port for container

* chore(runtime): change ctx field name
  • Loading branch information
0fatal authored Oct 7, 2023
1 parent 1f7d6af commit 5456de2
Show file tree
Hide file tree
Showing 18 changed files with 383 additions and 128 deletions.
2 changes: 2 additions & 0 deletions runtimes/nodejs/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ RUN apt update && apt-get install build-essential libcairo2-dev libpango1.0-dev
# RUN npm install npm -g

EXPOSE 8000
EXPOSE 9000

WORKDIR /app
ENV LOG_LEVEL=debug
COPY . /app
Expand Down
81 changes: 81 additions & 0 deletions runtimes/nodejs/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions runtimes/nodejs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"dotenv": "^8.2.0",
"ejs": "^3.1.8",
"express": "^4.18.2",
"express-http-proxy": "^2.0.0",
"express-xml-bodyparser": "^0.3.0",
"jsonwebtoken": "^9.0.0",
"lodash": "^4.17.21",
Expand All @@ -56,6 +57,7 @@
"@types/dotenv": "^8.2.0",
"@types/ejs": "^3.1.1",
"@types/express": "^4.17.15",
"@types/express-http-proxy": "^1.6.3",
"@types/express-xml-bodyparser": "^0.3.2",
"@types/jsonwebtoken": "^8.5.1",
"@types/lodash": "^4.14.171",
Expand Down
18 changes: 15 additions & 3 deletions runtimes/nodejs/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ export default class Config {
return (process.env.PORT ?? 8000) as number
}

static get STORAGE_PORT(): number {
return (process.env.STORAGE_PORT ?? 9000) as number
}

/**
* in production deploy or not
*/
Expand Down Expand Up @@ -77,15 +81,23 @@ export default class Config {
return process.env.REQUEST_LIMIT_SIZE || '10mb'
}

static get LOG_SERVER_URL(): string {
static get LOG_SERVER_URL(): string {
return process.env.LOG_SERVER_URL || ''
}

static get LOG_SERVER_TOKEN(): string {
static get LOG_SERVER_TOKEN(): string {
return process.env.LOG_SERVER_TOKEN || ''
}

static get CHANGE_STREAM_RECONNECT_INTERVAL(): number {
static get CHANGE_STREAM_RECONNECT_INTERVAL(): number {
return (process.env.CHANGE_STREAM_RECONNECT_INTERVAL || 3000) as number
}

static get OSS_INTERNAL_ENDPOINT(): string {
return process.env.OSS_INTERNAL_ENDPOINT || ''
}

static get OSS_EXTERNAL_ENDPOINT(): string {
return process.env.OSS_EXTERNAL_ENDPOINT
}
}
1 change: 1 addition & 0 deletions runtimes/nodejs/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
export const CLOUD_FUNCTION_COLLECTION = '__functions__'
export const POLICY_COLLECTION = '__policies__'
export const CONFIG_COLLECTION = '__conf__'
export const WEBSITE_HOSTING_COLLECTION = '__website_hosting__'

export const WEBSOCKET_FUNCTION_NAME = '__websocket__'
export const INTERCEPTOR_FUNCTION_NAME = '__interceptor__'
Expand Down
2 changes: 1 addition & 1 deletion runtimes/nodejs/src/handler/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,4 @@ router.all('*', uploader.any(), (req, res) => {

req.params['name'] = func_name
handleInvokeFunction(req, res)
})
})
18 changes: 9 additions & 9 deletions runtimes/nodejs/src/handler/typings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ const nodeModulesRoot = path.resolve(__dirname, '../../node_modules')
export async function handlePackageTypings(req: IRequest, res: Response) {
const requestId = req['requestId']

// verify the debug token
const token = req.get('x-laf-develop-token')
if (!token) {
return res.status(400).send('x-laf-develop-token is required')
}
const auth = parseToken(token) || null
if (auth?.type !== 'develop') {
return res.status(403).send('permission denied: invalid develop token')
}
// verify the debug token
const token = req.get('x-laf-develop-token')
if (!token) {
return res.status(400).send('x-laf-develop-token is required')
}
const auth = parseToken(token) || null
if (auth?.type !== 'develop') {
return res.status(403).send('permission denied: invalid develop token')
}

const packageName = req.query.packageName as string
if (!packageName) {
Expand Down
17 changes: 10 additions & 7 deletions runtimes/nodejs/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ import xmlparser from 'express-xml-bodyparser'

// init static method of class
import './support/cloud-sdk'
import storageServer from './storage-server'
import { DatabaseChangeStream } from './support/database-change-stream'
import { FunctionCache } from './support/function-engine/cache'
import { DatabaseChangeStream } from './support/db-change-stream'

const app = express()

DatabaseAgent.accessor.ready.then(() => {
FunctionCache.initialize()
DatabaseChangeStream.initialize()
FunctionCache.initialize()
})

if (process.env.NODE_ENV === 'development') {
Expand Down Expand Up @@ -69,7 +70,8 @@ app.use(function (req, res, next) {
if (req.url !== '/_/healthz') {
logger.info(
requestId,
`${req.method} "${req.url}" - referer: ${req.get('referer') || '-'
`${req.method} "${req.url}" - referer: ${
req.get('referer') || '-'
} ${req.get('user-agent')}`,
)
logger.trace(requestId, `${req.method} ${req.url}`, {
Expand Down Expand Up @@ -102,8 +104,9 @@ process.on('SIGINT', gracefullyExit)

async function gracefullyExit() {
await DatabaseAgent.accessor.close()
server.close(async () => {
logger.info('process gracefully exited!')
process.exit(0)
})
await server.close()
await storageServer.close()

logger.info('process gracefully exited!')
process.exit(0)
}
79 changes: 79 additions & 0 deletions runtimes/nodejs/src/storage-server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import express from 'express'
import Config from './config'
import { logger } from './support/logger'
import { DatabaseAgent } from './db'
import './support/cloud-sdk'
import { WebsiteHostingChangeStream } from './support/database-change-stream/website-hosting-change-stream'
import proxy from 'express-http-proxy'
import axios from 'axios'

const app = express()

DatabaseAgent.accessor.ready.then(() => {
WebsiteHostingChangeStream.initialize()
})

const tryPath = (bucket: string, path: string) => {
const testPaths = path.endsWith('/')
? [path + 'index.html', '/index.html']
: [path, path + '/index.html', '/index.html']
return testPaths.map((v) => `/${bucket}${v}`)
}

app.use(
proxy(Config.OSS_INTERNAL_ENDPOINT, {
preserveHostHdr: true,
parseReqBody: false,
proxyReqOptDecorator: function (proxyReqOpts, srcReq) {
// patch for
if ('content-length' in srcReq.headers) {
proxyReqOpts.headers['content-length'] =
srcReq.headers['content-length']
}
if ('connection' in srcReq.headers) {
proxyReqOpts.headers['connection'] = srcReq.headers['connection']
}
return proxyReqOpts
},
proxyReqPathResolver: async function (req) {
// check if is website hosting
const websiteHosting = WebsiteHostingChangeStream.websiteHosting.find(
(item) => req.hostname === item.domain,
)
if (!websiteHosting) {
return req.url
}

// req.url doesn't have hostname
const minioUrl = new URL(req.url, Config.OSS_INTERNAL_ENDPOINT)
const paths = tryPath(websiteHosting.bucketName, req.path)
const getUrl = () => minioUrl.pathname + minioUrl.search

for (const [idx, path] of paths.entries()) {
minioUrl.pathname = path

if (idx === paths.length - 1) {
return getUrl()
}

try {
await axios.head(minioUrl.toString())
return getUrl()
} catch (err) {
if (err.response.status === 404) {
continue
}
return getUrl()
}
}
},
}),
)

const storageServer = app.listen(Config.STORAGE_PORT, () =>
logger.info(
`storage server ${process.pid} listened on ${Config.STORAGE_PORT}`,
),
)

export default storageServer
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { CONFIG_COLLECTION } from '../../constants'
import { DatabaseAgent } from '../../db'
import { DatabaseChangeStream } from '.'

export class ConfChangeStream {
static initialize() {
DatabaseChangeStream.onStreamChange(
CONFIG_COLLECTION,
this.updateEnvironments,
)
}

private static async updateEnvironments() {
const conf = await DatabaseAgent.db
.collection(CONFIG_COLLECTION)
.findOne({})

if (!conf) {
return
}

const environments = conf.environments || []
for (const env of environments) {
process.env[env.name] = env.value
}
}
}
Loading

0 comments on commit 5456de2

Please sign in to comment.