platform/server/front/src/app.ts
Andrey Sobolev 5435c4162d
Duplicate CSS + Compression (#1071)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
2022-03-01 10:31:47 +07:00

384 lines
12 KiB
TypeScript

//
// Copyright © 2020, 2021 Anticrm Platform Contributors.
// Copyright © 2021 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
import attachment from '@anticrm/attachment'
import { Account, Doc, Ref, Space } from '@anticrm/core'
import { createElasticAdapter } from '@anticrm/elastic'
import type { IndexedDoc } from '@anticrm/server-core'
import { decodeToken } from '@anticrm/server-token'
import bp from 'body-parser'
import compression from 'compression'
import cors from 'cors'
import express from 'express'
import fileUpload, { UploadedFile } from 'express-fileupload'
import https from 'https'
import { Client, ItemBucketMetadata } from 'minio'
import { join, resolve } from 'path'
import { v4 as uuid } from 'uuid'
async function minioUpload (minio: Client, workspace: string, file: UploadedFile): Promise<string> {
const id = uuid()
const meta: ItemBucketMetadata = {
'Content-Type': file.mimetype
}
const resp = await minio.putObject(workspace, id, file.data, file.size, meta)
console.log(resp)
return id
}
/**
* @public
* @param port -
*/
export function start (config: { transactorEndpoint: string, elasticUrl: string, minio: Client, accountsUrl: string, uploadUrl: string, modelVersion: string }, port: number): () => void {
const app = express()
app.use(compression({
filter: (req, res) => {
if (req.headers['x-no-compression'] != null) {
// don't compress responses with this request header
return false
}
// fallback to standard filter function
return compression.filter(req, res)
}
}))
app.use(cors())
app.use(fileUpload())
app.use(bp.json())
app.use(bp.urlencoded({ extended: true }))
// eslint-disable-next-line @typescript-eslint/no-misused-promises
app.get('/config.json', async (req, res) => {
res.status(200)
res.set('Cache-Control', 'no-cache')
res.json(
{
ACCOUNTS_URL: config.accountsUrl,
UPLOAD_URL: config.uploadUrl,
MODEL_VERSION: config.modelVersion
}
)
})
const dist = resolve(process.env.PUBLIC_DIR ?? __dirname, 'dist')
console.log('serving static files from', dist)
app.use(express.static(dist, { maxAge: '168h' }))
// eslint-disable-next-line @typescript-eslint/no-misused-promises
app.get('/files', async (req, res) => {
try {
const token = req.query.token as string
const payload = decodeToken(token)
const uuid = req.query.file as string
const stat = await config.minio.statObject(payload.workspace, uuid)
config.minio.getObject(payload.workspace, uuid, function (err, dataStream) {
if (err !== null) {
return console.log(err)
}
res.status(200)
res.set('Cache-Control', 'max-age=604800')
const contentType = stat.metaData['content-type']
if (contentType !== undefined) {
res.setHeader('Content-Type', contentType)
}
dataStream.on('data', function (chunk) {
res.write(chunk)
})
dataStream.on('end', function () {
res.end()
})
dataStream.on('error', function (err) {
console.log(err)
})
})
} catch (error) {
console.log(error)
res.status(500).send()
}
})
// eslint-disable-next-line @typescript-eslint/no-misused-promises
app.post('/files', async (req, res) => {
const file = req.files?.file as UploadedFile
if (file === undefined) {
res.status(400).send()
return
}
const authHeader = req.headers.authorization
if (authHeader === undefined) {
res.status(403).send()
return
}
try {
const token = authHeader.split(' ')[1]
const payload = decodeToken(token)
// const fileId = await awsUpload(file as UploadedFile)
const uuid = await minioUpload(config.minio, payload.workspace, file)
console.log('uploaded uuid', uuid)
const space = req.query.space as Ref<Space> | undefined
const attachedTo = req.query.attachedTo as Ref<Doc> | undefined
// const name = req.query.name as string
// await createAttachment(
// transactorEndpoint,
// token,
// 'core:account:System' as Ref<Account>,
// space,
// attachedTo,
// collection,
// name,
// fileId
// )
if (space !== undefined && attachedTo !== undefined) {
const elastic = await createElasticAdapter(config.elasticUrl, payload.workspace)
const indexedDoc: IndexedDoc = {
id: uuid as Ref<Doc>,
_class: attachment.class.Attachment,
space,
modifiedOn: Date.now(),
modifiedBy: 'core:account:System' as Ref<Account>,
attachedTo,
data: file.data.toString('base64')
}
await elastic.index(indexedDoc)
}
res.status(200).send(uuid)
} catch (error) {
console.log(error)
res.status(500).send()
}
})
// eslint-disable-next-line @typescript-eslint/no-misused-promises
app.delete('/files', async (req, res) => {
try {
const authHeader = req.headers.authorization
if (authHeader === undefined) {
res.status(403).send()
return
}
const token = authHeader.split(' ')[1]
const payload = decodeToken(token)
const uuid = req.query.file as string
await config.minio.removeObject(payload.workspace, uuid)
res.status(200).send()
} catch (error) {
console.log(error)
res.status(500).send()
}
})
// todo remove it after update all customers chrome extensions
app.get('/import', (req, res) => {
try {
const authHeader = req.headers.authorization
if (authHeader === undefined) {
res.status(403).send()
return
}
const token = authHeader.split(' ')[1]
const payload = decodeToken(token)
const url = req.query.url as string
const cookie = req.query.cookie as string | undefined
const attachedTo = req.query.attachedTo as Ref<Doc> | undefined
if (url === undefined) {
res.status(500).send('URL param is not defined')
return
}
console.log('importing from', url)
console.log('cookie', cookie)
const options = cookie !== undefined
? {
headers: {
Cookie: cookie
}
}
: {}
https.get(url, options, response => {
console.log('status', response.statusCode)
if (response.statusCode !== 200) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
res.status(500).send(`server returned ${response.statusCode}`)
return
}
const id = uuid()
const contentType = response.headers['content-type']
const meta: ItemBucketMetadata = {
'Content-Type': contentType
}
const data: Buffer[] = []
response.on('data', function (chunk) {
data.push(chunk)
}).on('end', function () {
const buffer = Buffer.concat(data)
// eslint-disable-next-line @typescript-eslint/no-misused-promises
config.minio.putObject(payload.workspace, id, buffer, 0, meta, async (err, objInfo) => {
if (err !== null) {
console.log('minio putObject error', err)
res.status(500).send(err)
} else {
console.log('uploaded uuid', id)
if (attachedTo !== undefined) {
const space = req.query.space as Ref<Space>
const elastic = await createElasticAdapter(config.elasticUrl, payload.workspace)
const indexedDoc: IndexedDoc = {
id: id as Ref<Doc>,
_class: attachment.class.Attachment,
space,
modifiedOn: Date.now(),
modifiedBy: 'core:account:System' as Ref<Account>,
attachedTo,
data: buffer.toString('base64')
}
await elastic.index(indexedDoc)
}
res.status(200).send({
id,
contentType,
size: buffer.length
})
}
})
}).on('error', function (err) {
res.status(500).send(err)
})
})
} catch (error) {
console.log(error)
res.status(500).send()
}
})
app.post('/import', (req, res) => {
try {
const authHeader = req.headers.authorization
if (authHeader === undefined) {
res.status(403).send()
return
}
const token = authHeader.split(' ')[1]
const payload = decodeToken(token)
const { url, cookie, attachedTo, space } = req.body
if (url === undefined) {
res.status(500).send('URL param is not defined')
return
}
console.log('importing from', url)
console.log('cookie', cookie)
const options = cookie !== undefined
? {
headers: {
Cookie: cookie
}
}
: {}
https.get(url, options, response => {
console.log('status', response.statusCode)
if (response.statusCode !== 200) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
res.status(500).send(`server returned ${response.statusCode}`)
return
}
const id = uuid()
const contentType = response.headers['content-type']
const meta: ItemBucketMetadata = {
'Content-Type': contentType
}
const data: Buffer[] = []
response.on('data', function (chunk) {
data.push(chunk)
}).on('end', function () {
const buffer = Buffer.concat(data)
// eslint-disable-next-line @typescript-eslint/no-misused-promises
config.minio.putObject(payload.workspace, id, buffer, 0, meta, async (err, objInfo) => {
if (err !== null) {
console.log('minio putObject error', err)
res.status(500).send(err)
} else {
console.log('uploaded uuid', id)
if (attachedTo !== undefined) {
const elastic = await createElasticAdapter(config.elasticUrl, payload.workspace)
const indexedDoc: IndexedDoc = {
id: id as Ref<Doc>,
_class: attachment.class.Attachment,
space,
modifiedOn: Date.now(),
modifiedBy: 'core:account:System' as Ref<Account>,
attachedTo,
data: buffer.toString('base64')
}
await elastic.index(indexedDoc)
}
res.status(200).send({
id,
contentType,
size: buffer.length
})
}
})
}).on('error', function (err) {
res.status(500).send(err)
})
})
} catch (error) {
console.log(error)
res.status(500).send()
}
})
app.get('*', function (request, response) {
response.sendFile(join(dist, 'index.html'))
})
const server = app.listen(port)
return () => {
server.close()
}
}