mirror of
https://github.com/seejohnrun/haste-server.git
synced 2025-12-21 16:31:29 +00:00
177 lines
5.0 KiB
TypeScript
177 lines
5.0 KiB
TypeScript
import { Request, Response } from 'express'
|
|
import * as winston from 'winston'
|
|
import Busboy from 'busboy'
|
|
import type { Config } from 'src/types/config'
|
|
import type { Store } from 'src/types/store'
|
|
import type { KeyGenerator } from 'src/types/key-generator'
|
|
import type { Document } from 'src/types/document'
|
|
import constants from 'src/constants'
|
|
|
|
class DocumentHandler {
|
|
keyLength: number
|
|
|
|
maxLength?: number
|
|
|
|
public store: Store
|
|
|
|
keyGenerator: KeyGenerator
|
|
|
|
config: Config
|
|
|
|
constructor(options: Document) {
|
|
this.keyLength = options.keyLength || constants.DEFAULT_KEY_LENGTH
|
|
this.maxLength = options.maxLength // none by default
|
|
this.store = options.store
|
|
this.config = options.config
|
|
this.keyGenerator = options.keyGenerator
|
|
}
|
|
|
|
public handleGet(request: Request, response: Response) {
|
|
const key = request.params.id.split('.')[0]
|
|
const skipExpire = !!this.config.documents[key]
|
|
|
|
this.store.get(
|
|
key,
|
|
ret => {
|
|
if (ret) {
|
|
winston.verbose('retrieved document', { key })
|
|
response.writeHead(200, { 'content-type': 'application/json' })
|
|
if (request.method === 'HEAD') {
|
|
response.end()
|
|
} else {
|
|
response.end(JSON.stringify({ data: ret, key }))
|
|
}
|
|
} else {
|
|
winston.warn('document not found', { key })
|
|
response.writeHead(404, { 'content-type': 'application/json' })
|
|
if (request.method === 'HEAD') {
|
|
response.end()
|
|
} else {
|
|
response.end(JSON.stringify({ message: 'Document not found.' }))
|
|
}
|
|
}
|
|
},
|
|
skipExpire
|
|
)
|
|
}
|
|
|
|
public handlePost(request: Request, response: Response) {
|
|
// const this = this
|
|
let buffer = ''
|
|
let cancelled = false
|
|
|
|
// What to do when done
|
|
const onSuccess = () => {
|
|
// Check length
|
|
if (this.maxLength && buffer.length > this.maxLength) {
|
|
cancelled = true
|
|
winston.warn('document >maxLength', { maxLength: this.maxLength })
|
|
response.writeHead(400, { 'content-type': 'application/json' })
|
|
response.end(
|
|
JSON.stringify({ message: 'Document exceeds maximum length.' })
|
|
)
|
|
return
|
|
}
|
|
// And then save if we should
|
|
this.chooseKey(key => {
|
|
this.store.set(key, buffer, res => {
|
|
if (res) {
|
|
winston.verbose('added document', { key })
|
|
response.writeHead(200, { 'content-type': 'application/json' })
|
|
response.end(JSON.stringify({ key }))
|
|
} else {
|
|
winston.verbose('error adding document')
|
|
response.writeHead(500, { 'content-type': 'application/json' })
|
|
response.end(JSON.stringify({ message: 'Error adding document.' }))
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
// If we should, parse a form to grab the data
|
|
const ct = request.headers['content-type']
|
|
if (ct && ct.split(';')[0] === 'multipart/form-data') {
|
|
const busboy = Busboy({ headers: request.headers })
|
|
busboy.on('field', (fieldname, val) => {
|
|
if (fieldname === 'data') {
|
|
buffer = val
|
|
}
|
|
})
|
|
busboy.on('finish', () => {
|
|
onSuccess()
|
|
})
|
|
request.pipe(busboy)
|
|
// Otherwise, use our own and just grab flat data from POST body
|
|
} else {
|
|
request.on('data', data => {
|
|
buffer += data.toString()
|
|
})
|
|
request.on('end', () => {
|
|
if (cancelled) {
|
|
return
|
|
}
|
|
onSuccess()
|
|
})
|
|
request.on('error', error => {
|
|
winston.error(`connection error: ${error.message}`)
|
|
response.writeHead(500, { 'content-type': 'application/json' })
|
|
response.end(JSON.stringify({ message: 'Connection error.' }))
|
|
cancelled = true
|
|
})
|
|
}
|
|
}
|
|
|
|
public handleRawGet(request: Request, response: Response) {
|
|
const key = request.params.id.split('.')[0]
|
|
const skipExpire = !!this.config.documents[key]
|
|
|
|
this.store.get(
|
|
key,
|
|
ret => {
|
|
if (ret) {
|
|
winston.verbose('retrieved raw document', { key })
|
|
response.writeHead(200, {
|
|
'content-type': 'text/plain; charset=UTF-8'
|
|
})
|
|
if (request.method === 'HEAD') {
|
|
response.end()
|
|
} else {
|
|
response.end(ret)
|
|
}
|
|
} else {
|
|
winston.warn('raw document not found', { key })
|
|
response.writeHead(404, { 'content-type': 'application/json' })
|
|
if (request.method === 'HEAD') {
|
|
response.end()
|
|
} else {
|
|
response.end(JSON.stringify({ message: 'Document not found.' }))
|
|
}
|
|
}
|
|
},
|
|
skipExpire
|
|
)
|
|
}
|
|
|
|
chooseKey = (callback: { (key: string): void }) => {
|
|
const key = this.acceptableKey()
|
|
|
|
if (!key) return
|
|
|
|
this.store.get(
|
|
key,
|
|
(ret: string | boolean) => {
|
|
if (ret) {
|
|
this.chooseKey(callback)
|
|
} else {
|
|
callback(key)
|
|
}
|
|
},
|
|
true
|
|
) // Don't bump expirations when key searching
|
|
}
|
|
|
|
acceptableKey = () => this.keyGenerator.createKey?.(this.keyLength)
|
|
}
|
|
|
|
export default DocumentHandler
|