Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(runtime): proxy cloud storage request to minio #1560

Merged
merged 9 commits into from
Oct 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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