-
Notifications
You must be signed in to change notification settings - Fork 0
/
passweaver-api.mjs
120 lines (102 loc) · 3.32 KB
/
passweaver-api.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/**
* PassWeaver, a collaborative password manager
*
* @module main
* @author Stefano Rivoir <[email protected]>
* @licence MIT
* @copyright (c) 2023-2024 - Stefano Rivoir <[email protected]>
*/
import Express from 'express'
import compression from 'compression'
import Morgan from 'morgan'
import * as RFS from 'rotating-file-stream'
import FS from 'fs'
import helmet from 'helmet'
import https from 'https'
import rateLimitMiddleware from './lib/ratelimiter.mjs'
import * as Config from './lib/config.mjs'
import * as Cache from './lib/cache.mjs'
import * as R from './lib/response.mjs'
// Routes
import folders from './api/v1/routes/folders.mjs'
import groups from './api/v1/routes/groups.mjs'
import items from './api/v1/routes/items.mjs'
import users from './api/v1/routes/users.mjs'
import login from './api/v1/routes/login.mjs'
import util from './api/v1/routes/util.mjs'
import events from './api/v1/routes/events.mjs'
import personal from './api/v1/routes/personal.mjs'
import itemtypes from './api/v1/routes/itemtypes.mjs'
import onetimetokens from './api/v1/routes/onetimetokens.mjs'
export const app = Express()
console.log(`PassWeaver API ${Config.packageJson().version} starting...`)
// Read config
const cfg = Config.get()
// Init cache
await Cache.init()
// Rate limiter
app.use(rateLimitMiddleware)
// Use json middleware
app.use(Express.json())
// Compression
app.use(compression({ threshold: 10240 }))
// HSTS
if (cfg?.https?.hsts) {
app.use(helmet.hsts())
}
if (!FS.existsSync(cfg.log.dir)) {
FS.mkdirSync(cfg.log.dir)
}
// Log requests
const logAccess = RFS.createStream(`${cfg.log.dir}/passweaver-api-access.log`, {
interval: cfg.log.rotation,
rotate: cfg.log.retention
})
app.use(
Morgan(':remote-addr - :remote-user [:date[clf]] \':method :url HTTP/:http-version\' :status :res[content-length] :total-time[0]',
{ stream: logAccess }
)
)
// Log errors
const logErrors = RFS.createStream(`${cfg.log.dir}/passweaver-api-errors.log`, {
interval: cfg.log.rotation,
rotate: cfg.log.retention
})
// Install routers
app.use('/api/v1/items', items)
app.use('/api/v1/folders', folders)
app.use('/api/v1/groups', groups)
app.use('/api/v1/users', users)
app.use('/api/v1/login', login)
app.use('/api/v1/util', util)
app.use('/api/v1/events', events)
app.use('/api/v1/personal', personal)
app.use('/api/v1/itemtypes', itemtypes)
app.use('/api/v1/onetimetokens', onetimetokens)
// Error handler
app.use((err, req, res, next) => {
logErrors.write(`[${(new Date()).toString()}]\n`)
logErrors.write(`${req.method} ${req.originalUrl}\n`)
logErrors.write(`${err.stack}\n`)
logErrors.write(`${err.message}\n`)
res.status(R.INTERNAL_SERVER_ERROR).send(R.ko('Internal error'))
})
// Error handler for invalid path/method
app.all(/.*/, (_req, res, _next) => {
res.status(R.INTERNAL_SERVER_ERROR).send({
status: 'failed',
message: 'Path not found, or invalid method',
data: {}
})
})
// HTTP(S) server startup
if (cfg.https.enabled) {
https.createServer({
key: FS.readFileSync(cfg.https.private_key),
cert: FS.readFileSync(cfg.https.certificate)
}, app).listen(cfg.listen.port, cfg.listen.host)
console.log(`Listening on '${cfg.listen.host}' port ${cfg.listen.port} (https)`)
} else {
console.log(`Listening on '${cfg.listen.host}' port ${cfg.listen.port} (http)`)
app.listen(cfg.listen.port, cfg.listen.host)
}