2024-10-28 04:56:11 -04:00
|
|
|
import express from 'express'
|
2024-10-28 15:47:14 -04:00
|
|
|
import immich from './immich'
|
2024-10-29 05:29:14 -04:00
|
|
|
import render from './render'
|
2024-10-28 04:56:11 -04:00
|
|
|
import dayjs from 'dayjs'
|
2024-11-12 04:27:26 -05:00
|
|
|
import { AssetType, ImageSize } from './types'
|
2024-11-01 06:58:27 -04:00
|
|
|
import { decrypt } from './encrypt'
|
2024-11-12 03:48:03 -05:00
|
|
|
import { log, toString, addResponseHeaders } from './functions'
|
2024-10-28 15:47:14 -04:00
|
|
|
|
|
|
|
require('dotenv').config()
|
2024-10-28 04:08:16 -04:00
|
|
|
|
|
|
|
const app = express()
|
2024-10-30 10:43:07 -04:00
|
|
|
// Add the EJS view engine, to render the gallery page
|
2024-10-28 15:47:14 -04:00
|
|
|
app.set('view engine', 'ejs')
|
2024-11-01 06:58:27 -04:00
|
|
|
// For parsing the password unlock form
|
|
|
|
app.use(express.json())
|
2024-11-04 14:23:34 -05:00
|
|
|
// Serve static assets from the /public folder
|
|
|
|
app.use(express.static('public', { setHeaders: addResponseHeaders }))
|
2024-10-28 04:08:16 -04:00
|
|
|
|
2024-10-30 10:40:42 -04:00
|
|
|
// An incoming request for a shared link
|
2024-10-28 04:08:16 -04:00
|
|
|
app.get('/share/:key', async (req, res) => {
|
2024-11-01 06:58:27 -04:00
|
|
|
await immich.handleShareRequest({
|
2024-11-12 03:48:03 -05:00
|
|
|
key: req.params.key
|
2024-11-01 06:58:27 -04:00
|
|
|
}, res)
|
|
|
|
})
|
|
|
|
|
|
|
|
// Receive an unlock request from the password page
|
|
|
|
app.post('/unlock', async (req, res) => {
|
|
|
|
await immich.handleShareRequest({
|
|
|
|
key: toString(req.body.key),
|
|
|
|
password: toString(req.body.password)
|
|
|
|
}, res)
|
2024-10-28 04:08:16 -04:00
|
|
|
})
|
|
|
|
|
2024-10-30 10:40:42 -04:00
|
|
|
// Output the buffer data for a photo or video
|
2024-11-12 03:48:03 -05:00
|
|
|
app.get('/:type(photo|video)/:key/:id/:size?', async (req, res) => {
|
2024-11-04 14:23:34 -05:00
|
|
|
addResponseHeaders(res)
|
2024-10-29 10:07:54 -04:00
|
|
|
// Check for valid key and ID
|
|
|
|
if (immich.isKey(req.params.key) && immich.isId(req.params.id)) {
|
2024-11-12 04:27:26 -05:00
|
|
|
// Validate the size parameter
|
|
|
|
if (req.params.size && !Object.values(ImageSize).includes(req.params.size as ImageSize)) {
|
|
|
|
log('Invalid size parameter ' + req.path)
|
|
|
|
res.status(404).send()
|
|
|
|
return
|
|
|
|
}
|
2024-11-01 06:58:27 -04:00
|
|
|
let password
|
2024-11-03 13:48:32 -05:00
|
|
|
// Validate the password payload, if one was provided
|
2024-11-01 06:58:27 -04:00
|
|
|
if (req.query?.cr && req.query?.iv) {
|
2024-11-03 13:48:32 -05:00
|
|
|
try {
|
|
|
|
const payload = JSON.parse(decrypt({
|
|
|
|
iv: toString(req.query.iv),
|
|
|
|
cr: toString(req.query.cr)
|
|
|
|
}))
|
|
|
|
if (payload?.expires && dayjs(payload.expires) > dayjs()) {
|
|
|
|
password = payload.password
|
|
|
|
} else {
|
|
|
|
log(`Attempted to load assets from ${req.params.key} with an expired decryption token`)
|
|
|
|
}
|
|
|
|
} catch (e) { }
|
2024-11-01 06:58:27 -04:00
|
|
|
}
|
2024-10-29 10:07:54 -04:00
|
|
|
// Check if the key is a valid share link
|
2024-11-01 06:58:27 -04:00
|
|
|
const sharedLink = (await immich.getShareByKey(req.params.key, password))?.link
|
2024-11-10 15:00:41 -05:00
|
|
|
const request = { key: req.params.key, range: req.headers.range || '' }
|
2024-10-29 10:07:54 -04:00
|
|
|
if (sharedLink?.assets.length) {
|
|
|
|
// Check that the requested asset exists in this share
|
|
|
|
const asset = sharedLink.assets.find(x => x.id === req.params.id)
|
|
|
|
if (asset) {
|
|
|
|
asset.type = req.params.type === 'video' ? AssetType.video : AssetType.image
|
2024-11-12 04:27:26 -05:00
|
|
|
render.assetBuffer(request, res, asset, req.params.size).then()
|
2024-10-29 10:07:54 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2024-10-29 05:29:14 -04:00
|
|
|
}
|
2024-10-30 09:28:29 -04:00
|
|
|
log('No asset found for ' + req.path)
|
2024-10-29 10:07:54 -04:00
|
|
|
res.status(404).send()
|
2024-10-29 06:48:16 -04:00
|
|
|
})
|
|
|
|
|
2024-10-31 08:21:57 -04:00
|
|
|
// Healthcheck
|
2024-11-04 05:15:00 -05:00
|
|
|
app.get('/healthcheck', async (_req, res) => {
|
|
|
|
if (await immich.accessible()) {
|
|
|
|
res.send('ok')
|
|
|
|
} else {
|
|
|
|
res.status(502).send()
|
|
|
|
}
|
2024-10-31 08:21:57 -04:00
|
|
|
})
|
|
|
|
|
2024-10-30 10:40:42 -04:00
|
|
|
// Send a 404 for all other routes
|
2024-10-30 09:28:29 -04:00
|
|
|
app.get('*', (req, res) => {
|
|
|
|
log('Invalid route ' + req.path)
|
2024-10-29 06:48:16 -04:00
|
|
|
res.status(404).send()
|
2024-10-28 15:47:14 -04:00
|
|
|
})
|
|
|
|
|
2024-10-28 04:08:16 -04:00
|
|
|
app.listen(3000, () => {
|
|
|
|
console.log(dayjs().format() + ' Server started')
|
|
|
|
})
|