Update docs

This commit is contained in:
Alan Grainger 2024-11-11 19:34:49 +01:00
parent e17137d1a8
commit 6608d8ca5b
4 changed files with 14 additions and 11 deletions

View File

@ -1,6 +1,6 @@
import crypto from 'crypto' import crypto from 'crypto'
interface Payload { interface EncryptedPayload {
iv: string; iv: string;
cr: string; // Encrypted data cr: string; // Encrypted data
} }
@ -9,7 +9,7 @@ interface Payload {
const key = crypto.randomBytes(32) const key = crypto.randomBytes(32)
const algorithm = 'aes-256-cbc' const algorithm = 'aes-256-cbc'
export function encrypt (text: string): Payload { export function encrypt (text: string): EncryptedPayload {
try { try {
const ivBuf = crypto.randomBytes(16) const ivBuf = crypto.randomBytes(16)
const cipher = crypto.createCipheriv(algorithm, Buffer.from(key), ivBuf) const cipher = crypto.createCipheriv(algorithm, Buffer.from(key), ivBuf)
@ -27,7 +27,7 @@ export function encrypt (text: string): Payload {
} }
} }
export function decrypt (payload: Payload) { export function decrypt (payload: EncryptedPayload) {
try { try {
const decipher = crypto.createDecipheriv(algorithm, Buffer.from(key), Buffer.from(payload.iv, 'hex')) const decipher = crypto.createDecipheriv(algorithm, Buffer.from(key), Buffer.from(payload.iv, 'hex'))
let decrypted = decipher.update(payload.cr, 'hex', 'utf8') let decrypted = decipher.update(payload.cr, 'hex', 'utf8')

View File

@ -9,7 +9,7 @@ try {
} catch (e) { } } catch (e) { }
/** /**
* Get a configuration option using dotted notation. * Get a configuration option fron config.json using dotted notation.
* *
* @param path * @param path
* @param [defaultOption] - Specify a default option to return if no configuation value is found * @param [defaultOption] - Specify a default option to return if no configuation value is found
@ -39,6 +39,9 @@ export function getSize (req: Request) {
return req.query?.size === 'thumbnail' ? ImageSize.thumbnail : ImageSize.original return req.query?.size === 'thumbnail' ? ImageSize.thumbnail : ImageSize.original
} }
/**
* Force a value to be a string
*/
export function toString (value: unknown) { export function toString (value: unknown) {
return typeof value === 'string' ? value : '' return typeof value === 'string' ? value : ''
} }

View File

@ -45,8 +45,8 @@ class Immich {
*/ */
async handleShareRequest (request: IncomingShareRequest, res: Response) { async handleShareRequest (request: IncomingShareRequest, res: Response) {
addResponseHeaders(res) addResponseHeaders(res)
// Check that the key is a valid format
if (!immich.isKey(request.key)) { if (!immich.isKey(request.key)) {
// This is not a valid key format
log('Invalid share key ' + request.key) log('Invalid share key ' + request.key)
res.status(404).send() res.status(404).send()
} else { } else {
@ -61,13 +61,14 @@ class Immich {
res.status(401).send() res.status(401).send()
} else if (sharedLinkRes.passwordRequired) { } else if (sharedLinkRes.passwordRequired) {
// Password required - show the visitor the password page // Password required - show the visitor the password page
// `req.params.key` should already be sanitised at this point, but it never hurts to be explicit // `req.params.key` is already sanitised at this point, but it never hurts to be explicit
const key = request.key.replace(/[^\w-]/g, '') const key = request.key.replace(/[^\w-]/g, '')
res.render('password', { key, lgConfig: render.lgConfig }) res.render('password', { key, lgConfig: render.lgConfig })
} else if (sharedLinkRes.link) { } else if (sharedLinkRes.link) {
// Valid shared link // Valid shared link
const link = sharedLinkRes.link const link = sharedLinkRes.link
if (!link.assets.length) { if (!link.assets.length) {
// Immich didn't return any assets for this link (empty array)
log('No assets for key ' + request.key) log('No assets for key ' + request.key)
res.status(404).send() res.status(404).send()
} else if (link.assets.length === 1) { } else if (link.assets.length === 1) {
@ -105,8 +106,7 @@ class Immich {
password password
}) })
const res = await fetch(url) const res = await fetch(url)
const contentType = res.headers.get('Content-Type') || '' if ((res.headers.get('Content-Type') || '').includes('application/json')) {
if (contentType.includes('application/json')) {
const jsonBody = await res.json() const jsonBody = await res.json()
if (jsonBody) { if (jsonBody) {
if (res.status === 200) { if (res.status === 200) {

View File

@ -20,7 +20,7 @@ class Render {
const subpath = asset.type === AssetType.video ? '/video/playback' : '/' + size const subpath = asset.type === AssetType.video ? '/video/playback' : '/' + size
const headers = { range: '' } const headers = { range: '' }
// Stream the video in 2.5MB chunks // For videos, request them in 2.5MB chunks rather than the entire video
if (asset.type === AssetType.video) { if (asset.type === AssetType.video) {
const range = (req.range || '').replace(/bytes=/, '').split('-') const range = (req.range || '').replace(/bytes=/, '').split('-')
const start = parseInt(range[0], 10) || 0 const start = parseInt(range[0], 10) || 0
@ -39,12 +39,12 @@ class Render {
// Return the response to the client // Return the response to the client
if (data.status >= 200 && data.status < 300) { if (data.status >= 200 && data.status < 300) {
// Populate the response headers // Populate the whitelisted response headers
headerList.forEach(header => { headerList.forEach(header => {
const value = data.headers.get(header) const value = data.headers.get(header)
if (value) res.setHeader(header, value) if (value) res.setHeader(header, value)
}) })
// Return the body // Return the Immich asset binary data
await data.body?.pipeTo( await data.body?.pipeTo(
new WritableStream({ new WritableStream({
write (chunk) { res.write(chunk) } write (chunk) { res.write(chunk) }