From 24b9417cec5cc785a57b2fe169a1ae88b88801a4 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 8 Oct 2018 15:51:38 +0200 Subject: Add users search filter --- server/controllers/api/users/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'server/controllers') diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts index 0b0081520..4f8137c03 100644 --- a/server/controllers/api/users/index.ts +++ b/server/controllers/api/users/index.ts @@ -238,7 +238,7 @@ async function autocompleteUsers (req: express.Request, res: express.Response, n } async function listUsers (req: express.Request, res: express.Response, next: express.NextFunction) { - const resultList = await UserModel.listForApi(req.query.start, req.query.count, req.query.sort) + const resultList = await UserModel.listForApi(req.query.start, req.query.count, req.query.sort, req.query.search) return res.json(getFormattedObjects(resultList.data, resultList.total)) } -- cgit v1.2.3 From b014b6b9c7cb68d09c52b44046afe486c0736426 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 10 Oct 2018 09:43:53 +0200 Subject: Add ability to search on followers/following --- server/controllers/api/server/follows.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/server/follows.ts b/server/controllers/api/server/follows.ts index d62400e42..9fa6c34ba 100644 --- a/server/controllers/api/server/follows.ts +++ b/server/controllers/api/server/follows.ts @@ -61,14 +61,26 @@ export { async function listFollowing (req: express.Request, res: express.Response, next: express.NextFunction) { const serverActor = await getServerActor() - const resultList = await ActorFollowModel.listFollowingForApi(serverActor.id, req.query.start, req.query.count, req.query.sort) + const resultList = await ActorFollowModel.listFollowingForApi( + serverActor.id, + req.query.start, + req.query.count, + req.query.sort, + req.query.search + ) return res.json(getFormattedObjects(resultList.data, resultList.total)) } async function listFollowers (req: express.Request, res: express.Response, next: express.NextFunction) { const serverActor = await getServerActor() - const resultList = await ActorFollowModel.listFollowersForApi(serverActor.id, req.query.start, req.query.count, req.query.sort) + const resultList = await ActorFollowModel.listFollowersForApi( + serverActor.id, + req.query.start, + req.query.count, + req.query.sort, + req.query.search + ) return res.json(getFormattedObjects(resultList.data, resultList.total)) } -- cgit v1.2.3 From 1cd3facc3de899ac864e979cd6d6a704b712cce3 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 10 Oct 2018 11:46:50 +0200 Subject: Add ability to list all local videos Including private/unlisted for moderators/admins --- server/controllers/api/accounts.ts | 4 +++- server/controllers/api/search.ts | 1 + server/controllers/api/video-channel.ts | 4 +++- server/controllers/feeds.ts | 10 +++++++++- 4 files changed, 16 insertions(+), 3 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/accounts.ts b/server/controllers/api/accounts.ts index b7691ccba..8e3f60010 100644 --- a/server/controllers/api/accounts.ts +++ b/server/controllers/api/accounts.ts @@ -86,9 +86,11 @@ async function listAccountVideos (req: express.Request, res: express.Response, n languageOneOf: req.query.languageOneOf, tagsOneOf: req.query.tagsOneOf, tagsAllOf: req.query.tagsAllOf, + filter: req.query.filter, nsfw: buildNSFWFilter(res, req.query.nsfw), withFiles: false, - accountId: account.id + accountId: account.id, + userId: res.locals.oauth ? res.locals.oauth.token.User.id : undefined }) return res.json(getFormattedObjects(resultList.data, resultList.total)) diff --git a/server/controllers/api/search.ts b/server/controllers/api/search.ts index 4be2b5ef7..a8a6cfb08 100644 --- a/server/controllers/api/search.ts +++ b/server/controllers/api/search.ts @@ -118,6 +118,7 @@ async function searchVideosDB (query: VideosSearchQuery, res: express.Response) const options = Object.assign(query, { includeLocalVideos: true, nsfw: buildNSFWFilter(res, query.nsfw), + filter: query.filter, userId: res.locals.oauth ? res.locals.oauth.token.User.id : undefined }) const resultList = await VideoModel.searchAndPopulateAccountAndServer(options) diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts index 1fa842d9c..c84d1be58 100644 --- a/server/controllers/api/video-channel.ts +++ b/server/controllers/api/video-channel.ts @@ -215,9 +215,11 @@ async function listVideoChannelVideos (req: express.Request, res: express.Respon languageOneOf: req.query.languageOneOf, tagsOneOf: req.query.tagsOneOf, tagsAllOf: req.query.tagsAllOf, + filter: req.query.filter, nsfw: buildNSFWFilter(res, req.query.nsfw), withFiles: false, - videoChannelId: videoChannelInstance.id + videoChannelId: videoChannelInstance.id, + userId: res.locals.oauth ? res.locals.oauth.token.User.id : undefined }) return res.json(getFormattedObjects(resultList.data, resultList.total)) diff --git a/server/controllers/feeds.ts b/server/controllers/feeds.ts index b30ad8e8d..ccb9b6029 100644 --- a/server/controllers/feeds.ts +++ b/server/controllers/feeds.ts @@ -1,7 +1,14 @@ import * as express from 'express' import { CONFIG, FEEDS, ROUTE_CACHE_LIFETIME } from '../initializers/constants' import { THUMBNAILS_SIZE } from '../initializers' -import { asyncMiddleware, setDefaultSort, videoCommentsFeedsValidator, videoFeedsValidator, videosSortValidator } from '../middlewares' +import { + asyncMiddleware, + commonVideosFiltersValidator, + setDefaultSort, + videoCommentsFeedsValidator, + videoFeedsValidator, + videosSortValidator +} from '../middlewares' import { VideoModel } from '../models/video/video' import * as Feed from 'pfeed' import { AccountModel } from '../models/account/account' @@ -22,6 +29,7 @@ feedsRouter.get('/feeds/videos.:format', videosSortValidator, setDefaultSort, asyncMiddleware(cacheRoute(ROUTE_CACHE_LIFETIME.FEEDS)), + commonVideosFiltersValidator, asyncMiddleware(videoFeedsValidator), asyncMiddleware(generateVideoFeed) ) -- cgit v1.2.3 From 64cc5e8575fda47b281ae20abf0020e27fc8ce7c Mon Sep 17 00:00:00 2001 From: Rigel Kent Date: Fri, 5 Oct 2018 15:17:34 +0200 Subject: add webtorrent opt-out settings - add a key in localstorage to remember the opt-out - add a user setting --- server/controllers/api/users/me.ts | 1 + 1 file changed, 1 insertion(+) (limited to 'server/controllers') diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts index 591ec6b25..f78294f17 100644 --- a/server/controllers/api/users/me.ts +++ b/server/controllers/api/users/me.ts @@ -327,6 +327,7 @@ async function updateMe (req: express.Request, res: express.Response, next: expr if (body.password !== undefined) user.password = body.password if (body.email !== undefined) user.email = body.email if (body.nsfwPolicy !== undefined) user.nsfwPolicy = body.nsfwPolicy + if (body.webTorrentPolicy !== undefined) user.webTorrentPolicy = body.webTorrentPolicy if (body.autoPlayVideo !== undefined) user.autoPlayVideo = body.autoPlayVideo await sequelizeTypescript.transaction(async t => { -- cgit v1.2.3 From ed638e5325096ef580da20f370ac61c59cd48cf7 Mon Sep 17 00:00:00 2001 From: Rigel Kent Date: Fri, 12 Oct 2018 18:12:39 +0200 Subject: move to boolean switch --- server/controllers/api/users/me.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'server/controllers') diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts index f78294f17..3c511dc70 100644 --- a/server/controllers/api/users/me.ts +++ b/server/controllers/api/users/me.ts @@ -327,7 +327,7 @@ async function updateMe (req: express.Request, res: express.Response, next: expr if (body.password !== undefined) user.password = body.password if (body.email !== undefined) user.email = body.email if (body.nsfwPolicy !== undefined) user.nsfwPolicy = body.nsfwPolicy - if (body.webTorrentPolicy !== undefined) user.webTorrentPolicy = body.webTorrentPolicy + if (body.webTorrentEnabled !== undefined) user.webTorrentEnabled = body.webTorrentEnabled if (body.autoPlayVideo !== undefined) user.autoPlayVideo = body.autoPlayVideo await sequelizeTypescript.transaction(async t => { -- cgit v1.2.3 From 7ad9b9846c44d198a736183fb186c2039f5236b5 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 12 Oct 2018 15:26:04 +0200 Subject: Add ability for users to block an account/instance on server side --- server/controllers/api/accounts.ts | 5 +- server/controllers/api/search.ts | 2 +- server/controllers/api/users/index.ts | 2 + server/controllers/api/users/me.ts | 3 +- server/controllers/api/users/my-blocklist.ts | 125 +++++++++++++++++++++++++++ server/controllers/api/video-channel.ts | 2 +- server/controllers/api/videos/comment.ts | 12 ++- server/controllers/api/videos/index.ts | 2 +- 8 files changed, 144 insertions(+), 9 deletions(-) create mode 100644 server/controllers/api/users/my-blocklist.ts (limited to 'server/controllers') diff --git a/server/controllers/api/accounts.ts b/server/controllers/api/accounts.ts index 8e3f60010..86ef2aed1 100644 --- a/server/controllers/api/accounts.ts +++ b/server/controllers/api/accounts.ts @@ -1,7 +1,8 @@ import * as express from 'express' import { getFormattedObjects } from '../../helpers/utils' import { - asyncMiddleware, commonVideosFiltersValidator, + asyncMiddleware, + commonVideosFiltersValidator, listVideoAccountChannelsValidator, optionalAuthenticate, paginationValidator, @@ -90,7 +91,7 @@ async function listAccountVideos (req: express.Request, res: express.Response, n nsfw: buildNSFWFilter(res, req.query.nsfw), withFiles: false, accountId: account.id, - userId: res.locals.oauth ? res.locals.oauth.token.User.id : undefined + user: res.locals.oauth ? res.locals.oauth.token.User : undefined }) return res.json(getFormattedObjects(resultList.data, resultList.total)) diff --git a/server/controllers/api/search.ts b/server/controllers/api/search.ts index a8a6cfb08..534305ba6 100644 --- a/server/controllers/api/search.ts +++ b/server/controllers/api/search.ts @@ -119,7 +119,7 @@ async function searchVideosDB (query: VideosSearchQuery, res: express.Response) includeLocalVideos: true, nsfw: buildNSFWFilter(res, query.nsfw), filter: query.filter, - userId: res.locals.oauth ? res.locals.oauth.token.User.id : undefined + user: res.locals.oauth ? res.locals.oauth.token.User : undefined }) const resultList = await VideoModel.searchAndPopulateAccountAndServer(options) diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts index 4f8137c03..9fcb8077f 100644 --- a/server/controllers/api/users/index.ts +++ b/server/controllers/api/users/index.ts @@ -37,6 +37,7 @@ import { UserModel } from '../../../models/account/user' import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../helpers/audit-logger' import { meRouter } from './me' import { deleteUserToken } from '../../../lib/oauth-model' +import { myBlocklistRouter } from './my-blocklist' const auditLogger = auditLoggerFactory('users') @@ -53,6 +54,7 @@ const askSendEmailLimiter = new RateLimit({ }) const usersRouter = express.Router() +usersRouter.use('/', myBlocklistRouter) usersRouter.use('/', meRouter) usersRouter.get('/autocomplete', diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts index 591ec6b25..ebe668110 100644 --- a/server/controllers/api/users/me.ts +++ b/server/controllers/api/users/me.ts @@ -238,7 +238,8 @@ async function getUserSubscriptionVideos (req: express.Request, res: express.Res nsfw: buildNSFWFilter(res, req.query.nsfw), filter: req.query.filter as VideoFilter, withFiles: false, - actorId: user.Account.Actor.id + actorId: user.Account.Actor.id, + user }) return res.json(getFormattedObjects(resultList.data, resultList.total)) diff --git a/server/controllers/api/users/my-blocklist.ts b/server/controllers/api/users/my-blocklist.ts new file mode 100644 index 000000000..e955ffde9 --- /dev/null +++ b/server/controllers/api/users/my-blocklist.ts @@ -0,0 +1,125 @@ +import * as express from 'express' +import 'multer' +import { getFormattedObjects } from '../../../helpers/utils' +import { + asyncMiddleware, + asyncRetryTransactionMiddleware, + authenticate, + paginationValidator, + serverGetValidator, + setDefaultPagination, + setDefaultSort, + unblockAccountByAccountValidator +} from '../../../middlewares' +import { + accountsBlocklistSortValidator, + blockAccountByAccountValidator, + serversBlocklistSortValidator, + unblockServerByAccountValidator +} from '../../../middlewares/validators' +import { UserModel } from '../../../models/account/user' +import { AccountModel } from '../../../models/account/account' +import { AccountBlocklistModel } from '../../../models/account/account-blocklist' +import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../../../lib/blocklist' +import { ServerBlocklistModel } from '../../../models/server/server-blocklist' +import { ServerModel } from '../../../models/server/server' + +const myBlocklistRouter = express.Router() + +myBlocklistRouter.get('/me/blocklist/accounts', + authenticate, + paginationValidator, + accountsBlocklistSortValidator, + setDefaultSort, + setDefaultPagination, + asyncMiddleware(listBlockedAccounts) +) + +myBlocklistRouter.post('/me/blocklist/accounts', + authenticate, + asyncMiddleware(blockAccountByAccountValidator), + asyncRetryTransactionMiddleware(blockAccount) +) + +myBlocklistRouter.delete('/me/blocklist/accounts/:accountName', + authenticate, + asyncMiddleware(unblockAccountByAccountValidator), + asyncRetryTransactionMiddleware(unblockAccount) +) + +myBlocklistRouter.get('/me/blocklist/servers', + authenticate, + paginationValidator, + serversBlocklistSortValidator, + setDefaultSort, + setDefaultPagination, + asyncMiddleware(listBlockedServers) +) + +myBlocklistRouter.post('/me/blocklist/servers', + authenticate, + asyncMiddleware(serverGetValidator), + asyncRetryTransactionMiddleware(blockServer) +) + +myBlocklistRouter.delete('/me/blocklist/servers/:host', + authenticate, + asyncMiddleware(unblockServerByAccountValidator), + asyncRetryTransactionMiddleware(unblockServer) +) + +export { + myBlocklistRouter +} + +// --------------------------------------------------------------------------- + +async function listBlockedAccounts (req: express.Request, res: express.Response) { + const user: UserModel = res.locals.oauth.token.User + + const resultList = await AccountBlocklistModel.listForApi(user.Account.id, req.query.start, req.query.count, req.query.sort) + + return res.json(getFormattedObjects(resultList.data, resultList.total)) +} + +async function blockAccount (req: express.Request, res: express.Response) { + const user: UserModel = res.locals.oauth.token.User + const accountToBlock: AccountModel = res.locals.account + + await addAccountInBlocklist(user.Account.id, accountToBlock.id) + + return res.status(204).end() +} + +async function unblockAccount (req: express.Request, res: express.Response) { + const accountBlock: AccountBlocklistModel = res.locals.accountBlock + + await removeAccountFromBlocklist(accountBlock) + + return res.status(204).end() +} + +async function listBlockedServers (req: express.Request, res: express.Response) { + const user: UserModel = res.locals.oauth.token.User + + const resultList = await ServerBlocklistModel.listForApi(user.Account.id, req.query.start, req.query.count, req.query.sort) + + return res.json(getFormattedObjects(resultList.data, resultList.total)) +} + +async function blockServer (req: express.Request, res: express.Response) { + const user: UserModel = res.locals.oauth.token.User + const serverToBlock: ServerModel = res.locals.server + + await addServerInBlocklist(user.Account.id, serverToBlock.id) + + return res.status(204).end() +} + +async function unblockServer (req: express.Request, res: express.Response) { + const serverBlock: ServerBlocklistModel = res.locals.serverBlock + + await removeServerFromBlocklist(serverBlock) + + return res.status(204).end() +} diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts index c84d1be58..9bf3c5fd8 100644 --- a/server/controllers/api/video-channel.ts +++ b/server/controllers/api/video-channel.ts @@ -219,7 +219,7 @@ async function listVideoChannelVideos (req: express.Request, res: express.Respon nsfw: buildNSFWFilter(res, req.query.nsfw), withFiles: false, videoChannelId: videoChannelInstance.id, - userId: res.locals.oauth ? res.locals.oauth.token.User.id : undefined + user: res.locals.oauth ? res.locals.oauth.token.User : undefined }) return res.json(getFormattedObjects(resultList.data, resultList.total)) diff --git a/server/controllers/api/videos/comment.ts b/server/controllers/api/videos/comment.ts index 4f2b4faee..3875c8f79 100644 --- a/server/controllers/api/videos/comment.ts +++ b/server/controllers/api/videos/comment.ts @@ -8,7 +8,7 @@ import { buildFormattedCommentTree, createVideoComment } from '../../../lib/vide import { asyncMiddleware, asyncRetryTransactionMiddleware, - authenticate, + authenticate, optionalAuthenticate, paginationValidator, setDefaultPagination, setDefaultSort @@ -36,10 +36,12 @@ videoCommentRouter.get('/:videoId/comment-threads', setDefaultSort, setDefaultPagination, asyncMiddleware(listVideoCommentThreadsValidator), + optionalAuthenticate, asyncMiddleware(listVideoThreads) ) videoCommentRouter.get('/:videoId/comment-threads/:threadId', asyncMiddleware(listVideoThreadCommentsValidator), + optionalAuthenticate, asyncMiddleware(listVideoThreadComments) ) @@ -69,10 +71,12 @@ export { async function listVideoThreads (req: express.Request, res: express.Response, next: express.NextFunction) { const video = res.locals.video as VideoModel + const user: UserModel = res.locals.oauth ? res.locals.oauth.token.User : undefined + let resultList: ResultList if (video.commentsEnabled === true) { - resultList = await VideoCommentModel.listThreadsForApi(video.id, req.query.start, req.query.count, req.query.sort) + resultList = await VideoCommentModel.listThreadsForApi(video.id, req.query.start, req.query.count, req.query.sort, user) } else { resultList = { total: 0, @@ -85,10 +89,12 @@ async function listVideoThreads (req: express.Request, res: express.Response, ne async function listVideoThreadComments (req: express.Request, res: express.Response, next: express.NextFunction) { const video = res.locals.video as VideoModel + const user: UserModel = res.locals.oauth ? res.locals.oauth.token.User : undefined + let resultList: ResultList if (video.commentsEnabled === true) { - resultList = await VideoCommentModel.listThreadCommentsForApi(video.id, res.locals.videoCommentThread.id) + resultList = await VideoCommentModel.listThreadCommentsForApi(video.id, res.locals.videoCommentThread.id, user) } else { resultList = { total: 0, diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 6a73e13d0..664154406 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts @@ -437,7 +437,7 @@ async function listVideos (req: express.Request, res: express.Response, next: ex nsfw: buildNSFWFilter(res, req.query.nsfw), filter: req.query.filter as VideoFilter, withFiles: false, - userId: res.locals.oauth ? res.locals.oauth.token.User.id : undefined + user: res.locals.oauth ? res.locals.oauth.token.User : undefined }) return res.json(getFormattedObjects(resultList.data, resultList.total)) -- cgit v1.2.3 From af5767ffae41b2d5604e41ba9a7225c623dd6735 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 12 Oct 2018 17:26:40 +0200 Subject: Add user/instance block by users in the client --- server/controllers/api/users/my-blocklist.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/users/my-blocklist.ts b/server/controllers/api/users/my-blocklist.ts index e955ffde9..95a4105ec 100644 --- a/server/controllers/api/users/my-blocklist.ts +++ b/server/controllers/api/users/my-blocklist.ts @@ -6,7 +6,6 @@ import { asyncRetryTransactionMiddleware, authenticate, paginationValidator, - serverGetValidator, setDefaultPagination, setDefaultSort, unblockAccountByAccountValidator @@ -14,6 +13,7 @@ import { import { accountsBlocklistSortValidator, blockAccountByAccountValidator, + blockServerByAccountValidator, serversBlocklistSortValidator, unblockServerByAccountValidator } from '../../../middlewares/validators' @@ -58,7 +58,7 @@ myBlocklistRouter.get('/me/blocklist/servers', myBlocklistRouter.post('/me/blocklist/servers', authenticate, - asyncMiddleware(serverGetValidator), + asyncMiddleware(blockServerByAccountValidator), asyncRetryTransactionMiddleware(blockServer) ) -- cgit v1.2.3 From b44164bb567fe7c9f65f1ac2908d44990a8ccc8e Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 15 Oct 2018 13:03:04 +0200 Subject: Add ability to mute a user/instance by server in server api --- server/controllers/api/server/index.ts | 2 + server/controllers/api/server/server-blocklist.ts | 132 ++++++++++++++++++++++ server/controllers/api/users/my-blocklist.ts | 8 +- 3 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 server/controllers/api/server/server-blocklist.ts (limited to 'server/controllers') diff --git a/server/controllers/api/server/index.ts b/server/controllers/api/server/index.ts index 43bca2c10..c08192a8c 100644 --- a/server/controllers/api/server/index.ts +++ b/server/controllers/api/server/index.ts @@ -2,12 +2,14 @@ import * as express from 'express' import { serverFollowsRouter } from './follows' import { statsRouter } from './stats' import { serverRedundancyRouter } from './redundancy' +import { serverBlocklistRouter } from './server-blocklist' const serverRouter = express.Router() serverRouter.use('/', serverFollowsRouter) serverRouter.use('/', serverRedundancyRouter) serverRouter.use('/', statsRouter) +serverRouter.use('/', serverBlocklistRouter) // --------------------------------------------------------------------------- diff --git a/server/controllers/api/server/server-blocklist.ts b/server/controllers/api/server/server-blocklist.ts new file mode 100644 index 000000000..3cb3a96e2 --- /dev/null +++ b/server/controllers/api/server/server-blocklist.ts @@ -0,0 +1,132 @@ +import * as express from 'express' +import 'multer' +import { getFormattedObjects, getServerActor } from '../../../helpers/utils' +import { + asyncMiddleware, + asyncRetryTransactionMiddleware, + authenticate, + ensureUserHasRight, + paginationValidator, + setDefaultPagination, + setDefaultSort +} from '../../../middlewares' +import { + accountsBlocklistSortValidator, + blockAccountValidator, + blockServerValidator, + serversBlocklistSortValidator, + unblockAccountByServerValidator, + unblockServerByServerValidator +} from '../../../middlewares/validators' +import { AccountModel } from '../../../models/account/account' +import { AccountBlocklistModel } from '../../../models/account/account-blocklist' +import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../../../lib/blocklist' +import { ServerBlocklistModel } from '../../../models/server/server-blocklist' +import { ServerModel } from '../../../models/server/server' +import { UserRight } from '../../../../shared/models/users' + +const serverBlocklistRouter = express.Router() + +serverBlocklistRouter.get('/blocklist/accounts', + authenticate, + ensureUserHasRight(UserRight.MANAGE_ACCOUNTS_BLOCKLIST), + paginationValidator, + accountsBlocklistSortValidator, + setDefaultSort, + setDefaultPagination, + asyncMiddleware(listBlockedAccounts) +) + +serverBlocklistRouter.post('/blocklist/accounts', + authenticate, + ensureUserHasRight(UserRight.MANAGE_ACCOUNTS_BLOCKLIST), + asyncMiddleware(blockAccountValidator), + asyncRetryTransactionMiddleware(blockAccount) +) + +serverBlocklistRouter.delete('/blocklist/accounts/:accountName', + authenticate, + ensureUserHasRight(UserRight.MANAGE_ACCOUNTS_BLOCKLIST), + asyncMiddleware(unblockAccountByServerValidator), + asyncRetryTransactionMiddleware(unblockAccount) +) + +serverBlocklistRouter.get('/blocklist/servers', + authenticate, + ensureUserHasRight(UserRight.MANAGE_SERVERS_BLOCKLIST), + paginationValidator, + serversBlocklistSortValidator, + setDefaultSort, + setDefaultPagination, + asyncMiddleware(listBlockedServers) +) + +serverBlocklistRouter.post('/blocklist/servers', + authenticate, + ensureUserHasRight(UserRight.MANAGE_SERVERS_BLOCKLIST), + asyncMiddleware(blockServerValidator), + asyncRetryTransactionMiddleware(blockServer) +) + +serverBlocklistRouter.delete('/blocklist/servers/:host', + authenticate, + ensureUserHasRight(UserRight.MANAGE_SERVERS_BLOCKLIST), + asyncMiddleware(unblockServerByServerValidator), + asyncRetryTransactionMiddleware(unblockServer) +) + +export { + serverBlocklistRouter +} + +// --------------------------------------------------------------------------- + +async function listBlockedAccounts (req: express.Request, res: express.Response) { + const serverActor = await getServerActor() + + const resultList = await AccountBlocklistModel.listForApi(serverActor.Account.id, req.query.start, req.query.count, req.query.sort) + + return res.json(getFormattedObjects(resultList.data, resultList.total)) +} + +async function blockAccount (req: express.Request, res: express.Response) { + const serverActor = await getServerActor() + const accountToBlock: AccountModel = res.locals.account + + await addAccountInBlocklist(serverActor.Account.id, accountToBlock.id) + + return res.status(204).end() +} + +async function unblockAccount (req: express.Request, res: express.Response) { + const accountBlock: AccountBlocklistModel = res.locals.accountBlock + + await removeAccountFromBlocklist(accountBlock) + + return res.status(204).end() +} + +async function listBlockedServers (req: express.Request, res: express.Response) { + const serverActor = await getServerActor() + + const resultList = await ServerBlocklistModel.listForApi(serverActor.Account.id, req.query.start, req.query.count, req.query.sort) + + return res.json(getFormattedObjects(resultList.data, resultList.total)) +} + +async function blockServer (req: express.Request, res: express.Response) { + const serverActor = await getServerActor() + const serverToBlock: ServerModel = res.locals.server + + await addServerInBlocklist(serverActor.Account.id, serverToBlock.id) + + return res.status(204).end() +} + +async function unblockServer (req: express.Request, res: express.Response) { + const serverBlock: ServerBlocklistModel = res.locals.serverBlock + + await removeServerFromBlocklist(serverBlock) + + return res.status(204).end() +} diff --git a/server/controllers/api/users/my-blocklist.ts b/server/controllers/api/users/my-blocklist.ts index 95a4105ec..9575eab46 100644 --- a/server/controllers/api/users/my-blocklist.ts +++ b/server/controllers/api/users/my-blocklist.ts @@ -12,8 +12,8 @@ import { } from '../../../middlewares' import { accountsBlocklistSortValidator, - blockAccountByAccountValidator, - blockServerByAccountValidator, + blockAccountValidator, + blockServerValidator, serversBlocklistSortValidator, unblockServerByAccountValidator } from '../../../middlewares/validators' @@ -37,7 +37,7 @@ myBlocklistRouter.get('/me/blocklist/accounts', myBlocklistRouter.post('/me/blocklist/accounts', authenticate, - asyncMiddleware(blockAccountByAccountValidator), + asyncMiddleware(blockAccountValidator), asyncRetryTransactionMiddleware(blockAccount) ) @@ -58,7 +58,7 @@ myBlocklistRouter.get('/me/blocklist/servers', myBlocklistRouter.post('/me/blocklist/servers', authenticate, - asyncMiddleware(blockServerByAccountValidator), + asyncMiddleware(blockServerValidator), asyncRetryTransactionMiddleware(blockServer) ) -- cgit v1.2.3 From 5c6d985faeef1d6793d3f44ca6374f1a9b722806 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 14 Nov 2018 15:01:28 +0100 Subject: Check activities host --- server/controllers/activitypub/client.ts | 36 ++++++++++++++++++++++++++++---- server/controllers/activitypub/inbox.ts | 6 ++++-- server/controllers/api/videos/rate.ts | 17 ++++++++------- 3 files changed, 46 insertions(+), 13 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts index 433186179..ffbf1ba19 100644 --- a/server/controllers/activitypub/client.ts +++ b/server/controllers/activitypub/client.ts @@ -3,17 +3,22 @@ import * as express from 'express' import { VideoPrivacy, VideoRateType } from '../../../shared/models/videos' import { activityPubCollectionPagination, activityPubContextify } from '../../helpers/activitypub' import { CONFIG, ROUTE_CACHE_LIFETIME } from '../../initializers' -import { buildAnnounceWithVideoAudience } from '../../lib/activitypub/send' +import { buildAnnounceWithVideoAudience, buildDislikeActivity, buildLikeActivity } from '../../lib/activitypub/send' import { audiencify, getAudience } from '../../lib/activitypub/audience' import { buildCreateActivity } from '../../lib/activitypub/send/send-create' import { asyncMiddleware, + videosShareValidator, executeIfActivityPub, localAccountValidator, localVideoChannelValidator, videosCustomGetValidator } from '../../middlewares' -import { videoCommentGetValidator, videosGetValidator, videosShareValidator } from '../../middlewares/validators' +import { + getAccountVideoRateValidator, + videoCommentGetValidator, + videosGetValidator +} from '../../middlewares/validators' import { AccountModel } from '../../models/account/account' import { ActorModel } from '../../models/activitypub/actor' import { ActorFollowModel } from '../../models/activitypub/actor-follow' @@ -25,6 +30,7 @@ import { cacheRoute } from '../../middlewares/cache' import { activityPubResponse } from './utils' import { AccountVideoRateModel } from '../../models/account/account-video-rate' import { + getRateUrl, getVideoCommentsActivityPubUrl, getVideoDislikesActivityPubUrl, getVideoLikesActivityPubUrl, @@ -48,6 +54,14 @@ activityPubClientRouter.get('/accounts?/:name/following', executeIfActivityPub(asyncMiddleware(localAccountValidator)), executeIfActivityPub(asyncMiddleware(accountFollowingController)) ) +activityPubClientRouter.get('/accounts?/:name/likes/:videoId', + executeIfActivityPub(asyncMiddleware(getAccountVideoRateValidator('like'))), + executeIfActivityPub(getAccountVideoRate('like')) +) +activityPubClientRouter.get('/accounts?/:name/dislikes/:videoId', + executeIfActivityPub(asyncMiddleware(getAccountVideoRateValidator('dislike'))), + executeIfActivityPub(getAccountVideoRate('dislike')) +) activityPubClientRouter.get('/videos/watch/:id', executeIfActivityPub(asyncMiddleware(cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS))), @@ -62,7 +76,7 @@ activityPubClientRouter.get('/videos/watch/:id/announces', executeIfActivityPub(asyncMiddleware(videosCustomGetValidator('only-video'))), executeIfActivityPub(asyncMiddleware(videoAnnouncesController)) ) -activityPubClientRouter.get('/videos/watch/:id/announces/:accountId', +activityPubClientRouter.get('/videos/watch/:id/announces/:actorId', executeIfActivityPub(asyncMiddleware(videosShareValidator)), executeIfActivityPub(asyncMiddleware(videoAnnounceController)) ) @@ -133,6 +147,20 @@ async function accountFollowingController (req: express.Request, res: express.Re return activityPubResponse(activityPubContextify(activityPubResult), res) } +function getAccountVideoRate (rateType: VideoRateType) { + return (req: express.Request, res: express.Response) => { + const accountVideoRate: AccountVideoRateModel = res.locals.accountVideoRate + + const byActor = accountVideoRate.Account.Actor + const url = getRateUrl(rateType, byActor, accountVideoRate.Video) + const APObject = rateType === 'like' + ? buildLikeActivity(url, byActor, accountVideoRate.Video) + : buildCreateActivity(url, byActor, buildDislikeActivity(url, byActor, accountVideoRate.Video)) + + return activityPubResponse(activityPubContextify(APObject), res) + } +} + async function videoController (req: express.Request, res: express.Response, next: express.NextFunction) { const video: VideoModel = res.locals.video @@ -276,7 +304,7 @@ function videoRates (req: express.Request, rateType: VideoRateType, video: Video const result = await AccountVideoRateModel.listAndCountAccountUrlsByVideoId(rateType, video.id, start, count) return { total: result.count, - data: result.rows.map(r => r.Account.Actor.url) + data: result.rows.map(r => r.url) } } return activityPubCollectionPagination(url, handler, req.query.page) diff --git a/server/controllers/activitypub/inbox.ts b/server/controllers/activitypub/inbox.ts index 738d155eb..f0e65015b 100644 --- a/server/controllers/activitypub/inbox.ts +++ b/server/controllers/activitypub/inbox.ts @@ -43,11 +43,13 @@ export { // --------------------------------------------------------------------------- const inboxQueue = queue<{ activities: Activity[], signatureActor?: ActorModel, inboxActor?: ActorModel }, Error>((task, cb) => { - processActivities(task.activities, task.signatureActor, task.inboxActor) + const options = { signatureActor: task.signatureActor, inboxActor: task.inboxActor } + + processActivities(task.activities, options) .then(() => cb()) }) -function inboxController (req: express.Request, res: express.Response, next: express.NextFunction) { +function inboxController (req: express.Request, res: express.Response) { const rootActivity: RootActivity = req.body let activities: Activity[] = [] diff --git a/server/controllers/api/videos/rate.ts b/server/controllers/api/videos/rate.ts index dc322bb0c..53952a0a2 100644 --- a/server/controllers/api/videos/rate.ts +++ b/server/controllers/api/videos/rate.ts @@ -2,8 +2,8 @@ import * as express from 'express' import { UserVideoRateUpdate } from '../../../../shared' import { logger } from '../../../helpers/logger' import { sequelizeTypescript, VIDEO_RATE_TYPES } from '../../../initializers' -import { sendVideoRateChange } from '../../../lib/activitypub' -import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoRateValidator } from '../../../middlewares' +import { getRateUrl, sendVideoRateChange } from '../../../lib/activitypub' +import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoUpdateRateValidator } from '../../../middlewares' import { AccountModel } from '../../../models/account/account' import { AccountVideoRateModel } from '../../../models/account/account-video-rate' import { VideoModel } from '../../../models/video/video' @@ -12,7 +12,7 @@ const rateVideoRouter = express.Router() rateVideoRouter.put('/:id/rate', authenticate, - asyncMiddleware(videoRateValidator), + asyncMiddleware(videoUpdateRateValidator), asyncRetryTransactionMiddleware(rateVideo) ) @@ -28,11 +28,12 @@ async function rateVideo (req: express.Request, res: express.Response) { const body: UserVideoRateUpdate = req.body const rateType = body.rating const videoInstance: VideoModel = res.locals.video + const userAccount: AccountModel = res.locals.oauth.token.User.Account await sequelizeTypescript.transaction(async t => { const sequelizeOptions = { transaction: t } - const accountInstance = await AccountModel.load(res.locals.oauth.token.User.Account.id, t) + const accountInstance = await AccountModel.load(userAccount.id, t) const previousRate = await AccountVideoRateModel.load(accountInstance.id, videoInstance.id, t) let likesToIncrement = 0 @@ -44,20 +45,22 @@ async function rateVideo (req: express.Request, res: express.Response) { // There was a previous rate, update it if (previousRate) { // We will remove the previous rate, so we will need to update the video count attribute - if (previousRate.type === VIDEO_RATE_TYPES.LIKE) likesToIncrement-- - else if (previousRate.type === VIDEO_RATE_TYPES.DISLIKE) dislikesToIncrement-- + if (previousRate.type === 'like') likesToIncrement-- + else if (previousRate.type === 'dislike') dislikesToIncrement-- if (rateType === 'none') { // Destroy previous rate await previousRate.destroy(sequelizeOptions) } else { // Update previous rate previousRate.type = rateType + previousRate.url = getRateUrl(rateType, userAccount.Actor, videoInstance) await previousRate.save(sequelizeOptions) } } else if (rateType !== 'none') { // There was not a previous rate, insert a new one if there is a rate const query = { accountId: accountInstance.id, videoId: videoInstance.id, - type: rateType + type: rateType, + url: getRateUrl(rateType, userAccount.Actor, videoInstance) } await AccountVideoRateModel.create(query, sequelizeOptions) -- cgit v1.2.3 From 030177d246834fdba89be9bbaeac497589b47102 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 15 Nov 2018 16:18:12 +0100 Subject: Don't forward view, send updates instead To avoid inconsistencies in the federation, now the origin server will tell other instances what is the correct number of views --- server/controllers/api/videos/index.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'server/controllers') diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 664154406..e654bdd09 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts @@ -405,7 +405,11 @@ async function viewVideo (req: express.Request, res: express.Response) { const serverActor = await getServerActor() - await sendCreateView(serverActor, videoInstance, undefined) + // Send the event to the origin server + // If we own the video, we'll send an update event when we'll process the views (in our job queue) + if (videoInstance.isOwned() === false) { + await sendCreateView(serverActor, videoInstance, undefined) + } return res.status(204).end() } -- cgit v1.2.3 From 8d1fa36ad22a21a9b0fb6bf51a27d09954220013 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 16 Nov 2018 11:18:13 +0100 Subject: Do not host remote AP objects --- server/controllers/activitypub/client.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'server/controllers') diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts index ffbf1ba19..a342a48d4 100644 --- a/server/controllers/activitypub/client.ts +++ b/server/controllers/activitypub/client.ts @@ -39,6 +39,7 @@ import { import { VideoCaptionModel } from '../../models/video/video-caption' import { videoRedundancyGetValidator } from '../../middlewares/validators/redundancy' import { getServerActor } from '../../helpers/utils' +import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' const activityPubClientRouter = express.Router() @@ -164,6 +165,8 @@ function getAccountVideoRate (rateType: VideoRateType) { async function videoController (req: express.Request, res: express.Response, next: express.NextFunction) { const video: VideoModel = res.locals.video + if (video.isOwned() === false) return res.redirect(video.url) + // We need captions to render AP object video.VideoCaptions = await VideoCaptionModel.listVideoCaptions(video.id) @@ -180,6 +183,9 @@ async function videoController (req: express.Request, res: express.Response, nex async function videoAnnounceController (req: express.Request, res: express.Response, next: express.NextFunction) { const share = res.locals.videoShare as VideoShareModel + + if (share.Actor.isOwned() === false) return res.redirect(share.url) + const { activity } = await buildAnnounceWithVideoAudience(share.Actor, share, res.locals.video, undefined) return activityPubResponse(activityPubContextify(activity), res) @@ -252,6 +258,8 @@ async function videoChannelFollowingController (req: express.Request, res: expre async function videoCommentController (req: express.Request, res: express.Response, next: express.NextFunction) { const videoComment: VideoCommentModel = res.locals.videoComment + if (videoComment.isOwned() === false) return res.redirect(videoComment.url) + const threadParentComments = await VideoCommentModel.listThreadParentComments(videoComment, undefined) const isPublic = true // Comments are always public const audience = getAudience(videoComment.Account.Actor, isPublic) @@ -267,7 +275,9 @@ async function videoCommentController (req: express.Request, res: express.Respon } async function videoRedundancyController (req: express.Request, res: express.Response) { - const videoRedundancy = res.locals.videoRedundancy + const videoRedundancy: VideoRedundancyModel = res.locals.videoRedundancy + if (videoRedundancy.isOwned() === false) return res.redirect(videoRedundancy.url) + const serverActor = await getServerActor() const audience = getAudience(serverActor) -- cgit v1.2.3 From 8d4273463fb19d503b1aa0a32dc289f292ed614e Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 16 Nov 2018 15:02:48 +0100 Subject: Check follow constraints when getting a video --- server/controllers/api/videos/index.ts | 2 ++ 1 file changed, 2 insertions(+) (limited to 'server/controllers') diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index e654bdd09..89fd0432f 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts @@ -31,6 +31,7 @@ import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, + checkVideoFollowConstraints, commonVideosFiltersValidator, optionalAuthenticate, paginationValidator, @@ -123,6 +124,7 @@ videosRouter.get('/:id/description', videosRouter.get('/:id', optionalAuthenticate, asyncMiddleware(videosGetValidator), + asyncMiddleware(checkVideoFollowConstraints), getVideo ) videosRouter.post('/:id/views', -- cgit v1.2.3 From babecc3c09cd4ed06fe643a97fff4bcc31c5a9be Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 16 Nov 2018 15:38:09 +0100 Subject: Fix AP collections pagination --- server/controllers/activitypub/client.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts index a342a48d4..d9d385460 100644 --- a/server/controllers/activitypub/client.ts +++ b/server/controllers/activitypub/client.ts @@ -298,7 +298,7 @@ async function actorFollowing (req: express.Request, actor: ActorModel) { return ActorFollowModel.listAcceptedFollowingUrlsForApi([ actor.id ], undefined, start, count) } - return activityPubCollectionPagination(CONFIG.WEBSERVER.URL + req.url, handler, req.query.page) + return activityPubCollectionPagination(CONFIG.WEBSERVER.URL + req.path, handler, req.query.page) } async function actorFollowers (req: express.Request, actor: ActorModel) { @@ -306,7 +306,7 @@ async function actorFollowers (req: express.Request, actor: ActorModel) { return ActorFollowModel.listAcceptedFollowerUrlsForApi([ actor.id ], undefined, start, count) } - return activityPubCollectionPagination(CONFIG.WEBSERVER.URL + req.url, handler, req.query.page) + return activityPubCollectionPagination(CONFIG.WEBSERVER.URL + req.path, handler, req.query.page) } function videoRates (req: express.Request, rateType: VideoRateType, video: VideoModel, url: string) { -- cgit v1.2.3 From 04b8c3fba614efc3827f583096c78b08cb668470 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 20 Nov 2018 10:05:51 +0100 Subject: Delete invalid or deleted remote videos --- server/controllers/api/videos/index.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'server/controllers') diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 89fd0432f..b659f53ed 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts @@ -387,6 +387,11 @@ async function updateVideo (req: express.Request, res: express.Response) { function getVideo (req: express.Request, res: express.Response) { const videoInstance = res.locals.video + if (videoInstance.isOutdated()) { + JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video', videoUrl: videoInstance.url } }) + .catch(err => logger.error('Cannot create AP refresher job for video %s.', videoInstance.url, { err })) + } + return res.json(videoInstance.toFormattedDetailsJSON()) } @@ -429,7 +434,7 @@ async function getVideoDescription (req: express.Request, res: express.Response) return res.json({ description }) } -async function listVideos (req: express.Request, res: express.Response, next: express.NextFunction) { +async function listVideos (req: express.Request, res: express.Response) { const resultList = await VideoModel.listForApi({ start: req.query.start, count: req.query.count, -- cgit v1.2.3 From fc2ec87a8c4dcfbb91a1a62cf4c07a2a8e6a50fe Mon Sep 17 00:00:00 2001 From: Josh Morel Date: Wed, 21 Nov 2018 02:48:29 -0500 Subject: enable email verification by admin (#1348) * enable email verification by admin * rename/label to set email as verified to be more explicit that admin is not sending another email to confirm * add update user emailVerified check-params test * make user.model emailVerified property required --- server/controllers/api/users/index.ts | 1 + 1 file changed, 1 insertion(+) (limited to 'server/controllers') diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts index 9fcb8077f..87fab4a40 100644 --- a/server/controllers/api/users/index.ts +++ b/server/controllers/api/users/index.ts @@ -262,6 +262,7 @@ async function updateUser (req: express.Request, res: express.Response, next: ex const roleChanged = body.role !== undefined && body.role !== userToUpdate.role if (body.email !== undefined) userToUpdate.email = body.email + if (body.emailVerified !== undefined) userToUpdate.emailVerified = body.emailVerified if (body.videoQuota !== undefined) userToUpdate.videoQuota = body.videoQuota if (body.videoQuotaDaily !== undefined) userToUpdate.videoQuotaDaily = body.videoQuotaDaily if (body.role !== undefined) userToUpdate.role = body.role -- cgit v1.2.3 From 1b5e2d72900c8ceaf76940b72839d3c424ac96e8 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 23 Nov 2018 11:06:10 +0100 Subject: Optimize config endpoint --- server/controllers/api/config.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts index 03c1cec7b..5233e9f68 100644 --- a/server/controllers/api/config.ts +++ b/server/controllers/api/config.ts @@ -10,7 +10,7 @@ import { customConfigUpdateValidator } from '../../middlewares/validators/config import { ClientHtml } from '../../lib/client-html' import { auditLoggerFactory, CustomConfigAuditView, getAuditIdFromRes } from '../../helpers/audit-logger' import { remove, writeJSON } from 'fs-extra' -import { getVersion } from '../../helpers/utils' +import { getServerCommit } from '../../helpers/utils' const packageJSON = require('../../../../package.json') const configRouter = express.Router() @@ -40,11 +40,11 @@ configRouter.delete('/custom', ) let serverCommit: string -async function getConfig (req: express.Request, res: express.Response, next: express.NextFunction) { +async function getConfig (req: express.Request, res: express.Response) { const allowed = await isSignupAllowed() const allowedForCurrentIP = isSignupAllowedForCurrentIP(req.ip) - serverCommit = (serverCommit) ? serverCommit : await getVersion() - if (serverCommit === packageJSON.version) serverCommit = '' + + if (serverCommit === undefined) serverCommit = await getServerCommit() const enabledResolutions = Object.keys(CONFIG.TRANSCODING.RESOLUTIONS) .filter(key => CONFIG.TRANSCODING.ENABLED === CONFIG.TRANSCODING.RESOLUTIONS[key] === true) -- cgit v1.2.3 From 1a8dd4da77468068d1ff7f7bd67f76399ae04e04 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 30 Nov 2018 15:06:06 +0100 Subject: Fix AP redirection --- server/controllers/activitypub/client.ts | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts index d9d385460..1a4e28dc8 100644 --- a/server/controllers/activitypub/client.ts +++ b/server/controllers/activitypub/client.ts @@ -162,10 +162,10 @@ function getAccountVideoRate (rateType: VideoRateType) { } } -async function videoController (req: express.Request, res: express.Response, next: express.NextFunction) { +async function videoController (req: express.Request, res: express.Response) { const video: VideoModel = res.locals.video - if (video.isOwned() === false) return res.redirect(video.url) + if (video.url.startsWith(CONFIG.WEBSERVER.URL) === false) return res.redirect(video.url) // We need captions to render AP object video.VideoCaptions = await VideoCaptionModel.listVideoCaptions(video.id) @@ -181,17 +181,17 @@ async function videoController (req: express.Request, res: express.Response, nex return activityPubResponse(activityPubContextify(videoObject), res) } -async function videoAnnounceController (req: express.Request, res: express.Response, next: express.NextFunction) { +async function videoAnnounceController (req: express.Request, res: express.Response) { const share = res.locals.videoShare as VideoShareModel - if (share.Actor.isOwned() === false) return res.redirect(share.url) + if (share.url.startsWith(CONFIG.WEBSERVER.URL) === false) return res.redirect(share.url) const { activity } = await buildAnnounceWithVideoAudience(share.Actor, share, res.locals.video, undefined) return activityPubResponse(activityPubContextify(activity), res) } -async function videoAnnouncesController (req: express.Request, res: express.Response, next: express.NextFunction) { +async function videoAnnouncesController (req: express.Request, res: express.Response) { const video: VideoModel = res.locals.video const handler = async (start: number, count: number) => { @@ -206,21 +206,21 @@ async function videoAnnouncesController (req: express.Request, res: express.Resp return activityPubResponse(activityPubContextify(json), res) } -async function videoLikesController (req: express.Request, res: express.Response, next: express.NextFunction) { +async function videoLikesController (req: express.Request, res: express.Response) { const video: VideoModel = res.locals.video const json = await videoRates(req, 'like', video, getVideoLikesActivityPubUrl(video)) return activityPubResponse(activityPubContextify(json), res) } -async function videoDislikesController (req: express.Request, res: express.Response, next: express.NextFunction) { +async function videoDislikesController (req: express.Request, res: express.Response) { const video: VideoModel = res.locals.video const json = await videoRates(req, 'dislike', video, getVideoDislikesActivityPubUrl(video)) return activityPubResponse(activityPubContextify(json), res) } -async function videoCommentsController (req: express.Request, res: express.Response, next: express.NextFunction) { +async function videoCommentsController (req: express.Request, res: express.Response) { const video: VideoModel = res.locals.video const handler = async (start: number, count: number) => { @@ -235,30 +235,30 @@ async function videoCommentsController (req: express.Request, res: express.Respo return activityPubResponse(activityPubContextify(json), res) } -async function videoChannelController (req: express.Request, res: express.Response, next: express.NextFunction) { +async function videoChannelController (req: express.Request, res: express.Response) { const videoChannel: VideoChannelModel = res.locals.videoChannel return activityPubResponse(activityPubContextify(videoChannel.toActivityPubObject()), res) } -async function videoChannelFollowersController (req: express.Request, res: express.Response, next: express.NextFunction) { +async function videoChannelFollowersController (req: express.Request, res: express.Response) { const videoChannel: VideoChannelModel = res.locals.videoChannel const activityPubResult = await actorFollowers(req, videoChannel.Actor) return activityPubResponse(activityPubContextify(activityPubResult), res) } -async function videoChannelFollowingController (req: express.Request, res: express.Response, next: express.NextFunction) { +async function videoChannelFollowingController (req: express.Request, res: express.Response) { const videoChannel: VideoChannelModel = res.locals.videoChannel const activityPubResult = await actorFollowing(req, videoChannel.Actor) return activityPubResponse(activityPubContextify(activityPubResult), res) } -async function videoCommentController (req: express.Request, res: express.Response, next: express.NextFunction) { +async function videoCommentController (req: express.Request, res: express.Response) { const videoComment: VideoCommentModel = res.locals.videoComment - if (videoComment.isOwned() === false) return res.redirect(videoComment.url) + if (videoComment.url.startsWith(CONFIG.WEBSERVER.URL) === false) return res.redirect(videoComment.url) const threadParentComments = await VideoCommentModel.listThreadParentComments(videoComment, undefined) const isPublic = true // Comments are always public @@ -276,7 +276,7 @@ async function videoCommentController (req: express.Request, res: express.Respon async function videoRedundancyController (req: express.Request, res: express.Response) { const videoRedundancy: VideoRedundancyModel = res.locals.videoRedundancy - if (videoRedundancy.isOwned() === false) return res.redirect(videoRedundancy.url) + if (videoRedundancy.url.startsWith(CONFIG.WEBSERVER.URL) === false) return res.redirect(videoRedundancy.url) const serverActor = await getServerActor() -- cgit v1.2.3 From dbe6aa698eaacf9125d2c4232dee6e3e1f0d7ba1 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 3 Dec 2018 09:14:56 +0100 Subject: Fix trending page --- server/controllers/api/videos/index.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index b659f53ed..3d1b2e1a2 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts @@ -411,12 +411,7 @@ async function viewVideo (req: express.Request, res: express.Response) { ]) const serverActor = await getServerActor() - - // Send the event to the origin server - // If we own the video, we'll send an update event when we'll process the views (in our job queue) - if (videoInstance.isOwned() === false) { - await sendCreateView(serverActor, videoInstance, undefined) - } + await sendCreateView(serverActor, videoInstance, undefined) return res.status(204).end() } -- cgit v1.2.3 From 6040f87d143a5fa01db79867ece8197c3ce7be47 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 4 Dec 2018 16:02:49 +0100 Subject: Add tmp and redundancy directories --- server/controllers/api/users/me.ts | 4 ++-- server/controllers/api/video-channel.ts | 2 +- server/controllers/api/videos/import.ts | 6 +++--- server/controllers/api/videos/index.ts | 10 +++++----- server/controllers/static.ts | 9 +++++++-- 5 files changed, 18 insertions(+), 13 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts index 82299747d..47f2c9ec7 100644 --- a/server/controllers/api/users/me.ts +++ b/server/controllers/api/users/me.ts @@ -42,7 +42,7 @@ import { AccountModel } from '../../../models/account/account' const auditLogger = auditLoggerFactory('users-me') -const reqAvatarFile = createReqFiles([ 'avatarfile' ], IMAGE_MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.AVATARS_DIR }) +const reqAvatarFile = createReqFiles([ 'avatarfile' ], IMAGE_MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.TMP_DIR }) const meRouter = express.Router() @@ -348,7 +348,7 @@ async function updateMe (req: express.Request, res: express.Response, next: expr return res.sendStatus(204) } -async function updateMyAvatar (req: express.Request, res: express.Response, next: express.NextFunction) { +async function updateMyAvatar (req: express.Request, res: express.Response) { const avatarPhysicalFile = req.files[ 'avatarfile' ][ 0 ] const user: UserModel = res.locals.oauth.token.user const oldUserAuditView = new UserAuditView(user.toFormattedJSON()) diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts index 9bf3c5fd8..63240dfa1 100644 --- a/server/controllers/api/video-channel.ts +++ b/server/controllers/api/video-channel.ts @@ -32,7 +32,7 @@ import { resetSequelizeInstance } from '../../helpers/database-utils' import { UserModel } from '../../models/account/user' const auditLogger = auditLoggerFactory('channels') -const reqAvatarFile = createReqFiles([ 'avatarfile' ], IMAGE_MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.AVATARS_DIR }) +const reqAvatarFile = createReqFiles([ 'avatarfile' ], IMAGE_MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.TMP_DIR }) const videoChannelRouter = express.Router() diff --git a/server/controllers/api/videos/import.ts b/server/controllers/api/videos/import.ts index 398fd5a7f..f27d648c7 100644 --- a/server/controllers/api/videos/import.ts +++ b/server/controllers/api/videos/import.ts @@ -37,9 +37,9 @@ const reqVideoFileImport = createReqFiles( [ 'thumbnailfile', 'previewfile', 'torrentfile' ], Object.assign({}, TORRENT_MIMETYPE_EXT, IMAGE_MIMETYPE_EXT), { - thumbnailfile: CONFIG.STORAGE.THUMBNAILS_DIR, - previewfile: CONFIG.STORAGE.PREVIEWS_DIR, - torrentfile: CONFIG.STORAGE.TORRENTS_DIR + thumbnailfile: CONFIG.STORAGE.TMP_DIR, + previewfile: CONFIG.STORAGE.TMP_DIR, + torrentfile: CONFIG.STORAGE.TMP_DIR } ) diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 3d1b2e1a2..4e4697ef4 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts @@ -67,17 +67,17 @@ const reqVideoFileAdd = createReqFiles( [ 'videofile', 'thumbnailfile', 'previewfile' ], Object.assign({}, VIDEO_MIMETYPE_EXT, IMAGE_MIMETYPE_EXT), { - videofile: CONFIG.STORAGE.VIDEOS_DIR, - thumbnailfile: CONFIG.STORAGE.THUMBNAILS_DIR, - previewfile: CONFIG.STORAGE.PREVIEWS_DIR + videofile: CONFIG.STORAGE.TMP_DIR, + thumbnailfile: CONFIG.STORAGE.TMP_DIR, + previewfile: CONFIG.STORAGE.TMP_DIR } ) const reqVideoFileUpdate = createReqFiles( [ 'thumbnailfile', 'previewfile' ], IMAGE_MIMETYPE_EXT, { - thumbnailfile: CONFIG.STORAGE.THUMBNAILS_DIR, - previewfile: CONFIG.STORAGE.PREVIEWS_DIR + thumbnailfile: CONFIG.STORAGE.TMP_DIR, + previewfile: CONFIG.STORAGE.TMP_DIR } ) diff --git a/server/controllers/static.ts b/server/controllers/static.ts index 75e30353c..f16a7d72b 100644 --- a/server/controllers/static.ts +++ b/server/controllers/static.ts @@ -34,12 +34,17 @@ staticRouter.use( ) // Videos path for webseeding -const videosPhysicalPath = CONFIG.STORAGE.VIDEOS_DIR staticRouter.use( STATIC_PATHS.WEBSEED, cors(), - express.static(videosPhysicalPath) + express.static(CONFIG.STORAGE.VIDEOS_DIR) ) +staticRouter.use( + STATIC_PATHS.WEBSEED, + cors(), + express.static(CONFIG.STORAGE.REDUNDANCY_DIR, { fallthrough: false }) // 404, because we don't have this video +) + staticRouter.use( STATIC_DOWNLOAD_PATHS.VIDEOS + ':id-:resolution([0-9]+).:extension', asyncMiddleware(videosGetValidator), -- cgit v1.2.3 From b9fffa297f49a84df8ffd0d7b842599bc88a8e3e Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 4 Dec 2018 17:08:55 +0100 Subject: Create redundancy endpoint --- server/controllers/static.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/static.ts b/server/controllers/static.ts index f16a7d72b..55e7392a1 100644 --- a/server/controllers/static.ts +++ b/server/controllers/static.ts @@ -37,12 +37,12 @@ staticRouter.use( staticRouter.use( STATIC_PATHS.WEBSEED, cors(), - express.static(CONFIG.STORAGE.VIDEOS_DIR) + express.static(CONFIG.STORAGE.VIDEOS_DIR, { fallthrough: false }) // 404 because we don't have this video ) staticRouter.use( - STATIC_PATHS.WEBSEED, + STATIC_PATHS.REDUNDANCY, cors(), - express.static(CONFIG.STORAGE.REDUNDANCY_DIR, { fallthrough: false }) // 404, because we don't have this video + express.static(CONFIG.STORAGE.REDUNDANCY_DIR, { fallthrough: false }) // 404 because we don't have this video ) staticRouter.use( -- cgit v1.2.3 From 4e74e8032be8293ffe3cb3c30528d4ef7c11a798 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 5 Dec 2018 14:36:05 +0100 Subject: Remove inferred type --- server/controllers/api/accounts.ts | 4 ++-- server/controllers/api/users/me.ts | 2 +- server/controllers/api/video-channel.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/accounts.ts b/server/controllers/api/accounts.ts index 86ef2aed1..a69a83acf 100644 --- a/server/controllers/api/accounts.ts +++ b/server/controllers/api/accounts.ts @@ -74,10 +74,10 @@ async function listVideoAccountChannels (req: express.Request, res: express.Resp async function listAccountVideos (req: express.Request, res: express.Response, next: express.NextFunction) { const account: AccountModel = res.locals.account - const actorId = isUserAbleToSearchRemoteURI(res) ? null : undefined + const followerActorId = isUserAbleToSearchRemoteURI(res) ? null : undefined const resultList = await VideoModel.listForApi({ - actorId, + followerActorId, start: req.query.start, count: req.query.count, sort: req.query.sort, diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts index 47f2c9ec7..d2456346b 100644 --- a/server/controllers/api/users/me.ts +++ b/server/controllers/api/users/me.ts @@ -238,7 +238,7 @@ async function getUserSubscriptionVideos (req: express.Request, res: express.Res nsfw: buildNSFWFilter(res, req.query.nsfw), filter: req.query.filter as VideoFilter, withFiles: false, - actorId: user.Account.Actor.id, + followerActorId: user.Account.Actor.id, user }) diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts index 63240dfa1..fd143a139 100644 --- a/server/controllers/api/video-channel.ts +++ b/server/controllers/api/video-channel.ts @@ -202,10 +202,10 @@ async function getVideoChannel (req: express.Request, res: express.Response, nex async function listVideoChannelVideos (req: express.Request, res: express.Response, next: express.NextFunction) { const videoChannelInstance: VideoChannelModel = res.locals.videoChannel - const actorId = isUserAbleToSearchRemoteURI(res) ? null : undefined + const followerActorId = isUserAbleToSearchRemoteURI(res) ? null : undefined const resultList = await VideoModel.listForApi({ - actorId, + followerActorId, start: req.query.start, count: req.query.count, sort: req.query.sort, -- cgit v1.2.3 From 3b3b18203fe73e499bf8b49b15369710df95993e Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 5 Dec 2018 15:10:45 +0100 Subject: Add error when email system is not configured and using the forgot password system --- server/controllers/api/config.ts | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'server/controllers') diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts index 5233e9f68..d65e321e9 100644 --- a/server/controllers/api/config.ts +++ b/server/controllers/api/config.ts @@ -11,6 +11,7 @@ import { ClientHtml } from '../../lib/client-html' import { auditLoggerFactory, CustomConfigAuditView, getAuditIdFromRes } from '../../helpers/audit-logger' import { remove, writeJSON } from 'fs-extra' import { getServerCommit } from '../../helpers/utils' +import { Emailer } from '../../lib/emailer' const packageJSON = require('../../../../package.json') const configRouter = express.Router() @@ -61,6 +62,9 @@ async function getConfig (req: express.Request, res: express.Response) { css: CONFIG.INSTANCE.CUSTOMIZATIONS.CSS } }, + email: { + enabled: Emailer.Instance.isEnabled() + }, serverVersion: packageJSON.version, serverCommit, signup: { -- cgit v1.2.3 From 2feebf3e6afaad9ab80976d1557d3a7bcf94de03 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 5 Dec 2018 17:27:24 +0100 Subject: Add sitemap --- server/controllers/bots.ts | 101 ++++++++++++++++++++++++++++++++++++++++++++ server/controllers/index.ts | 1 + 2 files changed, 102 insertions(+) create mode 100644 server/controllers/bots.ts (limited to 'server/controllers') diff --git a/server/controllers/bots.ts b/server/controllers/bots.ts new file mode 100644 index 000000000..b4eaccf9f --- /dev/null +++ b/server/controllers/bots.ts @@ -0,0 +1,101 @@ +import * as express from 'express' +import { asyncMiddleware } from '../middlewares' +import { CONFIG, ROUTE_CACHE_LIFETIME } from '../initializers' +import * as sitemapModule from 'sitemap' +import { logger } from '../helpers/logger' +import { VideoModel } from '../models/video/video' +import { VideoChannelModel } from '../models/video/video-channel' +import { AccountModel } from '../models/account/account' +import { cacheRoute } from '../middlewares/cache' +import { buildNSFWFilter } from '../helpers/express-utils' +import { truncate } from 'lodash' + +const botsRouter = express.Router() + +// Special route that add OpenGraph and oEmbed tags +// Do not use a template engine for a so little thing +botsRouter.use('/sitemap.xml', + asyncMiddleware(cacheRoute(ROUTE_CACHE_LIFETIME.SITEMAP)), + asyncMiddleware(getSitemap) +) + +// --------------------------------------------------------------------------- + +export { + botsRouter +} + +// --------------------------------------------------------------------------- + +async function getSitemap (req: express.Request, res: express.Response) { + let urls = getSitemapBasicUrls() + + urls = urls.concat(await getSitemapLocalVideoUrls()) + urls = urls.concat(await getSitemapVideoChannelUrls()) + urls = urls.concat(await getSitemapAccountUrls()) + + const sitemap = sitemapModule.createSitemap({ + hostname: CONFIG.WEBSERVER.URL, + urls: urls + }) + + sitemap.toXML((err, xml) => { + if (err) { + logger.error('Cannot generate sitemap.', { err }) + return res.sendStatus(500) + } + + res.header('Content-Type', 'application/xml') + res.send(xml) + }) +} + +async function getSitemapVideoChannelUrls () { + const rows = await VideoChannelModel.listLocalsForSitemap('createdAt') + + return rows.map(channel => ({ + url: CONFIG.WEBSERVER.URL + '/video-channels/' + channel.Actor.preferredUsername + })) +} + +async function getSitemapAccountUrls () { + const rows = await AccountModel.listLocalsForSitemap('createdAt') + + return rows.map(channel => ({ + url: CONFIG.WEBSERVER.URL + '/accounts/' + channel.Actor.preferredUsername + })) +} + +async function getSitemapLocalVideoUrls () { + const resultList = await VideoModel.listForApi({ + start: 0, + count: undefined, + sort: 'createdAt', + includeLocalVideos: true, + nsfw: buildNSFWFilter(), + filter: 'local', + withFiles: false + }) + + return resultList.data.map(v => ({ + url: CONFIG.WEBSERVER.URL + '/videos/watch/' + v.uuid, + video: [ + { + title: v.name, + // Sitemap description should be < 2000 characters + description: truncate(v.description || v.name, { length: 2000, omission: '...' }), + player_loc: CONFIG.WEBSERVER.URL + '/videos/embed/' + v.uuid, + thumbnail_loc: v.getThumbnailStaticPath() + } + ] + })) +} + +function getSitemapBasicUrls () { + const paths = [ + '/about/instance', + '/videos/local' + ] + + return paths.map(p => ({ url: CONFIG.WEBSERVER.URL + p })) +} diff --git a/server/controllers/index.ts b/server/controllers/index.ts index 197fa897a..a88a03c79 100644 --- a/server/controllers/index.ts +++ b/server/controllers/index.ts @@ -6,3 +6,4 @@ export * from './services' export * from './static' export * from './webfinger' export * from './tracker' +export * from './bots' -- cgit v1.2.3 From d22b9b3e20c99ce2c079105673d2209aef64cec4 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 6 Dec 2018 09:29:00 +0100 Subject: Fix thumbnail sitemap location --- server/controllers/bots.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'server/controllers') diff --git a/server/controllers/bots.ts b/server/controllers/bots.ts index b4eaccf9f..2db86a2d8 100644 --- a/server/controllers/bots.ts +++ b/server/controllers/bots.ts @@ -85,7 +85,7 @@ async function getSitemapLocalVideoUrls () { // Sitemap description should be < 2000 characters description: truncate(v.description || v.name, { length: 2000, omission: '...' }), player_loc: CONFIG.WEBSERVER.URL + '/videos/embed/' + v.uuid, - thumbnail_loc: v.getThumbnailStaticPath() + thumbnail_loc: CONFIG.WEBSERVER.URL + v.getThumbnailStaticPath() } ] })) -- cgit v1.2.3 From 314141279db4826bf4862576b7e0df104834ad00 Mon Sep 17 00:00:00 2001 From: Rigel Kent Date: Fri, 7 Dec 2018 01:42:00 +0100 Subject: (well-known url) add change-password --- server/controllers/static.ts | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'server/controllers') diff --git a/server/controllers/static.ts b/server/controllers/static.ts index 55e7392a1..4fd58f70c 100644 --- a/server/controllers/static.ts +++ b/server/controllers/static.ts @@ -136,6 +136,12 @@ staticRouter.use('/.well-known/dnt/', } ) +staticRouter.use('/.well-known/change-password', + (_, res: express.Response) => { + res.redirect('/my-account/settings') + } +) + // --------------------------------------------------------------------------- export { -- cgit v1.2.3 From 14e2014acc1362cfbb770c051a7254b156cd8efb Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 11 Dec 2018 14:52:50 +0100 Subject: Support additional video extensions --- server/controllers/api/config.ts | 5 ++++- server/controllers/api/users/me.ts | 4 ++-- server/controllers/api/video-channel.ts | 4 ++-- server/controllers/api/videos/captions.ts | 4 ++-- server/controllers/api/videos/import.ts | 11 ++--------- server/controllers/api/videos/index.ts | 12 +++++------- 6 files changed, 17 insertions(+), 23 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts index d65e321e9..c75002aaf 100644 --- a/server/controllers/api/config.ts +++ b/server/controllers/api/config.ts @@ -172,7 +172,8 @@ async function updateCustomConfig (req: express.Request, res: express.Response, 'instance.defaultClientRoute', 'instance.shortDescription', 'cache.videoCaptions', - 'signup.requiresEmailVerification' + 'signup.requiresEmailVerification', + 'transcoding.allowAdditionalExtensions' ) toUpdateJSON.user['video_quota'] = toUpdate.user.videoQuota toUpdateJSON.user['video_quota_daily'] = toUpdate.user.videoQuotaDaily @@ -180,6 +181,7 @@ async function updateCustomConfig (req: express.Request, res: express.Response, toUpdateJSON.instance['short_description'] = toUpdate.instance.shortDescription toUpdateJSON.instance['default_nsfw_policy'] = toUpdate.instance.defaultNSFWPolicy toUpdateJSON.signup['requires_email_verification'] = toUpdate.signup.requiresEmailVerification + toUpdateJSON.transcoding['allow_additional_extensions'] = toUpdate.transcoding.allowAdditionalExtensions await writeJSON(CONFIG.CUSTOM_FILE, toUpdateJSON, { spaces: 2 }) @@ -247,6 +249,7 @@ function customConfig (): CustomConfig { }, transcoding: { enabled: CONFIG.TRANSCODING.ENABLED, + allowAdditionalExtensions: CONFIG.TRANSCODING.ALLOW_ADDITIONAL_EXTENSIONS, threads: CONFIG.TRANSCODING.THREADS, resolutions: { '240p': CONFIG.TRANSCODING.RESOLUTIONS[ '240p' ], diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts index d2456346b..f712b0f0b 100644 --- a/server/controllers/api/users/me.ts +++ b/server/controllers/api/users/me.ts @@ -2,7 +2,7 @@ import * as express from 'express' import 'multer' import { UserUpdateMe, UserVideoRate as FormattedUserVideoRate } from '../../../../shared' import { getFormattedObjects } from '../../../helpers/utils' -import { CONFIG, IMAGE_MIMETYPE_EXT, sequelizeTypescript } from '../../../initializers' +import { CONFIG, MIMETYPES, sequelizeTypescript } from '../../../initializers' import { sendUpdateActor } from '../../../lib/activitypub/send' import { asyncMiddleware, @@ -42,7 +42,7 @@ import { AccountModel } from '../../../models/account/account' const auditLogger = auditLoggerFactory('users-me') -const reqAvatarFile = createReqFiles([ 'avatarfile' ], IMAGE_MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.TMP_DIR }) +const reqAvatarFile = createReqFiles([ 'avatarfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.TMP_DIR }) const meRouter = express.Router() diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts index fd143a139..3d6a6af7f 100644 --- a/server/controllers/api/video-channel.ts +++ b/server/controllers/api/video-channel.ts @@ -22,7 +22,7 @@ import { createVideoChannel } from '../../lib/video-channel' import { buildNSFWFilter, createReqFiles, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' import { setAsyncActorKeys } from '../../lib/activitypub' import { AccountModel } from '../../models/account/account' -import { CONFIG, IMAGE_MIMETYPE_EXT, sequelizeTypescript } from '../../initializers' +import { CONFIG, MIMETYPES, sequelizeTypescript } from '../../initializers' import { logger } from '../../helpers/logger' import { VideoModel } from '../../models/video/video' import { updateAvatarValidator } from '../../middlewares/validators/avatar' @@ -32,7 +32,7 @@ import { resetSequelizeInstance } from '../../helpers/database-utils' import { UserModel } from '../../models/account/user' const auditLogger = auditLoggerFactory('channels') -const reqAvatarFile = createReqFiles([ 'avatarfile' ], IMAGE_MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.TMP_DIR }) +const reqAvatarFile = createReqFiles([ 'avatarfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.TMP_DIR }) const videoChannelRouter = express.Router() diff --git a/server/controllers/api/videos/captions.ts b/server/controllers/api/videos/captions.ts index 3ba918189..9b3661368 100644 --- a/server/controllers/api/videos/captions.ts +++ b/server/controllers/api/videos/captions.ts @@ -2,7 +2,7 @@ import * as express from 'express' import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate } from '../../../middlewares' import { addVideoCaptionValidator, deleteVideoCaptionValidator, listVideoCaptionsValidator } from '../../../middlewares/validators' import { createReqFiles } from '../../../helpers/express-utils' -import { CONFIG, sequelizeTypescript, VIDEO_CAPTIONS_MIMETYPE_EXT } from '../../../initializers' +import { CONFIG, MIMETYPES, sequelizeTypescript } from '../../../initializers' import { getFormattedObjects } from '../../../helpers/utils' import { VideoCaptionModel } from '../../../models/video/video-caption' import { VideoModel } from '../../../models/video/video' @@ -12,7 +12,7 @@ import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils' const reqVideoCaptionAdd = createReqFiles( [ 'captionfile' ], - VIDEO_CAPTIONS_MIMETYPE_EXT, + MIMETYPES.VIDEO_CAPTIONS.MIMETYPE_EXT, { captionfile: CONFIG.STORAGE.CAPTIONS_DIR } diff --git a/server/controllers/api/videos/import.ts b/server/controllers/api/videos/import.ts index f27d648c7..099ab7b8d 100644 --- a/server/controllers/api/videos/import.ts +++ b/server/controllers/api/videos/import.ts @@ -3,14 +3,7 @@ import * as magnetUtil from 'magnet-uri' import 'multer' import { auditLoggerFactory, getAuditIdFromRes, VideoImportAuditView } from '../../../helpers/audit-logger' import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoImportAddValidator } from '../../../middlewares' -import { - CONFIG, - IMAGE_MIMETYPE_EXT, - PREVIEWS_SIZE, - sequelizeTypescript, - THUMBNAILS_SIZE, - TORRENT_MIMETYPE_EXT -} from '../../../initializers' +import { CONFIG, MIMETYPES, PREVIEWS_SIZE, sequelizeTypescript, THUMBNAILS_SIZE } from '../../../initializers' import { getYoutubeDLInfo, YoutubeDLInfo } from '../../../helpers/youtube-dl' import { createReqFiles } from '../../../helpers/express-utils' import { logger } from '../../../helpers/logger' @@ -35,7 +28,7 @@ const videoImportsRouter = express.Router() const reqVideoFileImport = createReqFiles( [ 'thumbnailfile', 'previewfile', 'torrentfile' ], - Object.assign({}, TORRENT_MIMETYPE_EXT, IMAGE_MIMETYPE_EXT), + Object.assign({}, MIMETYPES.TORRENT.MIMETYPE_EXT, MIMETYPES.IMAGE.MIMETYPE_EXT), { thumbnailfile: CONFIG.STORAGE.TMP_DIR, previewfile: CONFIG.STORAGE.TMP_DIR, diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 4e4697ef4..00a1302d1 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts @@ -7,15 +7,13 @@ import { logger } from '../../../helpers/logger' import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' import { getFormattedObjects, getServerActor } from '../../../helpers/utils' import { - CONFIG, - IMAGE_MIMETYPE_EXT, + CONFIG, MIMETYPES, PREVIEWS_SIZE, sequelizeTypescript, THUMBNAILS_SIZE, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, - VIDEO_MIMETYPE_EXT, VIDEO_PRIVACIES } from '../../../initializers' import { @@ -57,7 +55,7 @@ import { ScheduleVideoUpdateModel } from '../../../models/video/schedule-video-u import { videoCaptionsRouter } from './captions' import { videoImportsRouter } from './import' import { resetSequelizeInstance } from '../../../helpers/database-utils' -import { rename } from 'fs-extra' +import { move } from 'fs-extra' import { watchingRouter } from './watching' const auditLogger = auditLoggerFactory('videos') @@ -65,7 +63,7 @@ const videosRouter = express.Router() const reqVideoFileAdd = createReqFiles( [ 'videofile', 'thumbnailfile', 'previewfile' ], - Object.assign({}, VIDEO_MIMETYPE_EXT, IMAGE_MIMETYPE_EXT), + Object.assign({}, MIMETYPES.VIDEO.MIMETYPE_EXT, MIMETYPES.IMAGE.MIMETYPE_EXT), { videofile: CONFIG.STORAGE.TMP_DIR, thumbnailfile: CONFIG.STORAGE.TMP_DIR, @@ -74,7 +72,7 @@ const reqVideoFileAdd = createReqFiles( ) const reqVideoFileUpdate = createReqFiles( [ 'thumbnailfile', 'previewfile' ], - IMAGE_MIMETYPE_EXT, + MIMETYPES.IMAGE.MIMETYPE_EXT, { thumbnailfile: CONFIG.STORAGE.TMP_DIR, previewfile: CONFIG.STORAGE.TMP_DIR @@ -208,7 +206,7 @@ async function addVideo (req: express.Request, res: express.Response) { // Move physical file const videoDir = CONFIG.STORAGE.VIDEOS_DIR const destination = join(videoDir, video.getVideoFilename(videoFile)) - await rename(videoPhysicalFile.path, destination) + await move(videoPhysicalFile.path, destination) // This is important in case if there is another attempt in the retry process videoPhysicalFile.filename = video.getVideoFilename(videoFile) videoPhysicalFile.path = destination -- cgit v1.2.3 From f481c4f9f31e897a08e818f388fecdee07f57142 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 11 Dec 2018 15:12:38 +0100 Subject: Use move instead rename To avoid EXDEV errors --- server/controllers/api/videos/import.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/videos/import.ts b/server/controllers/api/videos/import.ts index 099ab7b8d..98366cd82 100644 --- a/server/controllers/api/videos/import.ts +++ b/server/controllers/api/videos/import.ts @@ -21,7 +21,7 @@ import { VideoChannelModel } from '../../../models/video/video-channel' import * as Bluebird from 'bluebird' import * as parseTorrent from 'parse-torrent' import { getSecureTorrentName } from '../../../helpers/utils' -import { readFile, rename } from 'fs-extra' +import { readFile, move } from 'fs-extra' const auditLogger = auditLoggerFactory('video-imports') const videoImportsRouter = express.Router() @@ -71,7 +71,7 @@ async function addTorrentImport (req: express.Request, res: express.Response, to // Rename the torrent to a secured name const newTorrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, getSecureTorrentName(torrentName)) - await rename(torrentfile.path, newTorrentPath) + await move(torrentfile.path, newTorrentPath) torrentfile.path = newTorrentPath const buf = await readFile(torrentfile.path) -- cgit v1.2.3 From 5e755fff9d70a7fd3c4f85bb524f1b774dd85b25 Mon Sep 17 00:00:00 2001 From: Rigel Kent Date: Thu, 13 Dec 2018 09:49:45 +0100 Subject: add Content Security Policy (#1252) * add Content Security Policy * remove reflect-metadata on production builds to get rid of unsafe-eval * fix baseCSP usage * add SRI to CSP * add blob: to media-src * remove SRI * CSP set to reportOnly * adding data: to connect-src CSP * remove block-all-mixed-content * add report-uri support --- server/controllers/client.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'server/controllers') diff --git a/server/controllers/client.ts b/server/controllers/client.ts index 73b40cf65..e5bd487f1 100644 --- a/server/controllers/client.ts +++ b/server/controllers/client.ts @@ -2,7 +2,7 @@ import * as express from 'express' import { join } from 'path' import { root } from '../helpers/core-utils' import { ACCEPT_HEADERS, STATIC_MAX_AGE } from '../initializers' -import { asyncMiddleware } from '../middlewares' +import { asyncMiddleware, embedCSP } from '../middlewares' import { buildFileLocale, getCompleteLocale, is18nLocale, LOCALE_FILES } from '../../shared/models/i18n/i18n' import { ClientHtml } from '../lib/client-html' import { logger } from '../helpers/logger' @@ -22,6 +22,7 @@ clientsRouter.use('/videos/watch/:id', clientsRouter.use('' + '/videos/embed', + embedCSP, (req: express.Request, res: express.Response, next: express.NextFunction) => { res.removeHeader('X-Frame-Options') res.sendFile(embedPath) -- cgit v1.2.3 From 9aac44236c84f17b14ce35e358a87389766e2743 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 14 Dec 2018 15:49:36 +0100 Subject: Add video title/description when rendering html --- server/controllers/client.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/client.ts b/server/controllers/client.ts index e5bd487f1..f17f2a5d2 100644 --- a/server/controllers/client.ts +++ b/server/controllers/client.ts @@ -16,22 +16,20 @@ const testEmbedPath = join(distPath, 'standalone', 'videos', 'test-embed.html') // Special route that add OpenGraph and oEmbed tags // Do not use a template engine for a so little thing -clientsRouter.use('/videos/watch/:id', - asyncMiddleware(generateWatchHtmlPage) -) +clientsRouter.use('/videos/watch/:id', asyncMiddleware(generateWatchHtmlPage)) -clientsRouter.use('' + +clientsRouter.use( '/videos/embed', embedCSP, - (req: express.Request, res: express.Response, next: express.NextFunction) => { + (req: express.Request, res: express.Response) => { res.removeHeader('X-Frame-Options') res.sendFile(embedPath) } ) -clientsRouter.use('' + - '/videos/test-embed', (req: express.Request, res: express.Response, next: express.NextFunction) => { - res.sendFile(testEmbedPath) -}) +clientsRouter.use( + '/videos/test-embed', + (req: express.Request, res: express.Response) => res.sendFile(testEmbedPath) +) // Static HTML/CSS/JS client files @@ -90,7 +88,7 @@ export { // --------------------------------------------------------------------------- async function generateHTMLPage (req: express.Request, res: express.Response, paramLang?: string) { - const html = await ClientHtml.getIndexHTML(req, res, paramLang) + const html = await ClientHtml.getDefaultHTMLPage(req, res, paramLang) return sendHTML(html, res) } -- cgit v1.2.3 From 8b9a525a180cc9f3a98c334cc052dcfc8f36dcd4 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 17 Dec 2018 15:52:38 +0100 Subject: Add history on server side Add ability to disable, clear and list user videos history --- server/controllers/api/users/index.ts | 2 ++ server/controllers/api/users/me.ts | 1 + server/controllers/api/users/my-history.ts | 57 ++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 server/controllers/api/users/my-history.ts (limited to 'server/controllers') diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts index 87fab4a40..bc24792a2 100644 --- a/server/controllers/api/users/index.ts +++ b/server/controllers/api/users/index.ts @@ -38,6 +38,7 @@ import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../h import { meRouter } from './me' import { deleteUserToken } from '../../../lib/oauth-model' import { myBlocklistRouter } from './my-blocklist' +import { myVideosHistoryRouter } from './my-history' const auditLogger = auditLoggerFactory('users') @@ -55,6 +56,7 @@ const askSendEmailLimiter = new RateLimit({ const usersRouter = express.Router() usersRouter.use('/', myBlocklistRouter) +usersRouter.use('/', myVideosHistoryRouter) usersRouter.use('/', meRouter) usersRouter.get('/autocomplete', diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts index f712b0f0b..8a3208160 100644 --- a/server/controllers/api/users/me.ts +++ b/server/controllers/api/users/me.ts @@ -330,6 +330,7 @@ async function updateMe (req: express.Request, res: express.Response, next: expr if (body.nsfwPolicy !== undefined) user.nsfwPolicy = body.nsfwPolicy if (body.webTorrentEnabled !== undefined) user.webTorrentEnabled = body.webTorrentEnabled if (body.autoPlayVideo !== undefined) user.autoPlayVideo = body.autoPlayVideo + if (body.videosHistoryEnabled !== undefined) user.videosHistoryEnabled = body.videosHistoryEnabled await sequelizeTypescript.transaction(async t => { const userAccount = await AccountModel.load(user.Account.id) diff --git a/server/controllers/api/users/my-history.ts b/server/controllers/api/users/my-history.ts new file mode 100644 index 000000000..6cd782c47 --- /dev/null +++ b/server/controllers/api/users/my-history.ts @@ -0,0 +1,57 @@ +import * as express from 'express' +import { + asyncMiddleware, + asyncRetryTransactionMiddleware, + authenticate, + paginationValidator, + setDefaultPagination, + userHistoryRemoveValidator +} from '../../../middlewares' +import { UserModel } from '../../../models/account/user' +import { getFormattedObjects } from '../../../helpers/utils' +import { UserVideoHistoryModel } from '../../../models/account/user-video-history' +import { sequelizeTypescript } from '../../../initializers' + +const myVideosHistoryRouter = express.Router() + +myVideosHistoryRouter.get('/me/history/videos', + authenticate, + paginationValidator, + setDefaultPagination, + asyncMiddleware(listMyVideosHistory) +) + +myVideosHistoryRouter.post('/me/history/videos/remove', + authenticate, + userHistoryRemoveValidator, + asyncRetryTransactionMiddleware(removeUserHistory) +) + +// --------------------------------------------------------------------------- + +export { + myVideosHistoryRouter +} + +// --------------------------------------------------------------------------- + +async function listMyVideosHistory (req: express.Request, res: express.Response) { + const user: UserModel = res.locals.oauth.token.User + + const resultList = await UserVideoHistoryModel.listForApi(user, req.query.start, req.query.count) + + return res.json(getFormattedObjects(resultList.data, resultList.total)) +} + +async function removeUserHistory (req: express.Request, res: express.Response) { + const user: UserModel = res.locals.oauth.token.User + const beforeDate = req.body.beforeDate || null + + await sequelizeTypescript.transaction(t => { + return UserVideoHistoryModel.removeHistoryBefore(user, beforeDate, t) + }) + + // Do not send the delete to other instances, we delete OUR copy of this video abuse + + return res.type('json').status(204).end() +} -- cgit v1.2.3 From cef534ed53e4518fe0acf581bfe880788d42fc36 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 26 Dec 2018 10:36:24 +0100 Subject: Add user notification base code --- server/controllers/api/users/index.ts | 2 + server/controllers/api/users/my-notifications.ts | 84 ++++++++++++++++++++++++ server/controllers/api/videos/abuse.ts | 3 + server/controllers/api/videos/blacklist.ts | 14 +++- server/controllers/api/videos/comment.ts | 3 + server/controllers/api/videos/index.ts | 10 ++- server/controllers/feeds.ts | 2 +- server/controllers/tracker.ts | 4 +- 8 files changed, 116 insertions(+), 6 deletions(-) create mode 100644 server/controllers/api/users/my-notifications.ts (limited to 'server/controllers') diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts index bc24792a2..98be46ea2 100644 --- a/server/controllers/api/users/index.ts +++ b/server/controllers/api/users/index.ts @@ -39,6 +39,7 @@ import { meRouter } from './me' import { deleteUserToken } from '../../../lib/oauth-model' import { myBlocklistRouter } from './my-blocklist' import { myVideosHistoryRouter } from './my-history' +import { myNotificationsRouter } from './my-notifications' const auditLogger = auditLoggerFactory('users') @@ -55,6 +56,7 @@ const askSendEmailLimiter = new RateLimit({ }) const usersRouter = express.Router() +usersRouter.use('/', myNotificationsRouter) usersRouter.use('/', myBlocklistRouter) usersRouter.use('/', myVideosHistoryRouter) usersRouter.use('/', meRouter) diff --git a/server/controllers/api/users/my-notifications.ts b/server/controllers/api/users/my-notifications.ts new file mode 100644 index 000000000..cef1d237c --- /dev/null +++ b/server/controllers/api/users/my-notifications.ts @@ -0,0 +1,84 @@ +import * as express from 'express' +import 'multer' +import { + asyncMiddleware, + asyncRetryTransactionMiddleware, + authenticate, + paginationValidator, + setDefaultPagination, + setDefaultSort, + userNotificationsSortValidator +} from '../../../middlewares' +import { UserModel } from '../../../models/account/user' +import { getFormattedObjects } from '../../../helpers/utils' +import { UserNotificationModel } from '../../../models/account/user-notification' +import { meRouter } from './me' +import { + markAsReadUserNotificationsValidator, + updateNotificationSettingsValidator +} from '../../../middlewares/validators/user-notifications' +import { UserNotificationSetting } from '../../../../shared/models/users' +import { UserNotificationSettingModel } from '../../../models/account/user-notification-setting' + +const myNotificationsRouter = express.Router() + +meRouter.put('/me/notification-settings', + authenticate, + updateNotificationSettingsValidator, + asyncRetryTransactionMiddleware(updateNotificationSettings) +) + +myNotificationsRouter.get('/me/notifications', + authenticate, + paginationValidator, + userNotificationsSortValidator, + setDefaultSort, + setDefaultPagination, + asyncMiddleware(listUserNotifications) +) + +myNotificationsRouter.post('/me/notifications/read', + authenticate, + markAsReadUserNotificationsValidator, + asyncMiddleware(markAsReadUserNotifications) +) + +export { + myNotificationsRouter +} + +// --------------------------------------------------------------------------- + +async function updateNotificationSettings (req: express.Request, res: express.Response) { + const user: UserModel = res.locals.oauth.token.User + const body: UserNotificationSetting = req.body + + const query = { + where: { + userId: user.id + } + } + + await UserNotificationSettingModel.update({ + newVideoFromSubscription: body.newVideoFromSubscription, + newCommentOnMyVideo: body.newCommentOnMyVideo + }, query) + + return res.status(204).end() +} + +async function listUserNotifications (req: express.Request, res: express.Response) { + const user: UserModel = res.locals.oauth.token.User + + const resultList = await UserNotificationModel.listForApi(user.id, req.query.start, req.query.count, req.query.sort) + + return res.json(getFormattedObjects(resultList.data, resultList.total)) +} + +async function markAsReadUserNotifications (req: express.Request, res: express.Response) { + const user: UserModel = res.locals.oauth.token.User + + await UserNotificationModel.markAsRead(user.id, req.body.ids) + + return res.status(204).end() +} diff --git a/server/controllers/api/videos/abuse.ts b/server/controllers/api/videos/abuse.ts index d0c81804b..fe0a95cd5 100644 --- a/server/controllers/api/videos/abuse.ts +++ b/server/controllers/api/videos/abuse.ts @@ -22,6 +22,7 @@ import { VideoModel } from '../../../models/video/video' import { VideoAbuseModel } from '../../../models/video/video-abuse' import { auditLoggerFactory, VideoAbuseAuditView } from '../../../helpers/audit-logger' import { UserModel } from '../../../models/account/user' +import { Notifier } from '../../../lib/notifier' const auditLogger = auditLoggerFactory('abuse') const abuseVideoRouter = express.Router() @@ -117,6 +118,8 @@ async function reportVideoAbuse (req: express.Request, res: express.Response) { await sendVideoAbuse(reporterAccount.Actor, videoAbuseInstance, videoInstance) } + Notifier.Instance.notifyOnNewVideoAbuse(videoAbuseInstance) + auditLogger.create(reporterAccount.Actor.getIdentifier(), new VideoAbuseAuditView(videoAbuseInstance.toFormattedJSON())) return videoAbuseInstance diff --git a/server/controllers/api/videos/blacklist.ts b/server/controllers/api/videos/blacklist.ts index 7f803c8e9..9ef08812b 100644 --- a/server/controllers/api/videos/blacklist.ts +++ b/server/controllers/api/videos/blacklist.ts @@ -16,6 +16,8 @@ import { } from '../../../middlewares' import { VideoBlacklistModel } from '../../../models/video/video-blacklist' import { sequelizeTypescript } from '../../../initializers' +import { Notifier } from '../../../lib/notifier' +import { VideoModel } from '../../../models/video/video' const blacklistRouter = express.Router() @@ -67,13 +69,18 @@ async function addVideoToBlacklist (req: express.Request, res: express.Response) reason: body.reason } - await VideoBlacklistModel.create(toCreate) + const blacklist = await VideoBlacklistModel.create(toCreate) + blacklist.Video = videoInstance + + Notifier.Instance.notifyOnVideoBlacklist(blacklist) + + logger.info('Video %s blacklisted.', res.locals.video.uuid) + return res.type('json').status(204).end() } async function updateVideoBlacklistController (req: express.Request, res: express.Response) { const videoBlacklist = res.locals.videoBlacklist as VideoBlacklistModel - logger.info(videoBlacklist) if (req.body.reason !== undefined) videoBlacklist.reason = req.body.reason @@ -92,11 +99,14 @@ async function listBlacklist (req: express.Request, res: express.Response, next: async function removeVideoFromBlacklistController (req: express.Request, res: express.Response, next: express.NextFunction) { const videoBlacklist = res.locals.videoBlacklist as VideoBlacklistModel + const video: VideoModel = res.locals.video await sequelizeTypescript.transaction(t => { return videoBlacklist.destroy({ transaction: t }) }) + Notifier.Instance.notifyOnVideoUnblacklist(video) + logger.info('Video %s removed from blacklist.', res.locals.video.uuid) return res.type('json').status(204).end() diff --git a/server/controllers/api/videos/comment.ts b/server/controllers/api/videos/comment.ts index 3875c8f79..70c1148ba 100644 --- a/server/controllers/api/videos/comment.ts +++ b/server/controllers/api/videos/comment.ts @@ -26,6 +26,7 @@ import { VideoCommentModel } from '../../../models/video/video-comment' import { auditLoggerFactory, CommentAuditView, getAuditIdFromRes } from '../../../helpers/audit-logger' import { AccountModel } from '../../../models/account/account' import { UserModel } from '../../../models/account/user' +import { Notifier } from '../../../lib/notifier' const auditLogger = auditLoggerFactory('comments') const videoCommentRouter = express.Router() @@ -119,6 +120,7 @@ async function addVideoCommentThread (req: express.Request, res: express.Respons }, t) }) + Notifier.Instance.notifyOnNewComment(comment) auditLogger.create(getAuditIdFromRes(res), new CommentAuditView(comment.toFormattedJSON())) return res.json({ @@ -140,6 +142,7 @@ async function addVideoCommentReply (req: express.Request, res: express.Response }, t) }) + Notifier.Instance.notifyOnNewComment(comment) auditLogger.create(getAuditIdFromRes(res), new CommentAuditView(comment.toFormattedJSON())) return res.json({ comment: comment.toFormattedJSON() }).end() diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 00a1302d1..94ed08fed 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts @@ -7,7 +7,8 @@ import { logger } from '../../../helpers/logger' import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger' import { getFormattedObjects, getServerActor } from '../../../helpers/utils' import { - CONFIG, MIMETYPES, + CONFIG, + MIMETYPES, PREVIEWS_SIZE, sequelizeTypescript, THUMBNAILS_SIZE, @@ -57,6 +58,7 @@ import { videoImportsRouter } from './import' import { resetSequelizeInstance } from '../../../helpers/database-utils' import { move } from 'fs-extra' import { watchingRouter } from './watching' +import { Notifier } from '../../../lib/notifier' const auditLogger = auditLoggerFactory('videos') const videosRouter = express.Router() @@ -262,6 +264,7 @@ async function addVideo (req: express.Request, res: express.Response) { } await federateVideoIfNeeded(video, true, t) + Notifier.Instance.notifyOnNewVideo(video) auditLogger.create(getAuditIdFromRes(res), new VideoAuditView(videoCreated.toFormattedDetailsJSON())) logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoCreated.uuid) @@ -293,6 +296,7 @@ async function updateVideo (req: express.Request, res: express.Response) { const oldVideoAuditView = new VideoAuditView(videoInstance.toFormattedDetailsJSON()) const videoInfoToUpdate: VideoUpdate = req.body const wasPrivateVideo = videoInstance.privacy === VideoPrivacy.PRIVATE + const wasUnlistedVideo = videoInstance.privacy === VideoPrivacy.UNLISTED // Process thumbnail or create it from the video if (req.files && req.files['thumbnailfile']) { @@ -363,6 +367,10 @@ async function updateVideo (req: express.Request, res: express.Response) { const isNewVideo = wasPrivateVideo && videoInstanceUpdated.privacy !== VideoPrivacy.PRIVATE await federateVideoIfNeeded(videoInstanceUpdated, isNewVideo, t) + if (wasUnlistedVideo || wasPrivateVideo) { + Notifier.Instance.notifyOnNewVideo(videoInstanceUpdated) + } + auditLogger.update( getAuditIdFromRes(res), new VideoAuditView(videoInstanceUpdated.toFormattedDetailsJSON()), diff --git a/server/controllers/feeds.ts b/server/controllers/feeds.ts index ccb9b6029..960085af1 100644 --- a/server/controllers/feeds.ts +++ b/server/controllers/feeds.ts @@ -56,7 +56,7 @@ async function generateVideoCommentsFeed (req: express.Request, res: express.Res // Adding video items to the feed, one at a time comments.forEach(comment => { - const link = CONFIG.WEBSERVER.URL + '/videos/watch/' + comment.Video.uuid + ';threadId=' + comment.getThreadId() + const link = CONFIG.WEBSERVER.URL + comment.getCommentStaticPath() feed.addItem({ title: `${comment.Video.name} - ${comment.Account.getDisplayName()}`, diff --git a/server/controllers/tracker.ts b/server/controllers/tracker.ts index 9bc7586d1..53f1653b5 100644 --- a/server/controllers/tracker.ts +++ b/server/controllers/tracker.ts @@ -59,7 +59,7 @@ const onHttpRequest = trackerServer.onHttpRequest.bind(trackerServer) trackerRouter.get('/tracker/announce', (req, res) => onHttpRequest(req, res, { action: 'announce' })) trackerRouter.get('/tracker/scrape', (req, res) => onHttpRequest(req, res, { action: 'scrape' })) -function createWebsocketServer (app: express.Application) { +function createWebsocketTrackerServer (app: express.Application) { const server = http.createServer(app) const wss = new WebSocketServer({ server: server, path: '/tracker/socket' }) wss.on('connection', function (ws, req) { @@ -76,7 +76,7 @@ function createWebsocketServer (app: express.Application) { export { trackerRouter, - createWebsocketServer + createWebsocketTrackerServer } // --------------------------------------------------------------------------- -- cgit v1.2.3 From e8d246d5267ea8b6b3114d4bcf4f34fe5f3a5241 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 28 Dec 2018 13:47:17 +0100 Subject: Add notification settings migration --- server/controllers/api/videos/index.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 94ed08fed..33521a8c1 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts @@ -264,7 +264,6 @@ async function addVideo (req: express.Request, res: express.Response) { } await federateVideoIfNeeded(video, true, t) - Notifier.Instance.notifyOnNewVideo(video) auditLogger.create(getAuditIdFromRes(res), new VideoAuditView(videoCreated.toFormattedDetailsJSON())) logger.info('Video with name %s and uuid %s created.', videoInfo.name, videoCreated.uuid) @@ -272,6 +271,8 @@ async function addVideo (req: express.Request, res: express.Response) { return videoCreated }) + Notifier.Instance.notifyOnNewVideo(videoCreated) + if (video.state === VideoState.TO_TRANSCODE) { // Put uuid because we don't have id auto incremented for now const dataInput = { @@ -311,10 +312,8 @@ async function updateVideo (req: express.Request, res: express.Response) { } try { - await sequelizeTypescript.transaction(async t => { - const sequelizeOptions = { - transaction: t - } + const videoInstanceUpdated = await sequelizeTypescript.transaction(async t => { + const sequelizeOptions = { transaction: t } const oldVideoChannel = videoInstance.VideoChannel if (videoInfoToUpdate.name !== undefined) videoInstance.set('name', videoInfoToUpdate.name) @@ -367,17 +366,19 @@ async function updateVideo (req: express.Request, res: express.Response) { const isNewVideo = wasPrivateVideo && videoInstanceUpdated.privacy !== VideoPrivacy.PRIVATE await federateVideoIfNeeded(videoInstanceUpdated, isNewVideo, t) - if (wasUnlistedVideo || wasPrivateVideo) { - Notifier.Instance.notifyOnNewVideo(videoInstanceUpdated) - } - auditLogger.update( getAuditIdFromRes(res), new VideoAuditView(videoInstanceUpdated.toFormattedDetailsJSON()), oldVideoAuditView ) logger.info('Video with name %s and uuid %s updated.', videoInstance.name, videoInstance.uuid) + + return videoInstanceUpdated }) + + if (wasUnlistedVideo || wasPrivateVideo) { + Notifier.Instance.notifyOnNewVideo(videoInstanceUpdated) + } } catch (err) { // Force fields we want to update // If the transaction is retried, sequelize will think the object has not changed -- cgit v1.2.3 From dc13348070d808d0ba3feb56a435b835c2e7e791 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 2 Jan 2019 16:37:43 +0100 Subject: Add import finished and video published notifs --- server/controllers/api/users/my-notifications.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/users/my-notifications.ts b/server/controllers/api/users/my-notifications.ts index cef1d237c..4b81777a4 100644 --- a/server/controllers/api/users/my-notifications.ts +++ b/server/controllers/api/users/my-notifications.ts @@ -14,10 +14,11 @@ import { getFormattedObjects } from '../../../helpers/utils' import { UserNotificationModel } from '../../../models/account/user-notification' import { meRouter } from './me' import { + listUserNotificationsValidator, markAsReadUserNotificationsValidator, updateNotificationSettingsValidator } from '../../../middlewares/validators/user-notifications' -import { UserNotificationSetting } from '../../../../shared/models/users' +import { UserNotificationSetting, UserNotificationSettingValue } from '../../../../shared/models/users' import { UserNotificationSettingModel } from '../../../models/account/user-notification-setting' const myNotificationsRouter = express.Router() @@ -34,6 +35,7 @@ myNotificationsRouter.get('/me/notifications', userNotificationsSortValidator, setDefaultSort, setDefaultPagination, + listUserNotificationsValidator, asyncMiddleware(listUserNotifications) ) @@ -61,7 +63,11 @@ async function updateNotificationSettings (req: express.Request, res: express.Re await UserNotificationSettingModel.update({ newVideoFromSubscription: body.newVideoFromSubscription, - newCommentOnMyVideo: body.newCommentOnMyVideo + newCommentOnMyVideo: body.newCommentOnMyVideo, + videoAbuseAsModerator: body.videoAbuseAsModerator, + blacklistOnMyVideo: body.blacklistOnMyVideo, + myVideoPublished: body.myVideoPublished, + myVideoImportFinished: body.myVideoImportFinished }, query) return res.status(204).end() @@ -70,7 +76,7 @@ async function updateNotificationSettings (req: express.Request, res: express.Re async function listUserNotifications (req: express.Request, res: express.Response) { const user: UserModel = res.locals.oauth.token.User - const resultList = await UserNotificationModel.listForApi(user.id, req.query.start, req.query.count, req.query.sort) + const resultList = await UserNotificationModel.listForApi(user.id, req.query.start, req.query.count, req.query.sort, req.query.unread) return res.json(getFormattedObjects(resultList.data, resultList.total)) } -- cgit v1.2.3 From f7cc67b455a12ccae9b0ea16876d166720364357 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 4 Jan 2019 08:56:20 +0100 Subject: Add new follow, mention and user registered notifs --- server/controllers/api/users/index.ts | 3 +++ server/controllers/api/users/my-notifications.ts | 15 ++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts index 98be46ea2..9e6a019f6 100644 --- a/server/controllers/api/users/index.ts +++ b/server/controllers/api/users/index.ts @@ -40,6 +40,7 @@ import { deleteUserToken } from '../../../lib/oauth-model' import { myBlocklistRouter } from './my-blocklist' import { myVideosHistoryRouter } from './my-history' import { myNotificationsRouter } from './my-notifications' +import { Notifier } from '../../../lib/notifier' const auditLogger = auditLoggerFactory('users') @@ -213,6 +214,8 @@ async function registerUser (req: express.Request, res: express.Response) { await sendVerifyUserEmail(user) } + Notifier.Instance.notifyOnNewUserRegistration(user) + return res.type('json').status(204).end() } diff --git a/server/controllers/api/users/my-notifications.ts b/server/controllers/api/users/my-notifications.ts index 4b81777a4..d74d26add 100644 --- a/server/controllers/api/users/my-notifications.ts +++ b/server/controllers/api/users/my-notifications.ts @@ -18,7 +18,7 @@ import { markAsReadUserNotificationsValidator, updateNotificationSettingsValidator } from '../../../middlewares/validators/user-notifications' -import { UserNotificationSetting, UserNotificationSettingValue } from '../../../../shared/models/users' +import { UserNotificationSetting } from '../../../../shared/models/users' import { UserNotificationSettingModel } from '../../../models/account/user-notification-setting' const myNotificationsRouter = express.Router() @@ -53,7 +53,7 @@ export { async function updateNotificationSettings (req: express.Request, res: express.Response) { const user: UserModel = res.locals.oauth.token.User - const body: UserNotificationSetting = req.body + const body = req.body const query = { where: { @@ -61,14 +61,19 @@ async function updateNotificationSettings (req: express.Request, res: express.Re } } - await UserNotificationSettingModel.update({ + const values: UserNotificationSetting = { newVideoFromSubscription: body.newVideoFromSubscription, newCommentOnMyVideo: body.newCommentOnMyVideo, videoAbuseAsModerator: body.videoAbuseAsModerator, blacklistOnMyVideo: body.blacklistOnMyVideo, myVideoPublished: body.myVideoPublished, - myVideoImportFinished: body.myVideoImportFinished - }, query) + myVideoImportFinished: body.myVideoImportFinished, + newFollow: body.newFollow, + newUserRegistration: body.newUserRegistration, + commentMention: body.commentMention, + } + + await UserNotificationSettingModel.update(values, query) return res.status(204).end() } -- cgit v1.2.3 From 2f1548fda32c3ba9e53913270394eedfacd55986 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 8 Jan 2019 11:26:41 +0100 Subject: Add notifications in the client --- server/controllers/api/users/my-notifications.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'server/controllers') diff --git a/server/controllers/api/users/my-notifications.ts b/server/controllers/api/users/my-notifications.ts index d74d26add..76cf97587 100644 --- a/server/controllers/api/users/my-notifications.ts +++ b/server/controllers/api/users/my-notifications.ts @@ -45,6 +45,11 @@ myNotificationsRouter.post('/me/notifications/read', asyncMiddleware(markAsReadUserNotifications) ) +myNotificationsRouter.post('/me/notifications/read-all', + authenticate, + asyncMiddleware(markAsReadAllUserNotifications) +) + export { myNotificationsRouter } @@ -70,7 +75,7 @@ async function updateNotificationSettings (req: express.Request, res: express.Re myVideoImportFinished: body.myVideoImportFinished, newFollow: body.newFollow, newUserRegistration: body.newUserRegistration, - commentMention: body.commentMention, + commentMention: body.commentMention } await UserNotificationSettingModel.update(values, query) @@ -93,3 +98,11 @@ async function markAsReadUserNotifications (req: express.Request, res: express.R return res.status(204).end() } + +async function markAsReadAllUserNotifications (req: express.Request, res: express.Response) { + const user: UserModel = res.locals.oauth.token.User + + await UserNotificationModel.markAllAsRead(user.id) + + return res.status(204).end() +} -- cgit v1.2.3 From 89ada4e26ca1df8ff0dd02acda1d1661f121a294 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 8 Jan 2019 15:51:52 +0100 Subject: Fix socket.io websocket connection --- server/controllers/tracker.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/tracker.ts b/server/controllers/tracker.ts index 53f1653b5..1deb8c402 100644 --- a/server/controllers/tracker.ts +++ b/server/controllers/tracker.ts @@ -6,6 +6,7 @@ import * as proxyAddr from 'proxy-addr' import { Server as WebSocketServer } from 'ws' import { CONFIG, TRACKER_RATE_LIMITS } from '../initializers/constants' import { VideoFileModel } from '../models/video/video-file' +import { parse } from 'url' const TrackerServer = bitTorrentTracker.Server @@ -61,14 +62,24 @@ trackerRouter.get('/tracker/scrape', (req, res) => onHttpRequest(req, res, { act function createWebsocketTrackerServer (app: express.Application) { const server = http.createServer(app) - const wss = new WebSocketServer({ server: server, path: '/tracker/socket' }) + const wss = new WebSocketServer({ noServer: true }) + wss.on('connection', function (ws, req) { - const ip = proxyAddr(req, CONFIG.TRUST_PROXY) - ws['ip'] = ip + ws['ip'] = proxyAddr(req, CONFIG.TRUST_PROXY) trackerServer.onWebSocketConnection(ws) }) + server.on('upgrade', (request, socket, head) => { + const pathname = parse(request.url).pathname + + if (pathname === '/tracker/socket') { + wss.handleUpgrade(request, socket, head, ws => wss.emit('connection', ws, request)) + } + + // Don't destroy socket, we have Socket.IO too + }) + return server } -- cgit v1.2.3 From a4101923e699e49ceb9ff36e971c75417fafc9f0 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Wed, 9 Jan 2019 15:14:29 +0100 Subject: Implement contact form on server side --- server/controllers/api/config.ts | 55 ++++++++++++++++---------------- server/controllers/api/server/contact.ts | 28 ++++++++++++++++ server/controllers/api/server/index.ts | 2 ++ 3 files changed, 58 insertions(+), 27 deletions(-) create mode 100644 server/controllers/api/server/contact.ts (limited to 'server/controllers') diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts index c75002aaf..43b20e078 100644 --- a/server/controllers/api/config.ts +++ b/server/controllers/api/config.ts @@ -1,5 +1,5 @@ import * as express from 'express' -import { omit } from 'lodash' +import { omit, snakeCase } from 'lodash' import { ServerConfig, UserRight } from '../../../shared' import { About } from '../../../shared/models/server/about.model' import { CustomConfig } from '../../../shared/models/server/custom-config.model' @@ -12,6 +12,8 @@ import { auditLoggerFactory, CustomConfigAuditView, getAuditIdFromRes } from '.. import { remove, writeJSON } from 'fs-extra' import { getServerCommit } from '../../helpers/utils' import { Emailer } from '../../lib/emailer' +import { isNumeric } from 'validator' +import { objectConverter } from '../../helpers/core-utils' const packageJSON = require('../../../../package.json') const configRouter = express.Router() @@ -65,6 +67,9 @@ async function getConfig (req: express.Request, res: express.Response) { email: { enabled: Emailer.Instance.isEnabled() }, + contactForm: { + enabled: CONFIG.CONTACT_FORM.ENABLED + }, serverVersion: packageJSON.version, serverCommit, signup: { @@ -154,34 +159,10 @@ async function deleteCustomConfig (req: express.Request, res: express.Response, } async function updateCustomConfig (req: express.Request, res: express.Response, next: express.NextFunction) { - const toUpdate: CustomConfig = req.body const oldCustomConfigAuditKeys = new CustomConfigAuditView(customConfig()) - // Force number conversion - toUpdate.cache.previews.size = parseInt('' + toUpdate.cache.previews.size, 10) - toUpdate.cache.captions.size = parseInt('' + toUpdate.cache.captions.size, 10) - toUpdate.signup.limit = parseInt('' + toUpdate.signup.limit, 10) - toUpdate.user.videoQuota = parseInt('' + toUpdate.user.videoQuota, 10) - toUpdate.user.videoQuotaDaily = parseInt('' + toUpdate.user.videoQuotaDaily, 10) - toUpdate.transcoding.threads = parseInt('' + toUpdate.transcoding.threads, 10) - - // camelCase to snake_case key - const toUpdateJSON = omit( - toUpdate, - 'user.videoQuota', - 'instance.defaultClientRoute', - 'instance.shortDescription', - 'cache.videoCaptions', - 'signup.requiresEmailVerification', - 'transcoding.allowAdditionalExtensions' - ) - toUpdateJSON.user['video_quota'] = toUpdate.user.videoQuota - toUpdateJSON.user['video_quota_daily'] = toUpdate.user.videoQuotaDaily - toUpdateJSON.instance['default_client_route'] = toUpdate.instance.defaultClientRoute - toUpdateJSON.instance['short_description'] = toUpdate.instance.shortDescription - toUpdateJSON.instance['default_nsfw_policy'] = toUpdate.instance.defaultNSFWPolicy - toUpdateJSON.signup['requires_email_verification'] = toUpdate.signup.requiresEmailVerification - toUpdateJSON.transcoding['allow_additional_extensions'] = toUpdate.transcoding.allowAdditionalExtensions + // camelCase to snake_case key + Force number conversion + const toUpdateJSON = convertCustomConfigBody(req.body) await writeJSON(CONFIG.CUSTOM_FILE, toUpdateJSON, { spaces: 2 }) @@ -243,6 +224,9 @@ function customConfig (): CustomConfig { admin: { email: CONFIG.ADMIN.EMAIL }, + contactForm: { + enabled: CONFIG.CONTACT_FORM.ENABLED + }, user: { videoQuota: CONFIG.USER.VIDEO_QUOTA, videoQuotaDaily: CONFIG.USER.VIDEO_QUOTA_DAILY @@ -271,3 +255,20 @@ function customConfig (): CustomConfig { } } } + +function convertCustomConfigBody (body: CustomConfig) { + function keyConverter (k: string) { + // Transcoding resolutions exception + if (/^\d{3,4}p$/.exec(k)) return k + + return snakeCase(k) + } + + function valueConverter (v: any) { + if (isNumeric(v + '')) return parseInt('' + v, 10) + + return v + } + + return objectConverter(body, keyConverter, valueConverter) +} diff --git a/server/controllers/api/server/contact.ts b/server/controllers/api/server/contact.ts new file mode 100644 index 000000000..b1144c94e --- /dev/null +++ b/server/controllers/api/server/contact.ts @@ -0,0 +1,28 @@ +import * as express from 'express' +import { asyncMiddleware, contactAdministratorValidator } from '../../../middlewares' +import { Redis } from '../../../lib/redis' +import { Emailer } from '../../../lib/emailer' +import { ContactForm } from '../../../../shared/models/server' + +const contactRouter = express.Router() + +contactRouter.post('/contact', + asyncMiddleware(contactAdministratorValidator), + asyncMiddleware(contactAdministrator) +) + +async function contactAdministrator (req: express.Request, res: express.Response) { + const data = req.body as ContactForm + + await Emailer.Instance.addContactFormJob(data.fromEmail, data.fromName, data.body) + + await Redis.Instance.setContactFormIp(req.ip) + + return res.status(204).end() +} + +// --------------------------------------------------------------------------- + +export { + contactRouter +} diff --git a/server/controllers/api/server/index.ts b/server/controllers/api/server/index.ts index c08192a8c..814248e5f 100644 --- a/server/controllers/api/server/index.ts +++ b/server/controllers/api/server/index.ts @@ -3,6 +3,7 @@ import { serverFollowsRouter } from './follows' import { statsRouter } from './stats' import { serverRedundancyRouter } from './redundancy' import { serverBlocklistRouter } from './server-blocklist' +import { contactRouter } from './contact' const serverRouter = express.Router() @@ -10,6 +11,7 @@ serverRouter.use('/', serverFollowsRouter) serverRouter.use('/', serverRedundancyRouter) serverRouter.use('/', statsRouter) serverRouter.use('/', serverBlocklistRouter) +serverRouter.use('/', contactRouter) // --------------------------------------------------------------------------- -- cgit v1.2.3 From d3e56c0c4b307c99e83fbafb7f2c5884cbc20055 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 10 Jan 2019 11:12:41 +0100 Subject: Implement contact form in the client --- server/controllers/api/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'server/controllers') diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts index 43b20e078..dd06a0597 100644 --- a/server/controllers/api/config.ts +++ b/server/controllers/api/config.ts @@ -65,7 +65,7 @@ async function getConfig (req: express.Request, res: express.Response) { } }, email: { - enabled: Emailer.Instance.isEnabled() + enabled: Emailer.isEnabled() }, contactForm: { enabled: CONFIG.CONTACT_FORM.ENABLED -- cgit v1.2.3 From 5abb9fbbd12e7097e348d6a38622d364b1fa47ed Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 10 Jan 2019 15:39:51 +0100 Subject: Add ability to unfederate a local video (on blacklist) --- server/controllers/api/videos/blacklist.ts | 17 +++++++++++++++-- server/controllers/api/videos/index.ts | 6 +++++- 2 files changed, 20 insertions(+), 3 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/videos/blacklist.ts b/server/controllers/api/videos/blacklist.ts index 9ef08812b..43b0516e7 100644 --- a/server/controllers/api/videos/blacklist.ts +++ b/server/controllers/api/videos/blacklist.ts @@ -18,6 +18,8 @@ import { VideoBlacklistModel } from '../../../models/video/video-blacklist' import { sequelizeTypescript } from '../../../initializers' import { Notifier } from '../../../lib/notifier' import { VideoModel } from '../../../models/video/video' +import { sendCreateVideo, sendDeleteVideo, sendUpdateVideo } from '../../../lib/activitypub/send' +import { federateVideoIfNeeded } from '../../../lib/activitypub' const blacklistRouter = express.Router() @@ -66,12 +68,17 @@ async function addVideoToBlacklist (req: express.Request, res: express.Response) const toCreate = { videoId: videoInstance.id, + unfederated: body.unfederate === true, reason: body.reason } const blacklist = await VideoBlacklistModel.create(toCreate) blacklist.Video = videoInstance + if (body.unfederate === true) { + await sendDeleteVideo(videoInstance, undefined) + } + Notifier.Instance.notifyOnVideoBlacklist(blacklist) logger.info('Video %s blacklisted.', res.locals.video.uuid) @@ -101,8 +108,14 @@ async function removeVideoFromBlacklistController (req: express.Request, res: ex const videoBlacklist = res.locals.videoBlacklist as VideoBlacklistModel const video: VideoModel = res.locals.video - await sequelizeTypescript.transaction(t => { - return videoBlacklist.destroy({ transaction: t }) + await sequelizeTypescript.transaction(async t => { + const unfederated = videoBlacklist.unfederated + await videoBlacklist.destroy({ transaction: t }) + + // Re federate the video + if (unfederated === true) { + await federateVideoIfNeeded(video, true, t) + } }) Notifier.Instance.notifyOnVideoUnblacklist(video) diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 33521a8c1..28ac26598 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts @@ -364,7 +364,11 @@ async function updateVideo (req: express.Request, res: express.Response) { } const isNewVideo = wasPrivateVideo && videoInstanceUpdated.privacy !== VideoPrivacy.PRIVATE - await federateVideoIfNeeded(videoInstanceUpdated, isNewVideo, t) + + // Don't send update if the video was unfederated + if (!videoInstanceUpdated.VideoBlacklist || videoInstanceUpdated.VideoBlacklist.unfederated === false) { + await federateVideoIfNeeded(videoInstanceUpdated, isNewVideo, t) + } auditLogger.update( getAuditIdFromRes(res), -- cgit v1.2.3 From 9b4b15f91c485f9a7fe2ed314b4101f4b7506b38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20B=C3=A9ranger?= <43744761+auberanger@users.noreply.github.com> Date: Mon, 14 Jan 2019 09:06:48 +0100 Subject: WIP : Indicate to users how "trending" works (#1458) * Get the INTERVAL_DAYS const in the video-trending component * Change Trending section title * Add a tooltip to explain how trending section works * Minor CSS fix for the my-feed popover next to the titlepage --- server/controllers/api/config.ts | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'server/controllers') diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts index dd06a0597..255026f46 100644 --- a/server/controllers/api/config.ts +++ b/server/controllers/api/config.ts @@ -120,6 +120,11 @@ async function getConfig (req: express.Request, res: express.Response) { user: { videoQuota: CONFIG.USER.VIDEO_QUOTA, videoQuotaDaily: CONFIG.USER.VIDEO_QUOTA_DAILY + }, + trending: { + videos: { + intervalDays: CONFIG.TRENDING.VIDEOS.INTERVAL_DAYS + } } } -- cgit v1.2.3 From cf405589f06a9ac9d5a05b09bf2183fbf88d56d7 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 14 Jan 2019 10:44:59 +0100 Subject: Move subscriptions controllers in its own file --- server/controllers/api/users/index.ts | 2 + server/controllers/api/users/me.ts | 156 +-------------------- server/controllers/api/users/my-subscriptions.ts | 170 +++++++++++++++++++++++ 3 files changed, 174 insertions(+), 154 deletions(-) create mode 100644 server/controllers/api/users/my-subscriptions.ts (limited to 'server/controllers') diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts index 9e6a019f6..dbe0718d4 100644 --- a/server/controllers/api/users/index.ts +++ b/server/controllers/api/users/index.ts @@ -41,6 +41,7 @@ import { myBlocklistRouter } from './my-blocklist' import { myVideosHistoryRouter } from './my-history' import { myNotificationsRouter } from './my-notifications' import { Notifier } from '../../../lib/notifier' +import { mySubscriptionsRouter } from './my-subscriptions' const auditLogger = auditLoggerFactory('users') @@ -58,6 +59,7 @@ const askSendEmailLimiter = new RateLimit({ const usersRouter = express.Router() usersRouter.use('/', myNotificationsRouter) +usersRouter.use('/', mySubscriptionsRouter) usersRouter.use('/', myBlocklistRouter) usersRouter.use('/', myVideosHistoryRouter) usersRouter.use('/', meRouter) diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts index 8a3208160..94a2b8732 100644 --- a/server/controllers/api/users/me.ts +++ b/server/controllers/api/users/me.ts @@ -8,36 +8,23 @@ import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, - commonVideosFiltersValidator, paginationValidator, setDefaultPagination, setDefaultSort, - userSubscriptionAddValidator, - userSubscriptionGetValidator, usersUpdateMeValidator, usersVideoRatingValidator } from '../../../middlewares' -import { - areSubscriptionsExistValidator, - deleteMeValidator, - userSubscriptionsSortValidator, - videoImportsSortValidator, - videosSortValidator -} from '../../../middlewares/validators' +import { deleteMeValidator, videoImportsSortValidator, videosSortValidator } from '../../../middlewares/validators' import { AccountVideoRateModel } from '../../../models/account/account-video-rate' import { UserModel } from '../../../models/account/user' import { VideoModel } from '../../../models/video/video' import { VideoSortField } from '../../../../client/src/app/shared/video/sort-field.type' -import { buildNSFWFilter, createReqFiles } from '../../../helpers/express-utils' +import { createReqFiles } from '../../../helpers/express-utils' import { UserVideoQuota } from '../../../../shared/models/users/user-video-quota.model' import { updateAvatarValidator } from '../../../middlewares/validators/avatar' import { updateActorAvatarFile } from '../../../lib/avatar' import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../helpers/audit-logger' import { VideoImportModel } from '../../../models/video/video-import' -import { VideoFilter } from '../../../../shared/models/videos/video-query.type' -import { ActorFollowModel } from '../../../models/activitypub/actor-follow' -import { JobQueue } from '../../../lib/job-queue' -import { logger } from '../../../helpers/logger' import { AccountModel } from '../../../models/account/account' const auditLogger = auditLoggerFactory('users-me') @@ -98,51 +85,6 @@ meRouter.post('/me/avatar/pick', asyncRetryTransactionMiddleware(updateMyAvatar) ) -// ##### Subscriptions part ##### - -meRouter.get('/me/subscriptions/videos', - authenticate, - paginationValidator, - videosSortValidator, - setDefaultSort, - setDefaultPagination, - commonVideosFiltersValidator, - asyncMiddleware(getUserSubscriptionVideos) -) - -meRouter.get('/me/subscriptions/exist', - authenticate, - areSubscriptionsExistValidator, - asyncMiddleware(areSubscriptionsExist) -) - -meRouter.get('/me/subscriptions', - authenticate, - paginationValidator, - userSubscriptionsSortValidator, - setDefaultSort, - setDefaultPagination, - asyncMiddleware(getUserSubscriptions) -) - -meRouter.post('/me/subscriptions', - authenticate, - userSubscriptionAddValidator, - asyncMiddleware(addUserSubscription) -) - -meRouter.get('/me/subscriptions/:uri', - authenticate, - userSubscriptionGetValidator, - getUserSubscription -) - -meRouter.delete('/me/subscriptions/:uri', - authenticate, - userSubscriptionGetValidator, - asyncRetryTransactionMiddleware(deleteUserSubscription) -) - // --------------------------------------------------------------------------- export { @@ -151,100 +93,6 @@ export { // --------------------------------------------------------------------------- -async function areSubscriptionsExist (req: express.Request, res: express.Response) { - const uris = req.query.uris as string[] - const user = res.locals.oauth.token.User as UserModel - - const handles = uris.map(u => { - let [ name, host ] = u.split('@') - if (host === CONFIG.WEBSERVER.HOST) host = null - - return { name, host, uri: u } - }) - - const results = await ActorFollowModel.listSubscribedIn(user.Account.Actor.id, handles) - - const existObject: { [id: string ]: boolean } = {} - for (const handle of handles) { - const obj = results.find(r => { - const server = r.ActorFollowing.Server - - return r.ActorFollowing.preferredUsername === handle.name && - ( - (!server && !handle.host) || - (server.host === handle.host) - ) - }) - - existObject[handle.uri] = obj !== undefined - } - - return res.json(existObject) -} - -async function addUserSubscription (req: express.Request, res: express.Response) { - const user = res.locals.oauth.token.User as UserModel - const [ name, host ] = req.body.uri.split('@') - - const payload = { - name, - host, - followerActorId: user.Account.Actor.id - } - - JobQueue.Instance.createJob({ type: 'activitypub-follow', payload }) - .catch(err => logger.error('Cannot create follow job for subscription %s.', req.body.uri, err)) - - return res.status(204).end() -} - -function getUserSubscription (req: express.Request, res: express.Response) { - const subscription: ActorFollowModel = res.locals.subscription - - return res.json(subscription.ActorFollowing.VideoChannel.toFormattedJSON()) -} - -async function deleteUserSubscription (req: express.Request, res: express.Response) { - const subscription: ActorFollowModel = res.locals.subscription - - await sequelizeTypescript.transaction(async t => { - return subscription.destroy({ transaction: t }) - }) - - return res.type('json').status(204).end() -} - -async function getUserSubscriptions (req: express.Request, res: express.Response) { - const user = res.locals.oauth.token.User as UserModel - const actorId = user.Account.Actor.id - - const resultList = await ActorFollowModel.listSubscriptionsForApi(actorId, req.query.start, req.query.count, req.query.sort) - - return res.json(getFormattedObjects(resultList.data, resultList.total)) -} - -async function getUserSubscriptionVideos (req: express.Request, res: express.Response, next: express.NextFunction) { - const user = res.locals.oauth.token.User as UserModel - const resultList = await VideoModel.listForApi({ - start: req.query.start, - count: req.query.count, - sort: req.query.sort, - includeLocalVideos: false, - categoryOneOf: req.query.categoryOneOf, - licenceOneOf: req.query.licenceOneOf, - languageOneOf: req.query.languageOneOf, - tagsOneOf: req.query.tagsOneOf, - tagsAllOf: req.query.tagsAllOf, - nsfw: buildNSFWFilter(res, req.query.nsfw), - filter: req.query.filter as VideoFilter, - withFiles: false, - followerActorId: user.Account.Actor.id, - user - }) - - return res.json(getFormattedObjects(resultList.data, resultList.total)) -} - async function getUserVideos (req: express.Request, res: express.Response, next: express.NextFunction) { const user = res.locals.oauth.token.User as UserModel const resultList = await VideoModel.listUserVideosForApi( diff --git a/server/controllers/api/users/my-subscriptions.ts b/server/controllers/api/users/my-subscriptions.ts new file mode 100644 index 000000000..accca6d52 --- /dev/null +++ b/server/controllers/api/users/my-subscriptions.ts @@ -0,0 +1,170 @@ +import * as express from 'express' +import 'multer' +import { getFormattedObjects } from '../../../helpers/utils' +import { CONFIG, sequelizeTypescript } from '../../../initializers' +import { + asyncMiddleware, + asyncRetryTransactionMiddleware, + authenticate, + commonVideosFiltersValidator, + paginationValidator, + setDefaultPagination, + setDefaultSort, + userSubscriptionAddValidator, + userSubscriptionGetValidator +} from '../../../middlewares' +import { areSubscriptionsExistValidator, userSubscriptionsSortValidator, videosSortValidator } from '../../../middlewares/validators' +import { UserModel } from '../../../models/account/user' +import { VideoModel } from '../../../models/video/video' +import { buildNSFWFilter } from '../../../helpers/express-utils' +import { VideoFilter } from '../../../../shared/models/videos/video-query.type' +import { ActorFollowModel } from '../../../models/activitypub/actor-follow' +import { JobQueue } from '../../../lib/job-queue' +import { logger } from '../../../helpers/logger' + +const mySubscriptionsRouter = express.Router() + +mySubscriptionsRouter.get('/me/subscriptions/videos', + authenticate, + paginationValidator, + videosSortValidator, + setDefaultSort, + setDefaultPagination, + commonVideosFiltersValidator, + asyncMiddleware(getUserSubscriptionVideos) +) + +mySubscriptionsRouter.get('/me/subscriptions/exist', + authenticate, + areSubscriptionsExistValidator, + asyncMiddleware(areSubscriptionsExist) +) + +mySubscriptionsRouter.get('/me/subscriptions', + authenticate, + paginationValidator, + userSubscriptionsSortValidator, + setDefaultSort, + setDefaultPagination, + asyncMiddleware(getUserSubscriptions) +) + +mySubscriptionsRouter.post('/me/subscriptions', + authenticate, + userSubscriptionAddValidator, + asyncMiddleware(addUserSubscription) +) + +mySubscriptionsRouter.get('/me/subscriptions/:uri', + authenticate, + userSubscriptionGetValidator, + getUserSubscription +) + +mySubscriptionsRouter.delete('/me/subscriptions/:uri', + authenticate, + userSubscriptionGetValidator, + asyncRetryTransactionMiddleware(deleteUserSubscription) +) + +// --------------------------------------------------------------------------- + +export { + mySubscriptionsRouter +} + +// --------------------------------------------------------------------------- + +async function areSubscriptionsExist (req: express.Request, res: express.Response) { + const uris = req.query.uris as string[] + const user = res.locals.oauth.token.User as UserModel + + const handles = uris.map(u => { + let [ name, host ] = u.split('@') + if (host === CONFIG.WEBSERVER.HOST) host = null + + return { name, host, uri: u } + }) + + const results = await ActorFollowModel.listSubscribedIn(user.Account.Actor.id, handles) + + const existObject: { [id: string ]: boolean } = {} + for (const handle of handles) { + const obj = results.find(r => { + const server = r.ActorFollowing.Server + + return r.ActorFollowing.preferredUsername === handle.name && + ( + (!server && !handle.host) || + (server.host === handle.host) + ) + }) + + existObject[handle.uri] = obj !== undefined + } + + return res.json(existObject) +} + +async function addUserSubscription (req: express.Request, res: express.Response) { + const user = res.locals.oauth.token.User as UserModel + const [ name, host ] = req.body.uri.split('@') + + const payload = { + name, + host, + followerActorId: user.Account.Actor.id + } + + JobQueue.Instance.createJob({ type: 'activitypub-follow', payload }) + .catch(err => logger.error('Cannot create follow job for subscription %s.', req.body.uri, err)) + + return res.status(204).end() +} + +function getUserSubscription (req: express.Request, res: express.Response) { + const subscription: ActorFollowModel = res.locals.subscription + + return res.json(subscription.ActorFollowing.VideoChannel.toFormattedJSON()) +} + +async function deleteUserSubscription (req: express.Request, res: express.Response) { + const subscription: ActorFollowModel = res.locals.subscription + + await sequelizeTypescript.transaction(async t => { + return subscription.destroy({ transaction: t }) + }) + + return res.type('json').status(204).end() +} + +async function getUserSubscriptions (req: express.Request, res: express.Response) { + const user = res.locals.oauth.token.User as UserModel + const actorId = user.Account.Actor.id + + const resultList = await ActorFollowModel.listSubscriptionsForApi(actorId, req.query.start, req.query.count, req.query.sort) + + return res.json(getFormattedObjects(resultList.data, resultList.total)) +} + +async function getUserSubscriptionVideos (req: express.Request, res: express.Response, next: express.NextFunction) { + const user = res.locals.oauth.token.User as UserModel + const resultList = await VideoModel.listForApi({ + start: req.query.start, + count: req.query.count, + sort: req.query.sort, + includeLocalVideos: false, + categoryOneOf: req.query.categoryOneOf, + licenceOneOf: req.query.licenceOneOf, + languageOneOf: req.query.languageOneOf, + tagsOneOf: req.query.tagsOneOf, + tagsAllOf: req.query.tagsAllOf, + nsfw: buildNSFWFilter(res, req.query.nsfw), + filter: req.query.filter as VideoFilter, + withFiles: false, + followerActorId: user.Account.Actor.id, + user + }) + + return res.json(getFormattedObjects(resultList.data, resultList.total)) +} -- cgit v1.2.3 From 744d0eca195bce7dafeb4a958d0eb3c0046be32d Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 14 Jan 2019 11:30:15 +0100 Subject: Refresh remote actors on GET enpoints --- server/controllers/api/accounts.ts | 7 +++++++ server/controllers/api/video-channel.ts | 6 ++++++ server/controllers/api/videos/index.ts | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) (limited to 'server/controllers') diff --git a/server/controllers/api/accounts.ts b/server/controllers/api/accounts.ts index a69a83acf..8c0237203 100644 --- a/server/controllers/api/accounts.ts +++ b/server/controllers/api/accounts.ts @@ -14,6 +14,8 @@ import { AccountModel } from '../../models/account/account' import { VideoModel } from '../../models/video/video' import { buildNSFWFilter, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' import { VideoChannelModel } from '../../models/video/video-channel' +import { JobQueue } from '../../lib/job-queue' +import { logger } from '../../helpers/logger' const accountsRouter = express.Router() @@ -57,6 +59,11 @@ export { function getAccount (req: express.Request, res: express.Response, next: express.NextFunction) { const account: AccountModel = res.locals.account + if (account.isOutdated()) { + JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'actor', url: account.Actor.url } }) + .catch(err => logger.error('Cannot create AP refresher job for actor %s.', account.Actor.url, { err })) + } + return res.json(account.toFormattedJSON()) } diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts index 3d6a6af7f..db7602139 100644 --- a/server/controllers/api/video-channel.ts +++ b/server/controllers/api/video-channel.ts @@ -30,6 +30,7 @@ import { updateActorAvatarFile } from '../../lib/avatar' import { auditLoggerFactory, getAuditIdFromRes, VideoChannelAuditView } from '../../helpers/audit-logger' import { resetSequelizeInstance } from '../../helpers/database-utils' import { UserModel } from '../../models/account/user' +import { JobQueue } from '../../lib/job-queue' const auditLogger = auditLoggerFactory('channels') const reqAvatarFile = createReqFiles([ 'avatarfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.TMP_DIR }) @@ -197,6 +198,11 @@ async function removeVideoChannel (req: express.Request, res: express.Response) async function getVideoChannel (req: express.Request, res: express.Response, next: express.NextFunction) { const videoChannelWithVideos = await VideoChannelModel.loadAndPopulateAccountAndVideos(res.locals.videoChannel.id) + if (videoChannelWithVideos.isOutdated()) { + JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'actor', url: videoChannelWithVideos.Actor.url } }) + .catch(err => logger.error('Cannot create AP refresher job for actor %s.', videoChannelWithVideos.Actor.url, { err })) + } + return res.json(videoChannelWithVideos.toFormattedJSON()) } diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 28ac26598..2b2dfa7ca 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts @@ -399,7 +399,7 @@ function getVideo (req: express.Request, res: express.Response) { const videoInstance = res.locals.video if (videoInstance.isOutdated()) { - JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video', videoUrl: videoInstance.url } }) + JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video', url: videoInstance.url } }) .catch(err => logger.error('Cannot create AP refresher job for video %s.', videoInstance.url, { err })) } -- cgit v1.2.3 From 44b9c0ba31c4a97e3d874f33226ad935c3a90dd5 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 15 Jan 2019 09:45:54 +0100 Subject: Add totalLocalVideoFilesSize in stats --- server/controllers/api/server/stats.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/server/stats.ts b/server/controllers/api/server/stats.ts index 85803f69e..89ffd1717 100644 --- a/server/controllers/api/server/stats.ts +++ b/server/controllers/api/server/stats.ts @@ -8,6 +8,7 @@ import { VideoCommentModel } from '../../../models/video/video-comment' import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' import { CONFIG, ROUTE_CACHE_LIFETIME } from '../../../initializers/constants' import { cacheRoute } from '../../../middlewares/cache' +import { VideoFileModel } from '../../../models/video/video-file' const statsRouter = express.Router() @@ -16,11 +17,12 @@ statsRouter.get('/stats', asyncMiddleware(getStats) ) -async function getStats (req: express.Request, res: express.Response, next: express.NextFunction) { +async function getStats (req: express.Request, res: express.Response) { const { totalLocalVideos, totalLocalVideoViews, totalVideos } = await VideoModel.getStats() const { totalLocalVideoComments, totalVideoComments } = await VideoCommentModel.getStats() const { totalUsers } = await UserModel.getStats() const { totalInstanceFollowers, totalInstanceFollowing } = await ActorFollowModel.getStats() + const { totalLocalVideoFilesSize } = await VideoFileModel.getStats() const videosRedundancyStats = await Promise.all( CONFIG.REDUNDANCY.VIDEOS.STRATEGIES.map(r => { @@ -32,8 +34,9 @@ async function getStats (req: express.Request, res: express.Response, next: expr const data: ServerStats = { totalLocalVideos, totalLocalVideoViews, - totalVideos, + totalLocalVideoFilesSize, totalLocalVideoComments, + totalVideos, totalVideoComments, totalUsers, totalInstanceFollowers, -- cgit v1.2.3 From 1e7eb25f6cb6893db8f99ff40ef0509aa2a16614 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 15 Jan 2019 14:52:33 +0100 Subject: Correctly send Flag/Dislike/View activities --- server/controllers/activitypub/client.ts | 15 ++++++--------- server/controllers/api/videos/abuse.ts | 2 +- server/controllers/api/videos/index.ts | 4 ++-- 3 files changed, 9 insertions(+), 12 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts index 1a4e28dc8..7e87f6f3b 100644 --- a/server/controllers/activitypub/client.ts +++ b/server/controllers/activitypub/client.ts @@ -3,22 +3,18 @@ import * as express from 'express' import { VideoPrivacy, VideoRateType } from '../../../shared/models/videos' import { activityPubCollectionPagination, activityPubContextify } from '../../helpers/activitypub' import { CONFIG, ROUTE_CACHE_LIFETIME } from '../../initializers' -import { buildAnnounceWithVideoAudience, buildDislikeActivity, buildLikeActivity } from '../../lib/activitypub/send' +import { buildAnnounceWithVideoAudience, buildLikeActivity } from '../../lib/activitypub/send' import { audiencify, getAudience } from '../../lib/activitypub/audience' import { buildCreateActivity } from '../../lib/activitypub/send/send-create' import { asyncMiddleware, - videosShareValidator, executeIfActivityPub, localAccountValidator, localVideoChannelValidator, - videosCustomGetValidator + videosCustomGetValidator, + videosShareValidator } from '../../middlewares' -import { - getAccountVideoRateValidator, - videoCommentGetValidator, - videosGetValidator -} from '../../middlewares/validators' +import { getAccountVideoRateValidator, videoCommentGetValidator, videosGetValidator } from '../../middlewares/validators' import { AccountModel } from '../../models/account/account' import { ActorModel } from '../../models/activitypub/actor' import { ActorFollowModel } from '../../models/activitypub/actor-follow' @@ -40,6 +36,7 @@ import { VideoCaptionModel } from '../../models/video/video-caption' import { videoRedundancyGetValidator } from '../../middlewares/validators/redundancy' import { getServerActor } from '../../helpers/utils' import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' +import { buildDislikeActivity } from '../../lib/activitypub/send/send-dislike' const activityPubClientRouter = express.Router() @@ -156,7 +153,7 @@ function getAccountVideoRate (rateType: VideoRateType) { const url = getRateUrl(rateType, byActor, accountVideoRate.Video) const APObject = rateType === 'like' ? buildLikeActivity(url, byActor, accountVideoRate.Video) - : buildCreateActivity(url, byActor, buildDislikeActivity(url, byActor, accountVideoRate.Video)) + : buildDislikeActivity(url, byActor, accountVideoRate.Video) return activityPubResponse(activityPubContextify(APObject), res) } diff --git a/server/controllers/api/videos/abuse.ts b/server/controllers/api/videos/abuse.ts index fe0a95cd5..32f9c4793 100644 --- a/server/controllers/api/videos/abuse.ts +++ b/server/controllers/api/videos/abuse.ts @@ -3,7 +3,6 @@ import { UserRight, VideoAbuseCreate, VideoAbuseState } from '../../../../shared import { logger } from '../../../helpers/logger' import { getFormattedObjects } from '../../../helpers/utils' import { sequelizeTypescript } from '../../../initializers' -import { sendVideoAbuse } from '../../../lib/activitypub/send' import { asyncMiddleware, asyncRetryTransactionMiddleware, @@ -23,6 +22,7 @@ import { VideoAbuseModel } from '../../../models/video/video-abuse' import { auditLoggerFactory, VideoAbuseAuditView } from '../../../helpers/audit-logger' import { UserModel } from '../../../models/account/user' import { Notifier } from '../../../lib/notifier' +import { sendVideoAbuse } from '../../../lib/activitypub/send/send-flag' const auditLogger = auditLoggerFactory('abuse') const abuseVideoRouter = express.Router() diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 2b2dfa7ca..8414ca42c 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts @@ -23,7 +23,6 @@ import { fetchRemoteVideoDescription, getVideoActivityPubUrl } from '../../../lib/activitypub' -import { sendCreateView } from '../../../lib/activitypub/send' import { JobQueue } from '../../../lib/job-queue' import { Redis } from '../../../lib/redis' import { @@ -59,6 +58,7 @@ import { resetSequelizeInstance } from '../../../helpers/database-utils' import { move } from 'fs-extra' import { watchingRouter } from './watching' import { Notifier } from '../../../lib/notifier' +import { sendView } from '../../../lib/activitypub/send/send-view' const auditLogger = auditLoggerFactory('videos') const videosRouter = express.Router() @@ -422,7 +422,7 @@ async function viewVideo (req: express.Request, res: express.Response) { ]) const serverActor = await getServerActor() - await sendCreateView(serverActor, videoInstance, undefined) + await sendView(serverActor, videoInstance, undefined) return res.status(204).end() } -- cgit v1.2.3 From 092092969633bbcf6d4891a083ea497a7d5c3154 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 29 Jan 2019 08:37:25 +0100 Subject: Add hls support on server --- server/controllers/activitypub/client.ts | 15 ++++++++++----- server/controllers/api/config.ts | 8 +++++++- server/controllers/api/videos/index.ts | 19 +++++++++++-------- server/controllers/static.ts | 9 ++++++++- server/controllers/tracker.ts | 25 ++++++++++++++++--------- 5 files changed, 52 insertions(+), 24 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts index 1a4e28dc8..32a83aa5f 100644 --- a/server/controllers/activitypub/client.ts +++ b/server/controllers/activitypub/client.ts @@ -37,7 +37,7 @@ import { getVideoSharesActivityPubUrl } from '../../lib/activitypub' import { VideoCaptionModel } from '../../models/video/video-caption' -import { videoRedundancyGetValidator } from '../../middlewares/validators/redundancy' +import { videoFileRedundancyGetValidator, videoPlaylistRedundancyGetValidator } from '../../middlewares/validators/redundancy' import { getServerActor } from '../../helpers/utils' import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' @@ -66,11 +66,11 @@ activityPubClientRouter.get('/accounts?/:name/dislikes/:videoId', activityPubClientRouter.get('/videos/watch/:id', executeIfActivityPub(asyncMiddleware(cacheRoute(ROUTE_CACHE_LIFETIME.ACTIVITY_PUB.VIDEOS))), - executeIfActivityPub(asyncMiddleware(videosGetValidator)), + executeIfActivityPub(asyncMiddleware(videosCustomGetValidator('only-video-with-rights'))), executeIfActivityPub(asyncMiddleware(videoController)) ) activityPubClientRouter.get('/videos/watch/:id/activity', - executeIfActivityPub(asyncMiddleware(videosGetValidator)), + executeIfActivityPub(asyncMiddleware(videosCustomGetValidator('only-video-with-rights'))), executeIfActivityPub(asyncMiddleware(videoController)) ) activityPubClientRouter.get('/videos/watch/:id/announces', @@ -116,7 +116,11 @@ activityPubClientRouter.get('/video-channels/:name/following', ) activityPubClientRouter.get('/redundancy/videos/:videoId/:resolution([0-9]+)(-:fps([0-9]+))?', - executeIfActivityPub(asyncMiddleware(videoRedundancyGetValidator)), + executeIfActivityPub(asyncMiddleware(videoFileRedundancyGetValidator)), + executeIfActivityPub(asyncMiddleware(videoRedundancyController)) +) +activityPubClientRouter.get('/redundancy/video-playlists/:streamingPlaylistType/:videoId', + executeIfActivityPub(asyncMiddleware(videoPlaylistRedundancyGetValidator)), executeIfActivityPub(asyncMiddleware(videoRedundancyController)) ) @@ -163,7 +167,8 @@ function getAccountVideoRate (rateType: VideoRateType) { } async function videoController (req: express.Request, res: express.Response) { - const video: VideoModel = res.locals.video + // We need more attributes + const video: VideoModel = await VideoModel.loadForGetAPI(res.locals.video.id) if (video.url.startsWith(CONFIG.WEBSERVER.URL) === false) return res.redirect(video.url) diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts index 255026f46..1f3341bc0 100644 --- a/server/controllers/api/config.ts +++ b/server/controllers/api/config.ts @@ -1,5 +1,5 @@ import * as express from 'express' -import { omit, snakeCase } from 'lodash' +import { snakeCase } from 'lodash' import { ServerConfig, UserRight } from '../../../shared' import { About } from '../../../shared/models/server/about.model' import { CustomConfig } from '../../../shared/models/server/custom-config.model' @@ -78,6 +78,9 @@ async function getConfig (req: express.Request, res: express.Response) { requiresEmailVerification: CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION }, transcoding: { + hls: { + enabled: CONFIG.TRANSCODING.HLS.ENABLED + }, enabledResolutions }, import: { @@ -246,6 +249,9 @@ function customConfig (): CustomConfig { '480p': CONFIG.TRANSCODING.RESOLUTIONS[ '480p' ], '720p': CONFIG.TRANSCODING.RESOLUTIONS[ '720p' ], '1080p': CONFIG.TRANSCODING.RESOLUTIONS[ '1080p' ] + }, + hls: { + enabled: CONFIG.TRANSCODING.HLS.ENABLED } }, import: { diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts index 2b2dfa7ca..e04fc8186 100644 --- a/server/controllers/api/videos/index.ts +++ b/server/controllers/api/videos/index.ts @@ -37,6 +37,7 @@ import { setDefaultPagination, setDefaultSort, videosAddValidator, + videosCustomGetValidator, videosGetValidator, videosRemoveValidator, videosSortValidator, @@ -123,9 +124,9 @@ videosRouter.get('/:id/description', ) videosRouter.get('/:id', optionalAuthenticate, - asyncMiddleware(videosGetValidator), + asyncMiddleware(videosCustomGetValidator('only-video-with-rights')), asyncMiddleware(checkVideoFollowConstraints), - getVideo + asyncMiddleware(getVideo) ) videosRouter.post('/:id/views', asyncMiddleware(videosGetValidator), @@ -395,15 +396,17 @@ async function updateVideo (req: express.Request, res: express.Response) { return res.type('json').status(204).end() } -function getVideo (req: express.Request, res: express.Response) { - const videoInstance = res.locals.video +async function getVideo (req: express.Request, res: express.Response) { + // We need more attributes + const userId: number = res.locals.oauth ? res.locals.oauth.token.User.id : null + const video: VideoModel = await VideoModel.loadForGetAPI(res.locals.video.id, undefined, userId) - if (videoInstance.isOutdated()) { - JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video', url: videoInstance.url } }) - .catch(err => logger.error('Cannot create AP refresher job for video %s.', videoInstance.url, { err })) + if (video.isOutdated()) { + JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video', url: video.url } }) + .catch(err => logger.error('Cannot create AP refresher job for video %s.', video.url, { err })) } - return res.json(videoInstance.toFormattedDetailsJSON()) + return res.json(video.toFormattedDetailsJSON()) } async function viewVideo (req: express.Request, res: express.Response) { diff --git a/server/controllers/static.ts b/server/controllers/static.ts index 4fd58f70c..b21f9da00 100644 --- a/server/controllers/static.ts +++ b/server/controllers/static.ts @@ -1,6 +1,6 @@ import * as cors from 'cors' import * as express from 'express' -import { CONFIG, ROUTE_CACHE_LIFETIME, STATIC_DOWNLOAD_PATHS, STATIC_MAX_AGE, STATIC_PATHS } from '../initializers' +import { CONFIG, HLS_PLAYLIST_DIRECTORY, ROUTE_CACHE_LIFETIME, STATIC_DOWNLOAD_PATHS, STATIC_MAX_AGE, STATIC_PATHS } from '../initializers' import { VideosPreviewCache } from '../lib/cache' import { cacheRoute } from '../middlewares/cache' import { asyncMiddleware, videosGetValidator } from '../middlewares' @@ -51,6 +51,13 @@ staticRouter.use( asyncMiddleware(downloadVideoFile) ) +// HLS +staticRouter.use( + STATIC_PATHS.PLAYLISTS.HLS, + cors(), + express.static(HLS_PLAYLIST_DIRECTORY, { fallthrough: false }) // 404 if the file does not exist +) + // Thumbnails path for express const thumbnailsPhysicalPath = CONFIG.STORAGE.THUMBNAILS_DIR staticRouter.use( diff --git a/server/controllers/tracker.ts b/server/controllers/tracker.ts index 1deb8c402..8b77d9de7 100644 --- a/server/controllers/tracker.ts +++ b/server/controllers/tracker.ts @@ -7,6 +7,7 @@ import { Server as WebSocketServer } from 'ws' import { CONFIG, TRACKER_RATE_LIMITS } from '../initializers/constants' import { VideoFileModel } from '../models/video/video-file' import { parse } from 'url' +import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' const TrackerServer = bitTorrentTracker.Server @@ -21,7 +22,7 @@ const trackerServer = new TrackerServer({ udp: false, ws: false, dht: false, - filter: function (infoHash, params, cb) { + filter: async function (infoHash, params, cb) { let ip: string if (params.type === 'ws') { @@ -32,19 +33,25 @@ const trackerServer = new TrackerServer({ const key = ip + '-' + infoHash - peersIps[ip] = peersIps[ip] ? peersIps[ip] + 1 : 1 - peersIpInfoHash[key] = peersIpInfoHash[key] ? peersIpInfoHash[key] + 1 : 1 + peersIps[ ip ] = peersIps[ ip ] ? peersIps[ ip ] + 1 : 1 + peersIpInfoHash[ key ] = peersIpInfoHash[ key ] ? peersIpInfoHash[ key ] + 1 : 1 - if (peersIpInfoHash[key] > TRACKER_RATE_LIMITS.ANNOUNCES_PER_IP_PER_INFOHASH) { + if (peersIpInfoHash[ key ] > TRACKER_RATE_LIMITS.ANNOUNCES_PER_IP_PER_INFOHASH) { return cb(new Error(`Too many requests (${peersIpInfoHash[ key ]} of ip ${ip} for torrent ${infoHash}`)) } - VideoFileModel.isInfohashExists(infoHash) - .then(exists => { - if (exists === false) return cb(new Error(`Unknown infoHash ${infoHash}`)) + try { + const videoFileExists = await VideoFileModel.doesInfohashExist(infoHash) + if (videoFileExists === true) return cb() - return cb() - }) + const playlistExists = await VideoStreamingPlaylistModel.doesInfohashExist(infoHash) + if (playlistExists === true) return cb() + + return cb(new Error(`Unknown infoHash ${infoHash}`)) + } catch (err) { + logger.error('Error in tracker filter.', { err }) + return cb(err) + } } }) -- cgit v1.2.3 From 328c78bc4a570a9aceaaa1a2124bacd4a0e8d295 Mon Sep 17 00:00:00 2001 From: Rigel Kent Date: Sat, 6 Oct 2018 13:54:00 +0200 Subject: allow administration to change/reset a user's password --- server/controllers/api/users/index.ts | 1 + 1 file changed, 1 insertion(+) (limited to 'server/controllers') diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts index dbe0718d4..beac6d8b1 100644 --- a/server/controllers/api/users/index.ts +++ b/server/controllers/api/users/index.ts @@ -3,6 +3,7 @@ import * as RateLimit from 'express-rate-limit' import { UserCreate, UserRight, UserRole, UserUpdate } from '../../../../shared' import { logger } from '../../../helpers/logger' import { getFormattedObjects } from '../../../helpers/utils' +import { pseudoRandomBytesPromise } from '../../../helpers/core-utils' import { CONFIG, RATES_LIMIT, sequelizeTypescript } from '../../../initializers' import { Emailer } from '../../../lib/emailer' import { Redis } from '../../../lib/redis' -- cgit v1.2.3 From b426edd4854adc6e65844d8c54b8998e792b5778 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 11 Feb 2019 09:30:29 +0100 Subject: Cleanup reset user password by admin And add some tests --- server/controllers/api/users/index.ts | 20 ++++++++++---------- server/controllers/api/users/me.ts | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'server/controllers') diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts index beac6d8b1..e3533a7f6 100644 --- a/server/controllers/api/users/index.ts +++ b/server/controllers/api/users/index.ts @@ -3,7 +3,6 @@ import * as RateLimit from 'express-rate-limit' import { UserCreate, UserRight, UserRole, UserUpdate } from '../../../../shared' import { logger } from '../../../helpers/logger' import { getFormattedObjects } from '../../../helpers/utils' -import { pseudoRandomBytesPromise } from '../../../helpers/core-utils' import { CONFIG, RATES_LIMIT, sequelizeTypescript } from '../../../initializers' import { Emailer } from '../../../lib/emailer' import { Redis } from '../../../lib/redis' @@ -230,7 +229,7 @@ async function unblockUser (req: express.Request, res: express.Response, next: e return res.status(204).end() } -async function blockUser (req: express.Request, res: express.Response, next: express.NextFunction) { +async function blockUser (req: express.Request, res: express.Response) { const user: UserModel = res.locals.user const reason = req.body.reason @@ -239,23 +238,23 @@ async function blockUser (req: express.Request, res: express.Response, next: exp return res.status(204).end() } -function getUser (req: express.Request, res: express.Response, next: express.NextFunction) { +function getUser (req: express.Request, res: express.Response) { return res.json((res.locals.user as UserModel).toFormattedJSON()) } -async function autocompleteUsers (req: express.Request, res: express.Response, next: express.NextFunction) { +async function autocompleteUsers (req: express.Request, res: express.Response) { const resultList = await UserModel.autoComplete(req.query.search as string) return res.json(resultList) } -async function listUsers (req: express.Request, res: express.Response, next: express.NextFunction) { +async function listUsers (req: express.Request, res: express.Response) { const resultList = await UserModel.listForApi(req.query.start, req.query.count, req.query.sort, req.query.search) return res.json(getFormattedObjects(resultList.data, resultList.total)) } -async function removeUser (req: express.Request, res: express.Response, next: express.NextFunction) { +async function removeUser (req: express.Request, res: express.Response) { const user: UserModel = res.locals.user await user.destroy() @@ -265,12 +264,13 @@ async function removeUser (req: express.Request, res: express.Response, next: ex return res.sendStatus(204) } -async function updateUser (req: express.Request, res: express.Response, next: express.NextFunction) { +async function updateUser (req: express.Request, res: express.Response) { const body: UserUpdate = req.body const userToUpdate = res.locals.user as UserModel const oldUserAuditView = new UserAuditView(userToUpdate.toFormattedJSON()) const roleChanged = body.role !== undefined && body.role !== userToUpdate.role + if (body.password !== undefined) userToUpdate.password = body.password if (body.email !== undefined) userToUpdate.email = body.email if (body.emailVerified !== undefined) userToUpdate.emailVerified = body.emailVerified if (body.videoQuota !== undefined) userToUpdate.videoQuota = body.videoQuota @@ -280,11 +280,11 @@ async function updateUser (req: express.Request, res: express.Response, next: ex const user = await userToUpdate.save() // Destroy user token to refresh rights - if (roleChanged) await deleteUserToken(userToUpdate.id) + if (roleChanged || body.password !== undefined) await deleteUserToken(userToUpdate.id) auditLogger.update(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()), oldUserAuditView) - // Don't need to send this update to followers, these attributes are not propagated + // Don't need to send this update to followers, these attributes are not federated return res.sendStatus(204) } @@ -294,7 +294,7 @@ async function askResetUserPassword (req: express.Request, res: express.Response const verificationString = await Redis.Instance.setResetPasswordVerificationString(user.id) const url = CONFIG.WEBSERVER.URL + '/reset-password?userId=' + user.id + '&verificationString=' + verificationString - await Emailer.Instance.addForgetPasswordEmailJob(user.email, url) + await Emailer.Instance.addPasswordResetEmailJob(user.email, url) return res.status(204).end() } diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts index 94a2b8732..d5e154869 100644 --- a/server/controllers/api/users/me.ts +++ b/server/controllers/api/users/me.ts @@ -167,7 +167,7 @@ async function deleteMe (req: express.Request, res: express.Response) { return res.sendStatus(204) } -async function updateMe (req: express.Request, res: express.Response, next: express.NextFunction) { +async function updateMe (req: express.Request, res: express.Response) { const body: UserUpdateMe = req.body const user: UserModel = res.locals.oauth.token.user -- cgit v1.2.3