mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-06-23 01:09:26 +02:00
Add:Create media item shares with expiration #1768
This commit is contained in:
parent
e52b695f7e
commit
d6eae9b43e
12 changed files with 801 additions and 104 deletions
137
server/controllers/ShareController.js
Normal file
137
server/controllers/ShareController.js
Normal file
|
@ -0,0 +1,137 @@
|
|||
const Logger = require('../Logger')
|
||||
const Database = require('../Database')
|
||||
const { Op } = require('sequelize')
|
||||
|
||||
const ShareManager = require('../managers/ShareManager')
|
||||
|
||||
class ShareController {
|
||||
constructor() {}
|
||||
|
||||
/**
|
||||
* Public route
|
||||
* GET: /api/share/mediaitem/:slug
|
||||
* Get media item share by slug
|
||||
*
|
||||
* @param {import('express').Request} req
|
||||
* @param {import('express').Response} res
|
||||
*/
|
||||
async getMediaItemShareBySlug(req, res) {
|
||||
const { slug } = req.params
|
||||
|
||||
const mediaItemShare = ShareManager.findBySlug(slug)
|
||||
if (!mediaItemShare) {
|
||||
return res.status(404)
|
||||
}
|
||||
if (mediaItemShare.expiresAt && mediaItemShare.expiresAt.valueOf() < Date.now()) {
|
||||
ShareManager.removeMediaItemShare(mediaItemShare.id)
|
||||
return res.status(404).send('Media item share not found')
|
||||
}
|
||||
|
||||
try {
|
||||
const mediaItemModel = mediaItemShare.mediaItemType === 'book' ? Database.bookModel : Database.podcastEpisodeModel
|
||||
mediaItemShare.mediaItem = await mediaItemModel.findByPk(mediaItemShare.mediaItemId)
|
||||
|
||||
if (!mediaItemShare.mediaItem) {
|
||||
return res.status(404).send('Media item not found')
|
||||
}
|
||||
res.json(mediaItemShare)
|
||||
} catch (error) {
|
||||
Logger.error(`[ShareController] Failed`, error)
|
||||
res.status(500).send('Internal server error')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* POST: /api/share/mediaitem
|
||||
* Create a new media item share
|
||||
*
|
||||
* @param {import('express').Request} req
|
||||
* @param {import('express').Response} res
|
||||
*/
|
||||
async createMediaItemShare(req, res) {
|
||||
if (!req.user.isAdminOrUp) {
|
||||
Logger.error(`[ShareController] Non-admin user "${req.user.username}" attempted to create item share`)
|
||||
return res.sendStatus(403)
|
||||
}
|
||||
|
||||
const { slug, expiresAt, mediaItemType, mediaItemId } = req.body
|
||||
|
||||
if (!slug?.trim?.() || typeof mediaItemType !== 'string' || typeof mediaItemId !== 'string') {
|
||||
return res.status(400).send('Missing or invalid required fields')
|
||||
}
|
||||
if (expiresAt === null || isNaN(expiresAt) || expiresAt < 0) {
|
||||
return res.status(400).send('Invalid expiration date')
|
||||
}
|
||||
if (!['book', 'podcastEpisode'].includes(mediaItemType)) {
|
||||
return res.status(400).send('Invalid media item type')
|
||||
}
|
||||
|
||||
try {
|
||||
// Check if the media item share already exists by slug or mediaItemId
|
||||
const existingMediaItemShare = await Database.models.mediaItemShare.findOne({
|
||||
where: {
|
||||
[Op.or]: [{ slug }, { mediaItemId }]
|
||||
}
|
||||
})
|
||||
if (existingMediaItemShare) {
|
||||
if (existingMediaItemShare.mediaItemId === mediaItemId) {
|
||||
return res.status(409).send('Item is already shared')
|
||||
} else {
|
||||
return res.status(409).send('Slug is already in use')
|
||||
}
|
||||
}
|
||||
|
||||
// Check that media item exists
|
||||
const mediaItemModel = mediaItemType === 'book' ? Database.bookModel : Database.podcastEpisodeModel
|
||||
const mediaItem = await mediaItemModel.findByPk(mediaItemId)
|
||||
if (!mediaItem) {
|
||||
return res.status(404).send('Media item not found')
|
||||
}
|
||||
|
||||
const mediaItemShare = await Database.models.mediaItemShare.create({
|
||||
slug,
|
||||
expiresAt: expiresAt || null,
|
||||
mediaItemId,
|
||||
mediaItemType,
|
||||
userId: req.user.id
|
||||
})
|
||||
|
||||
ShareManager.openMediaItemShare(mediaItemShare)
|
||||
|
||||
res.status(201).json(mediaItemShare?.toJSONForClient())
|
||||
} catch (error) {
|
||||
Logger.error(`[ShareController] Failed`, error)
|
||||
res.status(500).send('Internal server error')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DELETE: /api/share/mediaitem/:id
|
||||
* Delete media item share
|
||||
*
|
||||
* @param {import('express').Request} req
|
||||
* @param {import('express').Response} res
|
||||
*/
|
||||
async deleteMediaItemShare(req, res) {
|
||||
if (!req.user.isAdminOrUp) {
|
||||
Logger.error(`[ShareController] Non-admin user "${req.user.username}" attempted to delete item share`)
|
||||
return res.sendStatus(403)
|
||||
}
|
||||
|
||||
try {
|
||||
const mediaItemShare = await Database.models.mediaItemShare.findByPk(req.params.id)
|
||||
if (!mediaItemShare) {
|
||||
return res.status(404).send('Media item share not found')
|
||||
}
|
||||
|
||||
ShareManager.removeMediaItemShare(mediaItemShare.id)
|
||||
|
||||
await mediaItemShare.destroy()
|
||||
res.sendStatus(204)
|
||||
} catch (error) {
|
||||
Logger.error(`[ShareController] Failed`, error)
|
||||
res.status(500).send('Internal server error')
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports = new ShareController()
|
Loading…
Add table
Add a link
Reference in a new issue