Changeset View
Changeset View
Standalone View
Standalone View
keyserver/src/fetchers/upload-fetchers.js
// @flow | // @flow | ||||
import _keyBy from 'lodash/fp/keyBy.js'; | import _keyBy from 'lodash/fp/keyBy.js'; | ||||
import type { Media } from 'lib/types/media-types.js'; | import type { Media } from 'lib/types/media-types.js'; | ||||
import { messageTypes } from 'lib/types/message-types.js'; | |||||
import type { MediaMessageServerDBContent } from 'lib/types/messages/media.js'; | import type { MediaMessageServerDBContent } from 'lib/types/messages/media.js'; | ||||
import { getUploadIDsFromMediaMessageServerDBContents } from 'lib/types/messages/media.js'; | import { getUploadIDsFromMediaMessageServerDBContents } from 'lib/types/messages/media.js'; | ||||
import { threadPermissions } from 'lib/types/thread-types.js'; | |||||
import type { | |||||
ThreadFetchMediaResult, | |||||
ThreadFetchMediaRequest, | |||||
} from 'lib/types/thread-types.js'; | |||||
import { ServerError } from 'lib/utils/errors.js'; | import { ServerError } from 'lib/utils/errors.js'; | ||||
import { dbQuery, SQL } from '../database/database.js'; | import { dbQuery, SQL } from '../database/database.js'; | ||||
import type { Viewer } from '../session/viewer.js'; | import type { Viewer } from '../session/viewer.js'; | ||||
import { getAndAssertCommAppURLFacts } from '../utils/urls.js'; | import { getAndAssertCommAppURLFacts } from '../utils/urls.js'; | ||||
type UploadInfo = { | type UploadInfo = { | ||||
content: Buffer, | content: Buffer, | ||||
▲ Show 20 Lines • Show All 95 Lines • ▼ Show 20 Lines | SELECT id AS uploadID, secret AS uploadSecret, | ||||
type AS uploadType, extra AS uploadExtra | type AS uploadType, extra AS uploadExtra | ||||
FROM uploads | FROM uploads | ||||
WHERE id IN (${mediaIDs}) AND uploader = ${viewer.id} AND container IS NULL | WHERE id IN (${mediaIDs}) AND uploader = ${viewer.id} AND container IS NULL | ||||
`; | `; | ||||
const [result] = await dbQuery(query); | const [result] = await dbQuery(query); | ||||
return result.map(mediaFromRow); | return result.map(mediaFromRow); | ||||
} | } | ||||
async function fetchMediaForThread( | |||||
viewer: Viewer, | |||||
request: ThreadFetchMediaRequest, | |||||
): Promise<ThreadFetchMediaResult> { | |||||
const visibleExtractString = `$.${threadPermissions.VISIBLE}.value`; | |||||
const query = SQL` | |||||
SELECT content.photo AS uploadID, | |||||
u.secret AS uploadSecret, | |||||
u.type AS uploadType, u.extra AS uploadExtra, | |||||
u.container, u.creation_time, | |||||
NULL AS thumbnailID, | |||||
NULL AS thumbnailUploadSecret | |||||
FROM messages m | |||||
LEFT JOIN JSON_TABLE( | |||||
m.content, | |||||
"$[*]" COLUMNS(photo INT PATH "$") | |||||
) content ON 1 | |||||
LEFT JOIN uploads u ON u.id = content.photo | |||||
LEFT JOIN memberships mm ON mm.thread = ${request.threadID} | |||||
AND mm.user = ${viewer.id} | |||||
WHERE m.thread = ${request.threadID} AND m.type = ${messageTypes.IMAGES} | |||||
AND JSON_EXTRACT(mm.permissions, ${visibleExtractString}) IS TRUE | |||||
UNION SELECT content.media AS uploadID, | |||||
uv.secret AS uploadSecret, | |||||
uv.type AS uploadType, uv.extra AS uploadExtra, | |||||
uv.container, uv.creation_time, | |||||
content.thumbnail AS thumbnailID, | |||||
ut.secret AS thumbnailUploadSecret | |||||
FROM messages m | |||||
LEFT JOIN JSON_TABLE( | |||||
m.content, | |||||
"$[*]" COLUMNS( | |||||
media INT PATH "$.uploadID", | |||||
thumbnail INT PATH "$.thumbnailUploadID" | |||||
) | |||||
) content ON 1 | |||||
LEFT JOIN uploads uv ON uv.id = content.media | |||||
LEFT JOIN uploads ut ON ut.id = content.thumbnail | |||||
LEFT JOIN memberships mm ON mm.thread = ${request.threadID} | |||||
AND mm.user = ${viewer.id} | |||||
WHERE m.thread = ${request.threadID} | |||||
AND m.type = ${messageTypes.MULTIMEDIA} | |||||
AND JSON_EXTRACT(mm.permissions, ${visibleExtractString}) IS TRUE | |||||
ORDER BY creation_time DESC | |||||
LIMIT ${request.limit} OFFSET ${request.offset} | |||||
`; | |||||
const [uploads] = await dbQuery(query); | |||||
const media = uploads.map(upload => { | |||||
const { uploadID, uploadType, uploadSecret, uploadExtra } = upload; | |||||
const { width, height } = JSON.parse(uploadExtra); | |||||
const dimensions = { width, height }; | |||||
if (uploadType === 'photo') { | |||||
return { | |||||
type: 'photo', | |||||
id: uploadID, | |||||
uri: getUploadURL(uploadID, uploadSecret), | |||||
dimensions, | |||||
}; | |||||
} | |||||
const { thumbnailID, thumbnailUploadSecret } = upload; | |||||
return { | |||||
type: 'video', | |||||
id: uploadID, | |||||
uri: getUploadURL(uploadID, uploadSecret), | |||||
dimensions, | |||||
thumbnailID, | |||||
thumbnailURI: getUploadURL(thumbnailID, thumbnailUploadSecret), | |||||
}; | |||||
}); | |||||
return { media }; | |||||
} | |||||
async function fetchUploadsForMessage( | async function fetchUploadsForMessage( | ||||
viewer: Viewer, | viewer: Viewer, | ||||
mediaMessageContents: $ReadOnlyArray<MediaMessageServerDBContent>, | mediaMessageContents: $ReadOnlyArray<MediaMessageServerDBContent>, | ||||
): Promise<$ReadOnlyArray<Object>> { | ): Promise<$ReadOnlyArray<Object>> { | ||||
const uploadIDs = | const uploadIDs = | ||||
getUploadIDsFromMediaMessageServerDBContents(mediaMessageContents); | getUploadIDsFromMediaMessageServerDBContents(mediaMessageContents); | ||||
const query = SQL` | const query = SQL` | ||||
SELECT id AS uploadID, secret AS uploadSecret, | SELECT id AS uploadID, secret AS uploadSecret, | ||||
▲ Show 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | |||||
export { | export { | ||||
fetchUpload, | fetchUpload, | ||||
fetchUploadChunk, | fetchUploadChunk, | ||||
getUploadSize, | getUploadSize, | ||||
getUploadURL, | getUploadURL, | ||||
mediaFromRow, | mediaFromRow, | ||||
fetchMedia, | fetchMedia, | ||||
fetchMediaForThread, | |||||
fetchMediaFromMediaMessageContent, | fetchMediaFromMediaMessageContent, | ||||
constructMediaFromMediaMessageContentsAndUploadRows, | constructMediaFromMediaMessageContentsAndUploadRows, | ||||
}; | }; |