"pem": "^1.12.3",
"pg": "^6.4.2",
"pg-hstore": "^2.3.2",
+ "reflect-metadata": "^0.1.10",
"request": "^2.81.0",
"rimraf": "^2.5.4",
"safe-buffer": "^5.0.1",
"scripty": "^1.5.0",
"sequelize": "^4.7.5",
+ "sequelize-typescript": "^0.6.1",
"ts-node": "^3.3.0",
"typescript": "^2.5.2",
"uuid": "^3.1.0",
-import * as rimraf from 'rimraf'
import * as Promise from 'bluebird'
+import * as rimraf from 'rimraf'
+import { CONFIG, initDatabase, sequelizeTypescript } from '../../../server/initializers'
-import { CONFIG } from '../../../server/initializers/constants'
-import { database as db } from '../../../server/initializers/database'
-
-db.init(true)
+initDatabase(true)
.then(() => {
- return db.sequelize.drop()
+ return sequelizeTypescript.drop()
})
.then(() => {
console.info('Tables of %s deleted.', CONFIG.DATABASE.DBNAME)
import * as program from 'commander'
-
-import { database as db } from '../server/initializers/database'
+import { initDatabase } from '../server/initializers'
+import { UserModel } from '../server/models/account/user'
program
.option('-u, --user [user]', 'User')
process.exit(-1)
}
-db.init(true)
+initDatabase(true)
.then(() => {
- return db.User.loadByUsername(program['user'])
+ return UserModel.loadByUsername(program['user'])
})
.then(user => {
if (!user) {
-import { database as db } from '../server/initializers/database'
-import { getServerAccount } from '../server/helpers/utils'
+import { getServerAccount } from '../server/helpers'
+import { initDatabase } from '../server/initializers'
+import { AccountFollowModel } from '../server/models/account/account-follow'
+import { VideoModel } from '../server/models/video/video'
-db.init(true)
+initDatabase(true)
.then(() => {
return getServerAccount()
})
.then(serverAccount => {
- return db.AccountFollow.listAcceptedFollowingUrlsForApi([ serverAccount.id ], undefined)
+ return AccountFollowModel.listAcceptedFollowingUrlsForApi([ serverAccount.id ], undefined)
})
.then(res => {
return res.total > 0
}
console.log('Updating torrent files.')
- return db.Video.list()
+ return VideoModel.list()
})
.then(videos => {
const tasks: Promise<any>[] = []
// Do not use barrels because we don't want to load all modules here (we need to initialize database first)
import { logger } from './server/helpers/logger'
// Initialize database and models
-import { database as db } from './server/initializers/database'
-db.init(false).then(() => onDatabaseInitDone())
+import { initDatabase } from './server/initializers/database'
+initDatabase(false).then(() => onDatabaseInitDone())
// ----------- PeerTube modules -----------
import { migrate, installApplication } from './server/initializers'
// Intercept ActivityPub client requests
import * as express from 'express'
-import { pageToStartAndCount } from '../../helpers'
-import { activityPubCollectionPagination } from '../../helpers/activitypub'
-
-import { database as db } from '../../initializers'
-import { ACTIVITY_PUB, CONFIG } from '../../initializers/constants'
-import { buildVideoChannelAnnounceToFollowers } from '../../lib/activitypub/send/send-announce'
+import { activityPubCollectionPagination, pageToStartAndCount } from '../../helpers'
+import { ACTIVITY_PUB, CONFIG } from '../../initializers'
+import { buildVideoChannelAnnounceToFollowers } from '../../lib/activitypub/send'
import { buildVideoAnnounceToFollowers } from '../../lib/index'
-import { executeIfActivityPub, localAccountValidator } from '../../middlewares'
-import { asyncMiddleware } from '../../middlewares/async'
-import { videoChannelsGetValidator, videoChannelsShareValidator } from '../../middlewares/validators/video-channels'
-import { videosGetValidator, videosShareValidator } from '../../middlewares/validators/videos'
-import { AccountInstance, VideoChannelInstance } from '../../models'
-import { VideoChannelShareInstance } from '../../models/video/video-channel-share-interface'
-import { VideoInstance } from '../../models/video/video-interface'
-import { VideoShareInstance } from '../../models/video/video-share-interface'
+import { asyncMiddleware, executeIfActivityPub, localAccountValidator } from '../../middlewares'
+import {
+ videoChannelsGetValidator,
+ videoChannelsShareValidator,
+ videosGetValidator,
+ videosShareValidator
+} from '../../middlewares/validators'
+import { AccountModel } from '../../models/account/account'
+import { AccountFollowModel } from '../../models/account/account-follow'
+import { VideoModel } from '../../models/video/video'
+import { VideoChannelModel } from '../../models/video/video-channel'
+import { VideoChannelShareModel } from '../../models/video/video-channel-share'
+import { VideoShareModel } from '../../models/video/video-share'
const activityPubClientRouter = express.Router()
// ---------------------------------------------------------------------------
function accountController (req: express.Request, res: express.Response, next: express.NextFunction) {
- const account: AccountInstance = res.locals.account
+ const account: AccountModel = res.locals.account
return res.json(account.toActivityPubObject()).end()
}
async function accountFollowersController (req: express.Request, res: express.Response, next: express.NextFunction) {
- const account: AccountInstance = res.locals.account
+ const account: AccountModel = res.locals.account
const page = req.query.page || 1
const { start, count } = pageToStartAndCount(page, ACTIVITY_PUB.COLLECTION_ITEMS_PER_PAGE)
- const result = await db.AccountFollow.listAcceptedFollowerUrlsForApi([ account.id ], undefined, start, count)
+ const result = await AccountFollowModel.listAcceptedFollowerUrlsForApi([ account.id ], undefined, start, count)
const activityPubResult = activityPubCollectionPagination(CONFIG.WEBSERVER.URL + req.url, page, result)
return res.json(activityPubResult)
}
async function accountFollowingController (req: express.Request, res: express.Response, next: express.NextFunction) {
- const account: AccountInstance = res.locals.account
+ const account: AccountModel = res.locals.account
const page = req.query.page || 1
const { start, count } = pageToStartAndCount(page, ACTIVITY_PUB.COLLECTION_ITEMS_PER_PAGE)
- const result = await db.AccountFollow.listAcceptedFollowingUrlsForApi([ account.id ], undefined, start, count)
+ const result = await AccountFollowModel.listAcceptedFollowingUrlsForApi([ account.id ], undefined, start, count)
const activityPubResult = activityPubCollectionPagination(CONFIG.WEBSERVER.URL + req.url, page, result)
return res.json(activityPubResult)
}
function videoController (req: express.Request, res: express.Response, next: express.NextFunction) {
- const video: VideoInstance = res.locals.video
+ const video: VideoModel = res.locals.video
return res.json(video.toActivityPubObject())
}
async function videoAnnounceController (req: express.Request, res: express.Response, next: express.NextFunction) {
- const share = res.locals.videoShare as VideoShareInstance
+ const share = res.locals.videoShare as VideoShareModel
const object = await buildVideoAnnounceToFollowers(share.Account, res.locals.video, undefined)
return res.json(object)
}
async function videoChannelAnnounceController (req: express.Request, res: express.Response, next: express.NextFunction) {
- const share = res.locals.videoChannelShare as VideoChannelShareInstance
+ const share = res.locals.videoChannelShare as VideoChannelShareModel
const object = await buildVideoChannelAnnounceToFollowers(share.Account, share.VideoChannel, undefined)
return res.json(object)
}
async function videoChannelController (req: express.Request, res: express.Response, next: express.NextFunction) {
- const videoChannel: VideoChannelInstance = res.locals.videoChannel
+ const videoChannel: VideoChannelModel = res.locals.videoChannel
return res.json(videoChannel.toActivityPubObject())
}
import { Activity } from '../../../shared/models/activitypub/activity'
import { activityPubCollectionPagination } from '../../helpers/activitypub'
import { pageToStartAndCount } from '../../helpers/core-utils'
-import { database as db } from '../../initializers'
import { ACTIVITY_PUB } from '../../initializers/constants'
import { addActivityData } from '../../lib/activitypub/send/send-add'
import { getAnnounceActivityPubUrl } from '../../lib/activitypub/url'
import { announceActivityData } from '../../lib/index'
import { asyncMiddleware, localAccountValidator } from '../../middlewares'
-import { AccountInstance } from '../../models/account/account-interface'
+import { AccountModel } from '../../models/account/account'
+import { VideoModel } from '../../models/video/video'
const outboxRouter = express.Router()
// ---------------------------------------------------------------------------
async function outboxController (req: express.Request, res: express.Response, next: express.NextFunction) {
- const account: AccountInstance = res.locals.account
+ const account: AccountModel = res.locals.account
const page = req.query.page || 1
const { start, count } = pageToStartAndCount(page, ACTIVITY_PUB.COLLECTION_ITEMS_PER_PAGE)
- const data = await db.Video.listAllAndSharedByAccountForOutbox(account.id, start, count)
+ const data = await VideoModel.listAllAndSharedByAccountForOutbox(account.id, start, count)
const activities: Activity[] = []
for (const video of data.data) {
import * as express from 'express'
-import { asyncMiddleware, jobsSortValidator, setJobsSort, setPagination } from '../../middlewares'
-import { paginationValidator } from '../../middlewares/validators/pagination'
-import { database as db } from '../../initializers'
-import { getFormattedObjects } from '../../helpers/utils'
-import { authenticate } from '../../middlewares/oauth'
-import { ensureUserHasRight } from '../../middlewares/user-right'
-import { UserRight } from '../../../shared/models/users/user-right.enum'
+import { UserRight } from '../../../shared/models/users'
+import { getFormattedObjects } from '../../helpers'
+import { asyncMiddleware, authenticate, ensureUserHasRight, jobsSortValidator, setJobsSort, setPagination } from '../../middlewares'
+import { paginationValidator } from '../../middlewares/validators'
+import { JobModel } from '../../models/job/job'
const jobsRouter = express.Router()
// ---------------------------------------------------------------------------
async function listJobs (req: express.Request, res: express.Response, next: express.NextFunction) {
- const resultList = await db.Job.listForApi(req.query.start, req.query.count, req.query.sort)
+ const resultList = await JobModel.listForApi(req.query.start, req.query.count, req.query.sort)
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
import { CONFIG } from '../../initializers'
import { logger } from '../../helpers'
import { asyncMiddleware } from '../../middlewares'
-import { database as db } from '../../initializers/database'
import { OAuthClientLocal } from '../../../shared'
+import { OAuthClientModel } from '../../models/oauth/oauth-client'
const oauthClientsRouter = express.Router()
return res.type('json').status(403).end()
}
- const client = await db.OAuthClient.loadFirstClient()
+ const client = await OAuthClientModel.loadFirstClient()
if (!client) throw new Error('No client available.')
const json: OAuthClientLocal = {
import * as express from 'express'
-import { UserRight } from '../../../../shared/models/users/user-right.enum'
-import { getFormattedObjects } from '../../../helpers'
-import { retryTransactionWrapper } from '../../../helpers/database-utils'
-import { logger } from '../../../helpers/logger'
-import { getServerAccount } from '../../../helpers/utils'
-import { getAccountFromWebfinger } from '../../../helpers/webfinger'
-import { SERVER_ACCOUNT_NAME } from '../../../initializers/constants'
-import { database as db } from '../../../initializers/database'
-import { saveAccountAndServerIfNotExist } from '../../../lib/activitypub/account'
-import { sendUndoFollow } from '../../../lib/activitypub/send/send-undo'
+import { UserRight } from '../../../../shared/models/users'
+import { getAccountFromWebfinger, getFormattedObjects, getServerAccount, logger, retryTransactionWrapper } from '../../../helpers'
+import { sequelizeTypescript, SERVER_ACCOUNT_NAME } from '../../../initializers'
+import { saveAccountAndServerIfNotExist } from '../../../lib/activitypub'
+import { sendUndoFollow } from '../../../lib/activitypub/send'
import { sendFollow } from '../../../lib/index'
-import { asyncMiddleware, paginationValidator, removeFollowingValidator, setFollowersSort, setPagination } from '../../../middlewares'
-import { authenticate } from '../../../middlewares/oauth'
-import { setBodyHostsPort } from '../../../middlewares/servers'
-import { setFollowingSort } from '../../../middlewares/sort'
-import { ensureUserHasRight } from '../../../middlewares/user-right'
-import { followValidator } from '../../../middlewares/validators/follows'
-import { followersSortValidator, followingSortValidator } from '../../../middlewares/validators/sort'
-import { AccountInstance } from '../../../models/account/account-interface'
-import { AccountFollowInstance } from '../../../models/index'
+import {
+ asyncMiddleware,
+ authenticate,
+ ensureUserHasRight,
+ paginationValidator,
+ removeFollowingValidator,
+ setBodyHostsPort,
+ setFollowersSort,
+ setFollowingSort,
+ setPagination
+} from '../../../middlewares'
+import { followersSortValidator, followingSortValidator, followValidator } from '../../../middlewares/validators'
+import { AccountModel } from '../../../models/account/account'
+import { AccountFollowModel } from '../../../models/account/account-follow'
const serverFollowsRouter = express.Router()
async function listFollowing (req: express.Request, res: express.Response, next: express.NextFunction) {
const serverAccount = await getServerAccount()
- const resultList = await db.AccountFollow.listFollowingForApi(serverAccount.id, req.query.start, req.query.count, req.query.sort)
+ const resultList = await AccountFollowModel.listFollowingForApi(serverAccount.id, req.query.start, req.query.count, req.query.sort)
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
async function listFollowers (req: express.Request, res: express.Response, next: express.NextFunction) {
const serverAccount = await getServerAccount()
- const resultList = await db.AccountFollow.listFollowersForApi(serverAccount.id, req.query.start, req.query.count, req.query.sort)
+ const resultList = await AccountFollowModel.listFollowersForApi(serverAccount.id, req.query.start, req.query.count, req.query.sort)
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
return res.status(204).end()
}
-async function follow (fromAccount: AccountInstance, targetAccount: AccountInstance, targetAlreadyInDB: boolean) {
+async function follow (fromAccount: AccountModel, targetAccount: AccountModel, targetAlreadyInDB: boolean) {
try {
- await db.sequelize.transaction(async t => {
+ await sequelizeTypescript.transaction(async t => {
if (targetAlreadyInDB === false) {
await saveAccountAndServerIfNotExist(targetAccount, t)
}
- const [ accountFollow ] = await db.AccountFollow.findOrCreate({
+ const [ accountFollow ] = await AccountFollowModel.findOrCreate({
where: {
accountId: fromAccount.id,
targetAccountId: targetAccount.id
}
async function removeFollow (req: express.Request, res: express.Response, next: express.NextFunction) {
- const follow: AccountFollowInstance = res.locals.follow
+ const follow: AccountFollowModel = res.locals.follow
- await db.sequelize.transaction(async t => {
+ await sequelizeTypescript.transaction(async t => {
if (follow.state === 'accepted') await sendUndoFollow(follow, t)
await follow.destroy({ transaction: t })
async function loadLocalOrGetAccountFromWebfinger (name: string, host: string) {
let loadedFromDB = true
- let account = await db.Account.loadByNameAndHost(name, host)
+ let account = await AccountModel.loadByNameAndHost(name, host)
if (!account) {
const nameWithDomain = name + '@' + host
import * as express from 'express'
import { UserCreate, UserRight, UserRole, UserUpdate, UserUpdateMe, UserVideoRate as FormattedUserVideoRate } from '../../../shared'
import { getFormattedObjects, logger, retryTransactionWrapper } from '../../helpers'
-import { CONFIG, database as db } from '../../initializers'
+import { CONFIG } from '../../initializers'
import { createUserAccountAndChannel } from '../../lib'
import {
asyncMiddleware,
paginationValidator,
setPagination,
setUsersSort,
+ setVideosSort,
token,
usersAddValidator,
usersGetValidator,
usersUpdateValidator,
usersVideoRatingValidator
} from '../../middlewares'
-import { setVideosSort } from '../../middlewares/sort'
-import { videosSortValidator } from '../../middlewares/validators/sort'
-import { UserInstance } from '../../models'
+import { videosSortValidator } from '../../middlewares/validators'
+import { AccountVideoRateModel } from '../../models/account/account-video-rate'
+import { UserModel } from '../../models/account/user'
+import { VideoModel } from '../../models/video/video'
const usersRouter = express.Router()
// ---------------------------------------------------------------------------
async function getUserVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
- const user = res.locals.oauth.token.User
- const resultList = await db.Video.listUserVideosForApi(user.id ,req.query.start, req.query.count, req.query.sort)
+ const user = res.locals.oauth.token.User as UserModel
+ const resultList = await VideoModel.listUserVideosForApi(user.id ,req.query.start, req.query.count, req.query.sort)
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
async function createUser (req: express.Request) {
const body: UserCreate = req.body
- const user = db.User.build({
+ const user = new UserModel({
username: body.username,
password: body.password,
email: body.email,
async function registerUser (req: express.Request) {
const body: UserCreate = req.body
- const user = db.User.build({
+ const user = new UserModel({
username: body.username,
password: body.password,
email: body.email,
async function getUserInformation (req: express.Request, res: express.Response, next: express.NextFunction) {
// We did not load channels in res.locals.user
- const user = await db.User.loadByUsernameAndPopulateChannels(res.locals.oauth.token.user.username)
+ const user = await UserModel.loadByUsernameAndPopulateChannels(res.locals.oauth.token.user.username)
return res.json(user.toFormattedJSON())
}
const videoId = +req.params.videoId
const accountId = +res.locals.oauth.token.User.Account.id
- const ratingObj = await db.AccountVideoRate.load(accountId, videoId, null)
+ const ratingObj = await AccountVideoRateModel.load(accountId, videoId, null)
const rating = ratingObj ? ratingObj.type : 'none'
const json: FormattedUserVideoRate = {
}
async function listUsers (req: express.Request, res: express.Response, next: express.NextFunction) {
- const resultList = await db.User.listForApi(req.query.start, req.query.count, req.query.sort)
+ const resultList = await UserModel.listForApi(req.query.start, req.query.count, req.query.sort)
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
async function removeUser (req: express.Request, res: express.Response, next: express.NextFunction) {
- const user = await db.User.loadById(req.params.id)
+ const user = await UserModel.loadById(req.params.id)
await user.destroy()
async function updateUser (req: express.Request, res: express.Response, next: express.NextFunction) {
const body: UserUpdate = req.body
- const user: UserInstance = res.locals.user
+ const user = res.locals.user as UserModel
if (body.email !== undefined) user.email = body.email
if (body.videoQuota !== undefined) user.videoQuota = body.videoQuota
import * as express from 'express'
-
-import { database as db } from '../../../initializers/database'
import {
logger,
getFormattedObjects,
retryTransactionWrapper
} from '../../../helpers'
+import { sequelizeTypescript } from '../../../initializers'
import {
authenticate,
ensureUserHasRight,
setPagination,
asyncMiddleware
} from '../../../middlewares'
-import { VideoInstance } from '../../../models'
import { VideoAbuseCreate, UserRight } from '../../../../shared'
import { sendVideoAbuse } from '../../../lib/index'
+import { AccountModel } from '../../../models/account/account'
+import { VideoModel } from '../../../models/video/video'
+import { VideoAbuseModel } from '../../../models/video/video-abuse'
const abuseVideoRouter = express.Router()
// ---------------------------------------------------------------------------
async function listVideoAbuses (req: express.Request, res: express.Response, next: express.NextFunction) {
- const resultList = await db.VideoAbuse.listForApi(req.query.start, req.query.count, req.query.sort)
+ const resultList = await VideoAbuseModel.listForApi(req.query.start, req.query.count, req.query.sort)
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
}
async function reportVideoAbuse (req: express.Request, res: express.Response) {
- const videoInstance = res.locals.video as VideoInstance
- const reporterAccount = res.locals.oauth.token.User.Account
+ const videoInstance = res.locals.video as VideoModel
+ const reporterAccount = res.locals.oauth.token.User.Account as AccountModel
const body: VideoAbuseCreate = req.body
const abuseToCreate = {
videoId: videoInstance.id
}
- await db.sequelize.transaction(async t => {
- const videoAbuseInstance = await db.VideoAbuse.create(abuseToCreate, { transaction: t })
+ await sequelizeTypescript.transaction(async t => {
+ const videoAbuseInstance = await VideoAbuseModel.create(abuseToCreate, { transaction: t })
videoAbuseInstance.Video = videoInstance
// We send the video abuse to the origin server
import * as express from 'express'
-
-import { database as db } from '../../../initializers'
import { logger, getFormattedObjects } from '../../../helpers'
import {
authenticate,
setPagination,
asyncMiddleware
} from '../../../middlewares'
-import { BlacklistedVideoInstance } from '../../../models'
import { BlacklistedVideo, UserRight } from '../../../../shared'
+import { VideoBlacklistModel } from '../../../models/video/video-blacklist'
const blacklistRouter = express.Router()
videoId: videoInstance.id
}
- await db.BlacklistedVideo.create(toCreate)
+ await VideoBlacklistModel.create(toCreate)
return res.type('json').status(204).end()
}
async function listBlacklist (req: express.Request, res: express.Response, next: express.NextFunction) {
- const resultList = await db.BlacklistedVideo.listForApi(req.query.start, req.query.count, req.query.sort)
+ const resultList = await VideoBlacklistModel.listForApi(req.query.start, req.query.count, req.query.sort)
- return res.json(getFormattedObjects<BlacklistedVideo, BlacklistedVideoInstance>(resultList.data, resultList.total))
+ return res.json(getFormattedObjects<BlacklistedVideo, VideoBlacklistModel>(resultList.data, resultList.total))
}
async function removeVideoFromBlacklistController (req: express.Request, res: express.Response, next: express.NextFunction) {
- const blacklistedVideo = res.locals.blacklistedVideo as BlacklistedVideoInstance
+ const blacklistedVideo = res.locals.blacklistedVideo as VideoBlacklistModel
try {
await blacklistedVideo.destroy()
import * as express from 'express'
import { VideoChannelCreate, VideoChannelUpdate } from '../../../../shared'
import { getFormattedObjects, logger, resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers'
-import { database as db } from '../../../initializers'
+import { sequelizeTypescript } from '../../../initializers'
import { createVideoChannel } from '../../../lib'
import { sendUpdateVideoChannel } from '../../../lib/activitypub/send/send-update'
import {
videoChannelsSortValidator,
videoChannelsUpdateValidator
} from '../../../middlewares'
-import { AccountInstance, VideoChannelInstance } from '../../../models'
+import { AccountModel } from '../../../models/account/account'
+import { VideoChannelModel } from '../../../models/video/video-channel'
const videoChannelRouter = express.Router()
// ---------------------------------------------------------------------------
async function listVideoChannels (req: express.Request, res: express.Response, next: express.NextFunction) {
- const resultList = await db.VideoChannel.listForApi(req.query.start, req.query.count, req.query.sort)
+ const resultList = await VideoChannelModel.listForApi(req.query.start, req.query.count, req.query.sort)
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
async function listVideoAccountChannels (req: express.Request, res: express.Response, next: express.NextFunction) {
- const resultList = await db.VideoChannel.listByAccount(res.locals.account.id)
+ const resultList = await VideoChannelModel.listByAccount(res.locals.account.id)
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
async function addVideoChannel (req: express.Request, res: express.Response) {
const videoChannelInfo: VideoChannelCreate = req.body
- const account: AccountInstance = res.locals.oauth.token.User.Account
- let videoChannelCreated: VideoChannelInstance
+ const account: AccountModel = res.locals.oauth.token.User.Account
+ let videoChannelCreated: VideoChannelModel
- await db.sequelize.transaction(async t => {
+ await sequelizeTypescript.transaction(async t => {
videoChannelCreated = await createVideoChannel(videoChannelInfo, account, t)
})
}
async function updateVideoChannel (req: express.Request, res: express.Response) {
- const videoChannelInstance: VideoChannelInstance = res.locals.videoChannel
+ const videoChannelInstance = res.locals.videoChannel as VideoChannelModel
const videoChannelFieldsSave = videoChannelInstance.toJSON()
- const videoChannelInfoToUpdate: VideoChannelUpdate = req.body
+ const videoChannelInfoToUpdate = req.body as VideoChannelUpdate
try {
- await db.sequelize.transaction(async t => {
+ await sequelizeTypescript.transaction(async t => {
const sequelizeOptions = {
transaction: t
}
}
async function removeVideoChannel (req: express.Request, res: express.Response) {
- const videoChannelInstance: VideoChannelInstance = res.locals.videoChannel
+ const videoChannelInstance: VideoChannelModel = res.locals.videoChannel
- await db.sequelize.transaction(async t => {
+ await sequelizeTypescript.transaction(async t => {
await videoChannelInstance.destroy({ transaction: t })
})
}
async function getVideoChannel (req: express.Request, res: express.Response, next: express.NextFunction) {
- const videoChannelWithVideos = await db.VideoChannel.loadAndPopulateAccountAndVideos(res.locals.videoChannel.id)
+ const videoChannelWithVideos = await VideoChannelModel.loadAndPopulateAccountAndVideos(res.locals.videoChannel.id)
return res.json(videoChannelWithVideos.toFormattedJSON())
}
retryTransactionWrapper
} from '../../../helpers'
import { getServerAccount } from '../../../helpers/utils'
-import { CONFIG, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_MIMETYPE_EXT, VIDEO_PRIVACIES } from '../../../initializers'
-import { database as db } from '../../../initializers/database'
-import { sendAddVideo } from '../../../lib/activitypub/send/send-add'
-import { sendCreateViewToOrigin } from '../../../lib/activitypub/send/send-create'
-import { sendUpdateVideo } from '../../../lib/activitypub/send/send-update'
-import { shareVideoByServer } from '../../../lib/activitypub/share'
-import { getVideoActivityPubUrl } from '../../../lib/activitypub/url'
-import { fetchRemoteVideoDescription } from '../../../lib/activitypub/videos'
+import {
+ CONFIG,
+ sequelizeTypescript,
+ VIDEO_CATEGORIES,
+ VIDEO_LANGUAGES,
+ VIDEO_LICENCES,
+ VIDEO_MIMETYPE_EXT,
+ VIDEO_PRIVACIES
+} from '../../../initializers'
+import { fetchRemoteVideoDescription, getVideoActivityPubUrl, shareVideoByServer } from '../../../lib/activitypub'
+import { sendAddVideo, sendCreateViewToOrigin, sendUpdateVideo } from '../../../lib/activitypub/send'
import { sendCreateViewToVideoFollowers } from '../../../lib/index'
-import { transcodingJobScheduler } from '../../../lib/jobs/transcoding-job-scheduler/transcoding-job-scheduler'
+import { transcodingJobScheduler } from '../../../lib/jobs/transcoding-job-scheduler'
import {
asyncMiddleware,
authenticate,
videosSortValidator,
videosUpdateValidator
} from '../../../middlewares'
-import { VideoInstance } from '../../../models'
+import { TagModel } from '../../../models/video/tag'
+import { VideoModel } from '../../../models/video/video'
+import { VideoFileModel } from '../../../models/video/video-file'
import { abuseVideoRouter } from './abuse'
import { blacklistRouter } from './blacklist'
import { videoChannelRouter } from './channel'
videosRouter.post('/upload',
authenticate,
reqFiles,
- videosAddValidator,
+ asyncMiddleware(videosAddValidator),
asyncMiddleware(addVideoRetryWrapper)
)
duration: videoPhysicalFile['duration'], // duration was added by a previous middleware
channelId: res.locals.videoChannel.id
}
- const video = db.Video.build(videoData)
+ const video = new VideoModel(videoData)
video.url = getVideoActivityPubUrl(video)
const videoFilePath = join(CONFIG.STORAGE.VIDEOS_DIR, videoPhysicalFile.filename)
resolution: videoFileHeight,
size: videoPhysicalFile.size
}
- const videoFile = db.VideoFile.build(videoFileData)
+ const videoFile = new VideoFileModel(videoFileData)
const videoDir = CONFIG.STORAGE.VIDEOS_DIR
const source = join(videoDir, videoPhysicalFile.filename)
const destination = join(videoDir, video.getVideoFilename(videoFile))
)
await Promise.all(tasks)
- return db.sequelize.transaction(async t => {
+ return sequelizeTypescript.transaction(async t => {
const sequelizeOptions = { transaction: t }
if (CONFIG.TRANSCODING.ENABLED === true) {
video.VideoFiles = [ videoFile ]
if (videoInfo.tags) {
- const tagInstances = await db.Tag.findOrCreateTags(videoInfo.tags, t)
+ const tagInstances = await TagModel.findOrCreateTags(videoInfo.tags, t)
- await video.setTags(tagInstances, sequelizeOptions)
+ await video.$set('Tags', tagInstances, sequelizeOptions)
video.Tags = tagInstances
}
}
async function updateVideo (req: express.Request, res: express.Response) {
- const videoInstance: VideoInstance = res.locals.video
+ const videoInstance: VideoModel = res.locals.video
const videoFieldsSave = videoInstance.toJSON()
const videoInfoToUpdate: VideoUpdate = req.body
const wasPrivateVideo = videoInstance.privacy === VideoPrivacy.PRIVATE
try {
- await db.sequelize.transaction(async t => {
+ await sequelizeTypescript.transaction(async t => {
const sequelizeOptions = {
transaction: t
}
const videoInstanceUpdated = await videoInstance.save(sequelizeOptions)
if (videoInfoToUpdate.tags) {
- const tagInstances = await db.Tag.findOrCreateTags(videoInfoToUpdate.tags, t)
+ const tagInstances = await TagModel.findOrCreateTags(videoInfoToUpdate.tags, t)
- await videoInstance.setTags(tagInstances, sequelizeOptions)
+ await videoInstance.$set('Tags', tagInstances, sequelizeOptions)
videoInstance.Tags = tagInstances
}
}
async function listVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
- const resultList = await db.Video.listForApi(req.query.start, req.query.count, req.query.sort)
+ const resultList = await VideoModel.listForApi(req.query.start, req.query.count, req.query.sort)
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
}
async function removeVideo (req: express.Request, res: express.Response) {
- const videoInstance: VideoInstance = res.locals.video
+ const videoInstance: VideoModel = res.locals.video
- await db.sequelize.transaction(async t => {
+ await sequelizeTypescript.transaction(async t => {
await videoInstance.destroy({ transaction: t })
})
}
async function searchVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
- const resultList = await db.Video.searchAndPopulateAccountAndServerAndTags(
+ const resultList = await VideoModel.searchAndPopulateAccountAndServerAndTags(
req.query.search,
req.query.start,
req.query.count,
import * as express from 'express'
import { UserVideoRateUpdate } from '../../../../shared'
import { logger, retryTransactionWrapper } from '../../../helpers'
-import { VIDEO_RATE_TYPES } from '../../../initializers'
-import { database as db } from '../../../initializers/database'
-import { sendVideoRateChangeToFollowers, sendVideoRateChangeToOrigin } from '../../../lib/activitypub/videos'
+import { sequelizeTypescript, VIDEO_RATE_TYPES } from '../../../initializers'
+import { sendVideoRateChangeToFollowers, sendVideoRateChangeToOrigin } from '../../../lib/activitypub'
import { asyncMiddleware, authenticate, videoRateValidator } from '../../../middlewares'
-import { AccountInstance } from '../../../models/account/account-interface'
-import { VideoInstance } from '../../../models/video/video-interface'
+import { AccountModel } from '../../../models/account/account'
+import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
+import { VideoModel } from '../../../models/video/video'
const rateVideoRouter = express.Router()
async function rateVideo (req: express.Request, res: express.Response) {
const body: UserVideoRateUpdate = req.body
const rateType = body.rating
- const videoInstance: VideoInstance = res.locals.video
- const accountInstance: AccountInstance = res.locals.oauth.token.User.Account
+ const videoInstance: VideoModel = res.locals.video
+ const accountInstance: AccountModel = res.locals.oauth.token.User.Account
- await db.sequelize.transaction(async t => {
+ await sequelizeTypescript.transaction(async t => {
const sequelizeOptions = { transaction: t }
- const previousRate = await db.AccountVideoRate.load(accountInstance.id, videoInstance.id, t)
+ const previousRate = await AccountVideoRateModel.load(accountInstance.id, videoInstance.id, t)
let likesToIncrement = 0
let dislikesToIncrement = 0
type: rateType
}
- await db.AccountVideoRate.create(query, sequelizeOptions)
+ await AccountVideoRateModel.create(query, sequelizeOptions)
}
const incrementQuery = {
import { join } from 'path'
import * as validator from 'validator'
import * as Bluebird from 'bluebird'
-
-import { database as db } from '../initializers/database'
import {
CONFIG,
STATIC_PATHS,
} from '../initializers'
import { root, readFileBufferPromise, escapeHTML } from '../helpers'
import { asyncMiddleware } from '../middlewares'
-import { VideoInstance } from '../models'
+import { VideoModel } from '../models/video/video'
const clientsRouter = express.Router()
// ---------------------------------------------------------------------------
-function addOpenGraphAndOEmbedTags (htmlStringPage: string, video: VideoInstance) {
+function addOpenGraphAndOEmbedTags (htmlStringPage: string, video: VideoModel) {
const previewUrl = CONFIG.WEBSERVER.URL + STATIC_PATHS.PREVIEWS + video.getPreviewName()
const videoUrl = CONFIG.WEBSERVER.URL + '/videos/watch/' + video.uuid
async function generateWatchHtmlPage (req: express.Request, res: express.Response, next: express.NextFunction) {
const videoId = '' + req.params.id
- let videoPromise: Bluebird<VideoInstance>
+ let videoPromise: Bluebird<VideoModel>
// Let Angular application handle errors
if (validator.isUUID(videoId, 4)) {
- videoPromise = db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(videoId)
+ videoPromise = VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(videoId)
} else if (validator.isInt(videoId)) {
- videoPromise = db.Video.loadAndPopulateAccountAndServerAndTags(+videoId)
+ videoPromise = VideoModel.loadAndPopulateAccountAndServerAndTags(+videoId)
} else {
return res.sendFile(indexPath)
}
import * as express from 'express'
-
import { CONFIG, EMBED_SIZE, PREVIEWS_SIZE } from '../initializers'
-import { oembedValidator } from '../middlewares'
-import { asyncMiddleware } from '../middlewares/async'
-import { VideoInstance } from '../models'
+import { asyncMiddleware, oembedValidator } from '../middlewares'
+import { VideoModel } from '../models/video/video'
const servicesRouter = express.Router()
// ---------------------------------------------------------------------------
function generateOEmbed (req: express.Request, res: express.Response, next: express.NextFunction) {
- const video = res.locals.video as VideoInstance
+ const video = res.locals.video as VideoModel
const webserverUrl = CONFIG.WEBSERVER.URL
const maxHeight = parseInt(req.query.maxheight, 10)
const maxWidth = parseInt(req.query.maxwidth, 10)
import * as express from 'express'
import * as cors from 'cors'
-
import {
CONFIG,
STATIC_MAX_AGE,
import * as express from 'express'
-import { asyncMiddleware } from '../middlewares/async'
-import { webfingerValidator } from '../middlewares/validators/webfinger'
-import { AccountInstance } from '../models/account/account-interface'
+import { asyncMiddleware } from '../middlewares'
+import { webfingerValidator } from '../middlewares/validators'
+import { AccountModel } from '../models/account/account'
const webfingerRouter = express.Router()
// ---------------------------------------------------------------------------
function webfingerController (req: express.Request, res: express.Response, next: express.NextFunction) {
- const account: AccountInstance = res.locals.account
+ const account = res.locals.account as AccountModel
const json = {
subject: req.query.resource,
-import { Activity } from '../../shared/models/activitypub/activity'
-import { ResultList } from '../../shared/models/result-list.model'
-import { AccountInstance } from '../models/account/account-interface'
+import { ResultList } from '../../shared/models'
+import { Activity } from '../../shared/models/activitypub'
+import { ACTIVITY_PUB } from '../initializers'
+import { AccountModel } from '../models/account/account'
import { signObject } from './peertube-crypto'
-import { ACTIVITY_PUB } from '../initializers/constants'
function activityPubContextify <T> (data: T) {
return Object.assign(data,{
return orderedCollectionPagination
}
-function buildSignedActivity (byAccount: AccountInstance, data: Object) {
+function buildSignedActivity (byAccount: AccountModel, data: Object) {
const activity = activityPubContextify(data)
return signObject(byAccount, activity) as Promise<Activity>
import { Response } from 'express'
import 'express-validator'
import * as validator from 'validator'
-import { database as db } from '../../initializers'
-import { AccountInstance } from '../../models'
+import { AccountModel } from '../../models/account/account'
import { isUserUsernameValid } from './users'
function isAccountNameValid (value: string) {
}
function isAccountIdExist (id: number | string, res: Response) {
- let promise: Bluebird<AccountInstance>
+ let promise: Bluebird<AccountModel>
if (validator.isInt('' + id)) {
- promise = db.Account.load(+id)
+ promise = AccountModel.load(+id)
} else { // UUID
- promise = db.Account.loadByUUID('' + id)
+ promise = AccountModel.loadByUUID('' + id)
}
return isAccountExist(promise, res)
}
function isLocalAccountNameExist (name: string, res: Response) {
- const promise = db.Account.loadLocalByName(name)
+ const promise = AccountModel.loadLocalByName(name)
return isAccountExist(promise, res)
}
-async function isAccountExist (p: Bluebird<AccountInstance>, res: Response) {
+async function isAccountExist (p: Bluebird<AccountModel>, res: Response) {
const account = await p
if (!account) {
import * as validator from 'validator'
-import { CONSTRAINTS_FIELDS } from '../../../initializers/constants'
+import { CONSTRAINTS_FIELDS } from '../../../initializers'
import { isAccountNameValid } from '../accounts'
import { exists, isUUIDValid } from '../misc'
import { isActivityPubUrlValid, isBaseActivityValid } from './misc'
import * as validator from 'validator'
-import { Activity, ActivityType } from '../../../../shared/models/activitypub/activity'
+import { Activity, ActivityType } from '../../../../shared/models/activitypub'
import { isAccountAcceptActivityValid, isAccountDeleteActivityValid, isAccountFollowActivityValid } from './account'
import { isAnnounceActivityValid } from './announce'
import { isActivityPubUrlValid } from './misc'
+import { isDislikeActivityValid, isLikeActivityValid } from './rate'
import { isUndoActivityValid } from './undo'
import { isVideoChannelCreateActivityValid, isVideoChannelDeleteActivityValid, isVideoChannelUpdateActivityValid } from './video-channels'
import {
isVideoTorrentUpdateActivityValid
} from './videos'
import { isViewActivityValid } from './view'
-import { isDislikeActivityValid, isLikeActivityValid } from './rate'
function isRootActivityValid (activity: any) {
return Array.isArray(activity['@context']) &&
import * as validator from 'validator'
-import { exists } from '../misc'
+import { CONSTRAINTS_FIELDS } from '../../../initializers'
import { isTestInstance } from '../../core-utils'
-import { CONSTRAINTS_FIELDS } from '../../../initializers/constants'
+import { exists } from '../misc'
function isActivityPubUrlValid (url: string) {
const isURLOptions = {
+++ /dev/null
-export * from './activitypub'
-export * from './misc'
-export * from './servers'
-export * from './servers'
-export * from './users'
-export * from './accounts'
-export * from './video-channels'
-export * from './videos'
-export * from './webfinger'
import 'express-validator'
import 'multer'
import * as validator from 'validator'
-import { CONSTRAINTS_FIELDS, database as db } from '../../initializers'
-import { VideoChannelInstance } from '../../models'
+import { CONSTRAINTS_FIELDS } from '../../initializers'
+import { VideoChannelModel } from '../../models/video/video-channel'
import { exists } from './misc'
const VIDEO_CHANNELS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_CHANNELS
}
async function isVideoChannelExist (id: string, res: express.Response) {
- let videoChannel: VideoChannelInstance
+ let videoChannel: VideoChannelModel
if (validator.isInt(id)) {
- videoChannel = await db.VideoChannel.loadAndPopulateAccount(+id)
+ videoChannel = await VideoChannelModel.loadAndPopulateAccount(+id)
} else { // UUID
- videoChannel = await db.VideoChannel.loadByUUIDAndPopulateAccount(id)
+ videoChannel = await VideoChannelModel.loadByUUIDAndPopulateAccount(id)
}
if (!videoChannel) {
import 'multer'
import * as validator from 'validator'
import { VideoRateType } from '../../../shared'
-import { CONSTRAINTS_FIELDS, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_RATE_TYPES } from '../../initializers'
-import { VIDEO_PRIVACIES } from '../../initializers/constants'
-import { database as db } from '../../initializers/database'
-import { VideoInstance } from '../../models/video/video-interface'
+import {
+ CONSTRAINTS_FIELDS,
+ VIDEO_CATEGORIES,
+ VIDEO_LANGUAGES,
+ VIDEO_LICENCES,
+ VIDEO_PRIVACIES,
+ VIDEO_RATE_TYPES
+} from '../../initializers'
+import { VideoModel } from '../../models/video/video'
import { exists, isArray } from './misc'
const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS
}
async function isVideoExist (id: string, res: Response) {
- let video: VideoInstance
+ let video: VideoModel
if (validator.isInt(id)) {
- video = await db.Video.loadAndPopulateAccountAndServerAndTags(+id)
+ video = await VideoModel.loadAndPopulateAccountAndServerAndTags(+id)
} else { // UUID
- video = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(id)
+ video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(id)
}
if (!video) {
-import 'express-validator'
-import 'multer'
-import { CONFIG } from '../../initializers/constants'
+import { CONFIG } from '../../initializers'
import { exists } from './misc'
function isWebfingerResourceValid (value: string) {
const host = accountParts[1]
- if (host !== CONFIG.WEBSERVER.HOST) return false
-
- return true
+ return host === CONFIG.WEBSERVER.HOST
}
// ---------------------------------------------------------------------------
import * as ffmpeg from 'fluent-ffmpeg'
-import { VideoResolution } from '../../shared/models/videos/video-resolution.enum'
+import { VideoResolution } from '../../shared/models/videos'
import { CONFIG } from '../initializers'
function getVideoFileHeight (path: string) {
export * from './activitypub'
export * from './core-utils'
export * from './logger'
-export * from './custom-validators'
export * from './ffmpeg-utils'
export * from './database-utils'
export * from './peertube-crypto'
import * as mkdirp from 'mkdirp'
import * as path from 'path'
import * as winston from 'winston'
-import { CONFIG } from '../initializers/constants'
+import { CONFIG } from '../initializers'
const label = CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT
import { BCRYPT_SALT_SIZE, PRIVATE_RSA_KEY_SIZE } from '../initializers'
-import { AccountInstance } from '../models/account/account-interface'
+import { AccountModel } from '../models/account/account'
import { bcryptComparePromise, bcryptGenSaltPromise, bcryptHashPromise, createPrivateKey, getPublicKey } from './core-utils'
import { jsig } from './custom-jsonld-signature'
import { logger } from './logger'
return { privateKey: key, publicKey }
}
-function isSignatureVerified (fromAccount: AccountInstance, signedDocument: object) {
+function isSignatureVerified (fromAccount: AccountModel, signedDocument: object) {
const publicKeyObject = {
'@context': jsig.SECURITY_CONTEXT_URL,
'@id': fromAccount.url,
})
}
-function signObject (byAccount: AccountInstance, data: any) {
+function signObject (byAccount: AccountModel, data: any) {
const options = {
privateKeyPem: byAccount.privateKey,
creator: byAccount.url
import * as express from 'express'
-import * as Sequelize from 'sequelize'
+import { Model } from 'sequelize-typescript'
import { ResultList } from '../../shared'
-import { VideoResolution } from '../../shared/models/videos/video-resolution.enum'
-import { CONFIG, database as db } from '../initializers'
-import { AccountInstance } from '../models/account/account-interface'
+import { VideoResolution } from '../../shared/models/videos'
+import { CONFIG } from '../initializers'
+import { AccountModel } from '../models/account/account'
+import { UserModel } from '../models/account/user'
import { pseudoRandomBytesPromise } from './core-utils'
import { logger } from './logger'
return true
}
- const totalUsers = await db.User.countTotal()
+ const totalUsers = await UserModel.countTotal()
return totalUsers < CONFIG.SIGNUP.LIMIT
}
return resolutionsEnabled
}
-function resetSequelizeInstance (instance: Sequelize.Instance<any>, savedFields: object) {
+function resetSequelizeInstance (instance: Model<any>, savedFields: object) {
Object.keys(savedFields).forEach(key => {
const value = savedFields[key]
instance.set(key, value)
})
}
-let serverAccount: AccountInstance
+let serverAccount: AccountModel
async function getServerAccount () {
if (serverAccount === undefined) {
- serverAccount = await db.Account.loadApplication()
+ serverAccount = await AccountModel.loadApplication()
}
if (!serverAccount) {
import * as WebFinger from 'webfinger.js'
import { WebFingerData } from '../../shared'
-import { fetchRemoteAccount } from '../lib/activitypub/account'
+import { fetchRemoteAccount } from '../lib/activitypub'
import { isTestInstance } from './core-utils'
-import { isActivityPubUrlValid } from './custom-validators'
+import { isActivityPubUrlValid } from './custom-validators/activitypub'
const webfinger = new WebFinger({
webfist_fallback: false,
import * as config from 'config'
-import { promisify0 } from '../helpers/core-utils'
-import { UserModel } from '../models/account/user-interface'
-import { ApplicationModel } from '../models/application/application-interface'
-import { OAuthClientModel } from '../models/oauth/oauth-client-interface'
+import { promisify0 } from '../helpers'
+import { UserModel } from '../models/account/user'
+import { ApplicationModel } from '../models/application/application'
+import { OAuthClientModel } from '../models/oauth/oauth-client'
// Some checks on configuration files
function checkConfig () {
}
// We get db by param to not import it in this file (import orders)
-async function clientsExist (OAuthClient: OAuthClientModel) {
- const totalClients = await OAuthClient.countTotal()
+async function clientsExist () {
+ const totalClients = await OAuthClientModel.countTotal()
return totalClients !== 0
}
// We get db by param to not import it in this file (import orders)
-async function usersExist (User: UserModel) {
- const totalUsers = await User.countTotal()
+async function usersExist () {
+ const totalUsers = await UserModel.countTotal()
return totalUsers !== 0
}
// We get db by param to not import it in this file (import orders)
-async function applicationExist (Application: ApplicationModel) {
- const totalApplication = await Application.countTotal()
+async function applicationExist () {
+ const totalApplication = await ApplicationModel.countTotal()
return totalApplication !== 0
}
import * as config from 'config'
import { join } from 'path'
-
+import { JobCategory, JobState, VideoRateType } from '../../shared/models'
+import { FollowState } from '../../shared/models/accounts'
+import { VideoPrivacy } from '../../shared/models/videos'
// Do not use barrels, remain constants as independent as possible
-import { root, isTestInstance } from '../helpers/core-utils'
-
-import {
- VideoRateType,
- JobState,
- JobCategory
-} from '../../shared/models'
-import { VideoPrivacy } from '../../shared/models/videos/video-privacy.enum'
-import { FollowState } from '../../shared/models/accounts/follow.model'
+import { isTestInstance, root } from '../helpers/core-utils'
// ---------------------------------------------------------------------------
-import { join } from 'path'
-import { flattenDepth } from 'lodash'
-require('pg').defaults.parseInt8 = true // Avoid BIGINT to be converted to string
-import * as Sequelize from 'sequelize'
-import { AvatarModel } from '../models/avatar'
+import { Sequelize as SequelizeTypescript } from 'sequelize-typescript'
+import { isTestInstance } from '../helpers/core-utils'
+import { logger } from '../helpers/logger'
+import { AccountModel } from '../models/account/account'
+import { AccountFollowModel } from '../models/account/account-follow'
+import { AccountVideoRateModel } from '../models/account/account-video-rate'
+import { UserModel } from '../models/account/user'
+import { ApplicationModel } from '../models/application/application'
+import { AvatarModel } from '../models/avatar/avatar'
+import { JobModel } from '../models/job/job'
+import { OAuthClientModel } from '../models/oauth/oauth-client'
+import { OAuthTokenModel } from '../models/oauth/oauth-token'
+import { ServerModel } from '../models/server/server'
+import { TagModel } from '../models/video/tag'
+import { VideoModel } from '../models/video/video'
+import { VideoAbuseModel } from '../models/video/video-abuse'
+import { VideoBlacklistModel } from '../models/video/video-blacklist'
+import { VideoChannelModel } from '../models/video/video-channel'
+import { VideoChannelShareModel } from '../models/video/video-channel-share'
+import { VideoFileModel } from '../models/video/video-file'
+import { VideoShareModel } from '../models/video/video-share'
+import { VideoTagModel } from '../models/video/video-tag'
import { CONFIG } from './constants'
-// Do not use barrel, we need to load database first
-import { logger } from '../helpers/logger'
-import { isTestInstance, readdirPromise } from '../helpers/core-utils'
-import { VideoModel } from './../models/video/video-interface'
-import { VideoTagModel } from './../models/video/video-tag-interface'
-import { BlacklistedVideoModel } from './../models/video/video-blacklist-interface'
-import { VideoFileModel } from './../models/video/video-file-interface'
-import { VideoAbuseModel } from './../models/video/video-abuse-interface'
-import { VideoChannelModel } from './../models/video/video-channel-interface'
-import { UserModel } from '../models/account/user-interface'
-import { AccountVideoRateModel } from '../models/account/account-video-rate-interface'
-import { AccountFollowModel } from '../models/account/account-follow-interface'
-import { TagModel } from './../models/video/tag-interface'
-import { ServerModel } from '../models/server/server-interface'
-import { OAuthTokenModel } from './../models/oauth/oauth-token-interface'
-import { OAuthClientModel } from './../models/oauth/oauth-client-interface'
-import { JobModel } from './../models/job/job-interface'
-import { AccountModel } from './../models/account/account-interface'
-import { ApplicationModel } from './../models/application/application-interface'
-import { VideoChannelShareModel } from '../models/video/video-channel-share-interface'
-import { VideoShareModel } from '../models/video/video-share-interface'
+require('pg').defaults.parseInt8 = true // Avoid BIGINT to be converted to string
const dbname = CONFIG.DATABASE.DBNAME
const username = CONFIG.DATABASE.USERNAME
const password = CONFIG.DATABASE.PASSWORD
-export type PeerTubeDatabase = {
- sequelize?: Sequelize.Sequelize,
- init?: (silent: boolean) => Promise<void>,
-
- Application?: ApplicationModel,
- Avatar?: AvatarModel,
- Account?: AccountModel,
- Job?: JobModel,
- OAuthClient?: OAuthClientModel,
- OAuthToken?: OAuthTokenModel,
- Server?: ServerModel,
- Tag?: TagModel,
- AccountVideoRate?: AccountVideoRateModel,
- AccountFollow?: AccountFollowModel,
- User?: UserModel,
- VideoAbuse?: VideoAbuseModel,
- VideoChannel?: VideoChannelModel,
- VideoChannelShare?: VideoChannelShareModel,
- VideoShare?: VideoShareModel,
- VideoFile?: VideoFileModel,
- BlacklistedVideo?: BlacklistedVideoModel,
- VideoTag?: VideoTagModel,
- Video?: VideoModel
-}
-
-const database: PeerTubeDatabase = {}
-
-const sequelize = new Sequelize(dbname, username, password, {
+const sequelizeTypescript = new SequelizeTypescript({
+ database: dbname,
dialect: 'postgres',
- host: CONFIG.DATABASE.HOSTNAME,
- port: CONFIG.DATABASE.PORT,
+ username,
+ password,
+ modelPaths: [__dirname + '/models'],
benchmark: isTestInstance(),
- isolationLevel: Sequelize.Transaction.ISOLATION_LEVELS.SERIALIZABLE,
+ isolationLevel: SequelizeTypescript.Transaction.ISOLATION_LEVELS.SERIALIZABLE,
operatorsAliases: false,
-
logging: (message: string, benchmark: number) => {
if (process.env.NODE_DB_LOG === 'false') return
}
})
-database.sequelize = sequelize
-
-database.init = async (silent: boolean) => {
- const modelDirectory = join(__dirname, '..', 'models')
-
- const filePaths = await getModelFiles(modelDirectory)
-
- for (const filePath of filePaths) {
- try {
- const model = sequelize.import(filePath)
-
- database[model['name']] = model
- } catch (err) {
- logger.error('Cannot import database model %s.', filePath, err)
- process.exit(0)
- }
- }
-
- for (const modelName of Object.keys(database)) {
- if ('associate' in database[modelName]) {
- try {
- database[modelName].associate(database)
- } catch (err) {
- logger.error('Cannot associate model %s.', modelName, err)
- process.exit(0)
- }
- }
- }
+async function initDatabase (silent: boolean) {
+ sequelizeTypescript.addModels([
+ ApplicationModel,
+ AvatarModel,
+ AccountModel,
+ JobModel,
+ OAuthClientModel,
+ OAuthTokenModel,
+ ServerModel,
+ TagModel,
+ AccountVideoRateModel,
+ AccountFollowModel,
+ UserModel,
+ VideoAbuseModel,
+ VideoChannelModel,
+ VideoChannelShareModel,
+ VideoShareModel,
+ VideoFileModel,
+ VideoBlacklistModel,
+ VideoTagModel,
+ VideoModel
+ ])
if (!silent) logger.info('Database %s is ready.', dbname)
// ---------------------------------------------------------------------------
export {
- database
-}
-
-// ---------------------------------------------------------------------------
-
-async function getModelFiles (modelDirectory: string) {
- const files = await readdirPromise(modelDirectory)
- const directories = files.filter(directory => {
- // Find directories
- if (
- directory.endsWith('.js.map') ||
- directory === 'index.js' || directory === 'index.ts' ||
- directory === 'utils.js' || directory === 'utils.ts'
- ) return false
-
- return true
- })
-
- const tasks: Promise<any>[] = []
-
- // For each directory we read it and append model in the modelFilePaths array
- for (const directory of directories) {
- const modelDirectoryPath = join(modelDirectory, directory)
-
- const promise = readdirPromise(modelDirectoryPath)
- .then(files => {
- const filteredFiles = files
- .filter(file => {
- if (
- file === 'index.js' || file === 'index.ts' ||
- file === 'utils.js' || file === 'utils.ts' ||
- file.endsWith('-interface.js') || file.endsWith('-interface.ts') ||
- file.endsWith('.js.map')
- ) return false
-
- return true
- })
- .map(file => join(modelDirectoryPath, file))
-
- return filteredFiles
- })
-
- tasks.push(promise)
- }
-
- const filteredFilesArray: string[][] = await Promise.all(tasks)
- return flattenDepth<string>(filteredFilesArray, 1)
+ initDatabase,
+ sequelizeTypescript
}
import * as passwordGenerator from 'password-generator'
import { UserRole } from '../../shared'
-import { logger, mkdirpPromise, rimrafPromise } from '../helpers'
-import { createUserAccountAndChannel } from '../lib'
-import { createLocalAccountWithoutKeys } from '../lib/user'
+import { createPrivateAndPublicKeys, logger, mkdirpPromise, rimrafPromise } from '../helpers'
+import { createLocalAccountWithoutKeys, createUserAccountAndChannel } from '../lib'
+import { UserModel } from '../models/account/user'
+import { ApplicationModel } from '../models/application/application'
+import { OAuthClientModel } from '../models/oauth/oauth-client'
import { applicationExist, clientsExist, usersExist } from './checker'
import { CACHE, CONFIG, LAST_MIGRATION_VERSION, SERVER_ACCOUNT_NAME } from './constants'
-import { database as db } from './database'
-import { createPrivateAndPublicKeys } from '../helpers/peertube-crypto'
+import { sequelizeTypescript } from './database'
async function installApplication () {
try {
- await db.sequelize.sync()
+ await sequelizeTypescript.sync()
await removeCacheDirectories()
await createDirectoriesIfNotExist()
await createApplicationIfNotExist()
}
async function createOAuthClientIfNotExist () {
- const exist = await clientsExist(db.OAuthClient)
+ const exist = await clientsExist()
// Nothing to do, clients already exist
if (exist === true) return undefined
const id = passwordGenerator(32, false, /[a-z0-9]/)
const secret = passwordGenerator(32, false, /[a-zA-Z0-9]/)
- const client = db.OAuthClient.build({
+ const client = new OAuthClientModel({
clientId: id,
clientSecret: secret,
grants: [ 'password', 'refresh_token' ],
}
async function createOAuthAdminIfNotExist () {
- const exist = await usersExist(db.User)
+ const exist = await usersExist()
// Nothing to do, users already exist
if (exist === true) return undefined
role,
videoQuota: -1
}
- const user = db.User.build(userData)
+ const user = new UserModel(userData)
await createUserAccountAndChannel(user, validatePassword)
logger.info('Username: ' + username)
}
async function createApplicationIfNotExist () {
- const exist = await applicationExist(db.Application)
+ const exist = await applicationExist()
// Nothing to do, application already exist
if (exist === true) return undefined
logger.info('Creating Application table.')
- const applicationInstance = await db.Application.create({ migrationVersion: LAST_MIGRATION_VERSION })
+ const applicationInstance = await ApplicationModel.create({ migrationVersion: LAST_MIGRATION_VERSION })
logger.info('Creating application account.')
import * as Sequelize from 'sequelize'
import * as Promise from 'bluebird'
import { stat } from 'fs'
-
-import { VideoInstance } from '../../models'
+import { VideoModel } from '../../models/video/video'
function up (utils: {
transaction: Sequelize.Transaction,
db: any
}): Promise<void> {
return utils.db.Video.listOwnedAndPopulateAuthorAndTags()
- .then((videos: VideoInstance[]) => {
+ .then((videos: VideoModel[]) => {
const tasks: Promise<any>[] = []
videos.forEach(video => {
import { shareVideoByServer } from '../../lib/activitypub/share'
import { getVideoActivityPubUrl, getVideoChannelActivityPubUrl } from '../../lib/activitypub/url'
import { createLocalAccountWithoutKeys } from '../../lib/user'
+import { ApplicationModel } from '../../models/application/application'
import { JOB_CATEGORIES, SERVER_ACCOUNT_NAME } from '../constants'
-import { PeerTubeDatabase } from '../database'
async function up (utils: {
transaction: Sequelize.Transaction,
queryInterface: Sequelize.QueryInterface,
sequelize: Sequelize.Sequelize,
- db: PeerTubeDatabase
+ db: any
}): Promise<void> {
const q = utils.queryInterface
const db = utils.db
// Create application account
{
- const applicationInstance = await db.Application.findOne()
+ const applicationInstance = await ApplicationModel.findOne()
const accountCreated = await createLocalAccountWithoutKeys(SERVER_ACCOUNT_NAME, null, applicationInstance.id, undefined)
const { publicKey, privateKey } = await createPrivateAndPublicKeys()
import * as Sequelize from 'sequelize'
-import { PeerTubeDatabase } from '../database'
async function up (utils: {
transaction: Sequelize.Transaction,
queryInterface: Sequelize.QueryInterface,
sequelize: Sequelize.Sequelize,
- db: PeerTubeDatabase
+ db: any
}): Promise<void> {
await utils.queryInterface.removeColumn('Servers', 'email')
}
import * as Sequelize from 'sequelize'
-import { PeerTubeDatabase } from '../database'
async function up (utils: {
transaction: Sequelize.Transaction,
queryInterface: Sequelize.QueryInterface,
sequelize: Sequelize.Sequelize,
- db: PeerTubeDatabase
+ db: any
}): Promise<void> {
await utils.queryInterface.removeColumn('Servers', 'publicKey')
}
import * as Sequelize from 'sequelize'
-import { PeerTubeDatabase } from '../database'
async function up (utils: {
transaction: Sequelize.Transaction,
queryInterface: Sequelize.QueryInterface,
sequelize: Sequelize.Sequelize,
- db: PeerTubeDatabase
+ db: any
}): Promise<void> {
await utils.db.Avatar.sync()
import * as Sequelize from 'sequelize'
import { CONSTRAINTS_FIELDS } from '../constants'
-import { PeerTubeDatabase } from '../database'
async function up (utils: {
transaction: Sequelize.Transaction,
queryInterface: Sequelize.QueryInterface,
sequelize: Sequelize.Sequelize,
- db: PeerTubeDatabase
+ db: any
}): Promise<void> {
{
import * as path from 'path'
-
-import { database as db } from './database'
-import { LAST_MIGRATION_VERSION } from './constants'
import { logger, readdirPromise } from '../helpers'
+import { ApplicationModel } from '../models/application/application'
+import { LAST_MIGRATION_VERSION } from './constants'
+import { sequelizeTypescript } from './database'
async function migrate () {
- const tables = await db.sequelize.getQueryInterface().showAllTables()
+ const tables = await sequelizeTypescript.getQueryInterface().showAllTables()
// No tables, we don't need to migrate anything
// The installer will do that
if (tables.length === 0) return
- let actualVersion = await db.Application.loadMigrationVersion()
+ let actualVersion = await ApplicationModel.loadMigrationVersion()
if (actualVersion === null) {
- await db.Application.create({ migrationVersion: 0 })
+ await ApplicationModel.create({ migrationVersion: 0 })
actualVersion = 0
}
const migrationScript = require(path.join(__dirname, 'migrations', migrationScriptName))
- await db.sequelize.transaction(async t => {
+ await sequelizeTypescript.transaction(async t => {
const options = {
transaction: t,
- queryInterface: db.sequelize.getQueryInterface(),
- sequelize: db.sequelize,
- db
+ queryInterface: sequelizeTypescript.getQueryInterface(),
+ sequelize: sequelizeTypescript
}
await migrationScript.up(options)
// Update the new migration version
- await db.Application.updateMigrationVersion(versionScript, t)
+ await ApplicationModel.updateMigrationVersion(versionScript, t)
})
}
import * as Bluebird from 'bluebird'
-import * as url from 'url'
-import { ActivityPubActor } from '../../../shared/models/activitypub/activitypub-actor'
-import { isRemoteAccountValid } from '../../helpers/custom-validators/activitypub/account'
-import { retryTransactionWrapper } from '../../helpers/database-utils'
-import { logger } from '../../helpers/logger'
-import { doRequest } from '../../helpers/requests'
-import { ACTIVITY_PUB } from '../../initializers/constants'
-import { database as db } from '../../initializers/database'
-import { AccountInstance } from '../../models/account/account-interface'
import { Transaction } from 'sequelize'
+import * as url from 'url'
+import { ActivityPubActor } from '../../../shared/models/activitypub'
+import { doRequest, logger, retryTransactionWrapper } from '../../helpers'
+import { isRemoteAccountValid } from '../../helpers/custom-validators/activitypub'
+import { ACTIVITY_PUB, sequelizeTypescript } from '../../initializers'
+import { AccountModel } from '../../models/account/account'
+import { ServerModel } from '../../models/server/server'
async function getOrCreateAccountAndServer (accountUrl: string) {
- let account = await db.Account.loadByUrl(accountUrl)
+ let account = await AccountModel.loadByUrl(accountUrl)
// We don't have this account in our database, fetch it on remote
if (!account) {
return account
}
-function saveAccountAndServerIfNotExist (account: AccountInstance, t?: Transaction): Bluebird<AccountInstance> | Promise<AccountInstance> {
+function saveAccountAndServerIfNotExist (account: AccountModel, t?: Transaction): Bluebird<AccountModel> | Promise<AccountModel> {
if (t !== undefined) {
return save(t)
} else {
- return db.sequelize.transaction(t => {
+ return sequelizeTypescript.transaction(t => {
return save(t)
})
}
},
transaction: t
}
- const [ server ] = await db.Server.findOrCreate(serverOptions)
+ const [ server ] = await ServerModel.findOrCreate(serverOptions)
// Save our new account in database
account.set('serverId', server.id)
const followersCount = await fetchAccountCount(accountJSON.followers)
const followingCount = await fetchAccountCount(accountJSON.following)
- const account = db.Account.build({
+ return new AccountModel({
uuid: accountJSON.uuid,
name: accountJSON.preferredUsername,
url: accountJSON.url,
followersUrl: accountJSON.followers,
followingUrl: accountJSON.following
})
-
- return account
}
export {
import { Transaction } from 'sequelize'
-import { AccountInstance } from '../../models/account/account-interface'
-import { activitypubHttpJobScheduler, ActivityPubHttpPayload } from '../jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler'
+import { AccountModel } from '../../models/account/account'
+import { activitypubHttpJobScheduler, ActivityPubHttpPayload } from '../jobs/activitypub-http-job-scheduler'
-async function addFetchOutboxJob (account: AccountInstance, t: Transaction) {
+async function addFetchOutboxJob (account: AccountModel, t: Transaction) {
const jobPayload: ActivityPubHttpPayload = {
uris: [ account.outboxUrl ]
}
import * as magnetUtil from 'magnet-uri'
import { VideoTorrentObject } from '../../../../shared'
-import { VideoChannelObject } from '../../../../shared/models/activitypub/objects/video-channel-object'
-import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum'
+import { VideoChannelObject } from '../../../../shared/models/activitypub/objects'
+import { VideoPrivacy } from '../../../../shared/models/videos'
+import { doRequest } from '../../../helpers'
import { isVideoFileInfoHashValid } from '../../../helpers/custom-validators/videos'
-import { doRequest } from '../../../helpers/requests'
-import { database as db } from '../../../initializers'
-import { ACTIVITY_PUB, VIDEO_MIMETYPE_EXT } from '../../../initializers/constants'
-import { AccountInstance } from '../../../models/account/account-interface'
-import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
-import { VideoFileAttributes } from '../../../models/video/video-file-interface'
-import { VideoAttributes, VideoInstance } from '../../../models/video/video-interface'
+import { ACTIVITY_PUB, VIDEO_MIMETYPE_EXT } from '../../../initializers'
+import { AccountModel } from '../../../models/account/account'
+import { VideoModel } from '../../../models/video/video'
+import { VideoChannelModel } from '../../../models/video/video-channel'
+import { VideoChannelShareModel } from '../../../models/video/video-channel-share'
+import { VideoShareModel } from '../../../models/video/video-share'
import { getOrCreateAccountAndServer } from '../account'
-function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChannelObject, account: AccountInstance) {
+function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChannelObject, account: AccountModel) {
return {
name: videoChannelObject.name,
description: videoChannelObject.content,
}
async function videoActivityObjectToDBAttributes (
- videoChannel: VideoChannelInstance,
+ videoChannel: VideoChannelModel,
videoObject: VideoTorrentObject,
to: string[] = [],
cc: string[] = []
description = videoObject.content
}
- const videoData: VideoAttributes = {
+ return {
name: videoObject.name,
uuid: videoObject.uuid,
url: videoObject.id,
remote: true,
privacy
}
-
- return videoData
}
-function videoFileActivityUrlToDBAttributes (videoCreated: VideoInstance, videoObject: VideoTorrentObject) {
+function videoFileActivityUrlToDBAttributes (videoCreated: VideoModel, videoObject: VideoTorrentObject) {
const mimeTypes = Object.keys(VIDEO_MIMETYPE_EXT)
const fileUrls = videoObject.url.filter(u => {
return mimeTypes.indexOf(u.mimeType) !== -1 && u.mimeType.startsWith('video/')
throw new Error('Cannot find video files for ' + videoCreated.url)
}
- const attributes: VideoFileAttributes[] = []
+ const attributes = []
for (const fileUrl of fileUrls) {
// Fetch associated magnet uri
const magnet = videoObject.url.find(u => {
return attributes
}
-async function addVideoShares (instance: VideoInstance, shares: string[]) {
+async function addVideoShares (instance: VideoModel, shares: string[]) {
for (const share of shares) {
// Fetch url
const json = await doRequest({
videoId: instance.id
}
- await db.VideoShare.findOrCreate({
+ await VideoShareModel.findOrCreate({
where: entry,
defaults: entry
})
}
}
-async function addVideoChannelShares (instance: VideoChannelInstance, shares: string[]) {
+async function addVideoChannelShares (instance: VideoChannelModel, shares: string[]) {
for (const share of shares) {
// Fetch url
const json = await doRequest({
videoChannelId: instance.id
}
- await db.VideoChannelShare.findOrCreate({
+ await VideoChannelShareModel.findOrCreate({
where: entry,
defaults: entry
})
-import { ActivityAccept } from '../../../../shared/models/activitypub/activity'
-import { database as db } from '../../../initializers'
-import { AccountInstance } from '../../../models/account/account-interface'
+import { ActivityAccept } from '../../../../shared/models/activitypub'
+import { AccountModel } from '../../../models/account/account'
+import { AccountFollowModel } from '../../../models/account/account-follow'
import { addFetchOutboxJob } from '../fetch'
-async function processAcceptActivity (activity: ActivityAccept, inboxAccount?: AccountInstance) {
+async function processAcceptActivity (activity: ActivityAccept, inboxAccount?: AccountModel) {
if (inboxAccount === undefined) throw new Error('Need to accept on explicit inbox.')
- const targetAccount = await db.Account.loadByUrl(activity.actor)
+ const targetAccount = await AccountModel.loadByUrl(activity.actor)
return processAccept(inboxAccount, targetAccount)
}
// ---------------------------------------------------------------------------
-async function processAccept (account: AccountInstance, targetAccount: AccountInstance) {
- const follow = await db.AccountFollow.loadByAccountAndTarget(account.id, targetAccount.id)
+async function processAccept (account: AccountModel, targetAccount: AccountModel) {
+ const follow = await AccountFollowModel.loadByAccountAndTarget(account.id, targetAccount.id)
if (!follow) throw new Error('Cannot find associated follow.')
follow.set('state', 'accepted')
import * as Bluebird from 'bluebird'
import { VideoTorrentObject } from '../../../../shared'
-import { ActivityAdd } from '../../../../shared/models/activitypub/activity'
-import { VideoRateType } from '../../../../shared/models/videos/video-rate.type'
-import { retryTransactionWrapper } from '../../../helpers/database-utils'
-import { logger } from '../../../helpers/logger'
-import { database as db } from '../../../initializers'
-import { AccountInstance } from '../../../models/account/account-interface'
-import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
-import { VideoInstance } from '../../../models/video/video-interface'
+import { ActivityAdd } from '../../../../shared/models/activitypub'
+import { VideoRateType } from '../../../../shared/models/videos'
+import { logger, retryTransactionWrapper } from '../../../helpers'
+import { sequelizeTypescript } from '../../../initializers'
+import { AccountModel } from '../../../models/account/account'
+import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
+import { TagModel } from '../../../models/video/tag'
+import { VideoModel } from '../../../models/video/video'
+import { VideoChannelModel } from '../../../models/video/video-channel'
+import { VideoFileModel } from '../../../models/video/video-file'
import { getOrCreateAccountAndServer } from '../account'
import { getOrCreateVideoChannel } from '../video-channels'
import { generateThumbnailFromUrl } from '../videos'
// ---------------------------------------------------------------------------
-async function processAddVideo (account: AccountInstance,
+async function processAddVideo (account: AccountModel,
activity: ActivityAdd,
- videoChannel: VideoChannelInstance,
+ videoChannel: VideoChannelModel,
videoToCreateData: VideoTorrentObject) {
const options = {
arguments: [ account, activity, videoChannel, videoToCreateData ],
return video
}
-function addRemoteVideo (account: AccountInstance,
+function addRemoteVideo (account: AccountModel,
activity: ActivityAdd,
- videoChannel: VideoChannelInstance,
+ videoChannel: VideoChannelModel,
videoToCreateData: VideoTorrentObject) {
logger.debug('Adding remote video %s.', videoToCreateData.id)
- return db.sequelize.transaction(async t => {
+ return sequelizeTypescript.transaction(async t => {
const sequelizeOptions = {
transaction: t
}
if (videoChannel.Account.id !== account.id) throw new Error('Video channel is not owned by this account.')
- const videoFromDatabase = await db.Video.loadByUUIDOrURL(videoToCreateData.uuid, videoToCreateData.id, t)
+ const videoFromDatabase = await VideoModel.loadByUUIDOrURL(videoToCreateData.uuid, videoToCreateData.id, t)
if (videoFromDatabase) return videoFromDatabase
const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoToCreateData, activity.to, activity.cc)
- const video = db.Video.build(videoData)
+ const video = VideoModel.build(videoData)
// Don't block on request
generateThumbnailFromUrl(video, videoToCreateData.icon)
throw new Error('Cannot find valid files for video %s ' + videoToCreateData.url)
}
- const tasks: Bluebird<any>[] = videoFileAttributes.map(f => db.VideoFile.create(f, { transaction: t }))
+ const tasks: Bluebird<any>[] = videoFileAttributes.map(f => VideoFileModel.create(f, { transaction: t }))
await Promise.all(tasks)
const tags = videoToCreateData.tag.map(t => t.name)
- const tagInstances = await db.Tag.findOrCreateTags(tags, t)
- await videoCreated.setTags(tagInstances, sequelizeOptions)
+ const tagInstances = await TagModel.findOrCreateTags(tags, t)
+ await videoCreated.$set('Tags', tagInstances, sequelizeOptions)
logger.info('Remote video with uuid %s inserted.', videoToCreateData.uuid)
})
}
-async function createRates (accountUrls: string[], video: VideoInstance, rate: VideoRateType) {
+async function createRates (accountUrls: string[], video: VideoModel, rate: VideoRateType) {
let rateCounts = 0
const tasks: Bluebird<any>[] = []
for (const accountUrl of accountUrls) {
const account = await getOrCreateAccountAndServer(accountUrl)
- const p = db.AccountVideoRate
+ const p = AccountVideoRateModel
.create({
videoId: video.id,
accountId: account.id,
-import { ActivityAdd, ActivityAnnounce, ActivityCreate } from '../../../../shared/models/activitypub/activity'
-import { retryTransactionWrapper } from '../../../helpers/database-utils'
-import { logger } from '../../../helpers/logger'
-import { database as db } from '../../../initializers/index'
-import { AccountInstance } from '../../../models/account/account-interface'
-import { VideoInstance } from '../../../models/index'
-import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
+import { ActivityAdd, ActivityAnnounce, ActivityCreate } from '../../../../shared/models/activitypub'
+import { logger, retryTransactionWrapper } from '../../../helpers'
+import { sequelizeTypescript } from '../../../initializers'
+import { AccountModel } from '../../../models/account/account'
+import { VideoModel } from '../../../models/video/video'
+import { VideoChannelModel } from '../../../models/video/video-channel'
+import { VideoChannelShareModel } from '../../../models/video/video-channel-share'
+import { VideoShareModel } from '../../../models/video/video-share'
import { getOrCreateAccountAndServer } from '../account'
import { forwardActivity } from '../send/misc'
import { processAddActivity } from './process-add'
// ---------------------------------------------------------------------------
-function processVideoChannelShare (accountAnnouncer: AccountInstance, activity: ActivityAnnounce) {
+function processVideoChannelShare (accountAnnouncer: AccountModel, activity: ActivityAnnounce) {
const options = {
arguments: [ accountAnnouncer, activity ],
errorMessage: 'Cannot share the video channel with many retries.'
return retryTransactionWrapper(shareVideoChannel, options)
}
-async function shareVideoChannel (accountAnnouncer: AccountInstance, activity: ActivityAnnounce) {
+async function shareVideoChannel (accountAnnouncer: AccountModel, activity: ActivityAnnounce) {
const announcedActivity = activity.object as ActivityCreate
- return db.sequelize.transaction(async t => {
+ return sequelizeTypescript.transaction(async t => {
// Add share entry
- const videoChannel: VideoChannelInstance = await processCreateActivity(announcedActivity)
+ const videoChannel: VideoChannelModel = await processCreateActivity(announcedActivity)
const share = {
accountId: accountAnnouncer.id,
videoChannelId: videoChannel.id
}
- const [ , created ] = await db.VideoChannelShare.findOrCreate({
+ const [ , created ] = await VideoChannelShareModel.findOrCreate({
where: share,
defaults: share,
transaction: t
})
}
-function processVideoShare (accountAnnouncer: AccountInstance, activity: ActivityAnnounce) {
+function processVideoShare (accountAnnouncer: AccountModel, activity: ActivityAnnounce) {
const options = {
arguments: [ accountAnnouncer, activity ],
errorMessage: 'Cannot share the video with many retries.'
return retryTransactionWrapper(shareVideo, options)
}
-function shareVideo (accountAnnouncer: AccountInstance, activity: ActivityAnnounce) {
+function shareVideo (accountAnnouncer: AccountModel, activity: ActivityAnnounce) {
const announcedActivity = activity.object as ActivityAdd
- return db.sequelize.transaction(async t => {
+ return sequelizeTypescript.transaction(async t => {
// Add share entry
- const video: VideoInstance = await processAddActivity(announcedActivity)
+ const video: VideoModel = await processAddActivity(announcedActivity)
const share = {
accountId: accountAnnouncer.id,
videoId: video.id
}
- const [ , created ] = await db.VideoShare.findOrCreate({
+ const [ , created ] = await VideoShareModel.findOrCreate({
where: share,
defaults: share,
transaction: t
import { ActivityCreate, VideoChannelObject } from '../../../../shared'
-import { DislikeObject } from '../../../../shared/models/activitypub/objects/dislike-object'
-import { VideoAbuseObject } from '../../../../shared/models/activitypub/objects/video-abuse-object'
-import { ViewObject } from '../../../../shared/models/activitypub/objects/view-object'
+import { DislikeObject, VideoAbuseObject, ViewObject } from '../../../../shared/models/activitypub/objects'
import { logger, retryTransactionWrapper } from '../../../helpers'
-import { database as db } from '../../../initializers'
-import { AccountInstance } from '../../../models/account/account-interface'
+import { sequelizeTypescript } from '../../../initializers'
+import { AccountModel } from '../../../models/account/account'
+import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
+import { VideoModel } from '../../../models/video/video'
+import { VideoAbuseModel } from '../../../models/video/video-abuse'
+import { VideoChannelModel } from '../../../models/video/video-channel'
import { getOrCreateAccountAndServer } from '../account'
import { forwardActivity } from '../send/misc'
import { getVideoChannelActivityPubUrl } from '../url'
// ---------------------------------------------------------------------------
-async function processCreateDislike (byAccount: AccountInstance, activity: ActivityCreate) {
+async function processCreateDislike (byAccount: AccountModel, activity: ActivityCreate) {
const options = {
arguments: [ byAccount, activity ],
errorMessage: 'Cannot dislike the video with many retries.'
return retryTransactionWrapper(createVideoDislike, options)
}
-function createVideoDislike (byAccount: AccountInstance, activity: ActivityCreate) {
+function createVideoDislike (byAccount: AccountModel, activity: ActivityCreate) {
const dislike = activity.object as DislikeObject
- return db.sequelize.transaction(async t => {
- const video = await db.Video.loadByUrlAndPopulateAccount(dislike.object, t)
+ return sequelizeTypescript.transaction(async t => {
+ const video = await VideoModel.loadByUrlAndPopulateAccount(dislike.object, t)
if (!video) throw new Error('Unknown video ' + dislike.object)
const rate = {
videoId: video.id,
accountId: byAccount.id
}
- const [ , created ] = await db.AccountVideoRate.findOrCreate({
+ const [ , created ] = await AccountVideoRateModel.findOrCreate({
where: rate,
defaults: rate,
transaction: t
})
}
-async function processCreateView (byAccount: AccountInstance, activity: ActivityCreate) {
+async function processCreateView (byAccount: AccountModel, activity: ActivityCreate) {
const view = activity.object as ViewObject
- const video = await db.Video.loadByUrlAndPopulateAccount(view.object)
+ const video = await VideoModel.loadByUrlAndPopulateAccount(view.object)
if (!video) throw new Error('Unknown video ' + view.object)
- const account = await db.Account.loadByUrl(view.actor)
+ const account = await AccountModel.loadByUrl(view.actor)
if (!account) throw new Error('Unknown account ' + view.actor)
await video.increment('views')
}
}
-async function processCreateVideoChannel (account: AccountInstance, videoChannelToCreateData: VideoChannelObject) {
+async function processCreateVideoChannel (account: AccountModel, videoChannelToCreateData: VideoChannelObject) {
const options = {
arguments: [ account, videoChannelToCreateData ],
errorMessage: 'Cannot insert the remote video channel with many retries.'
return videoChannel
}
-function addRemoteVideoChannel (account: AccountInstance, videoChannelToCreateData: VideoChannelObject) {
+function addRemoteVideoChannel (account: AccountModel, videoChannelToCreateData: VideoChannelObject) {
logger.debug('Adding remote video channel "%s".', videoChannelToCreateData.uuid)
- return db.sequelize.transaction(async t => {
- let videoChannel = await db.VideoChannel.loadByUUIDOrUrl(videoChannelToCreateData.uuid, videoChannelToCreateData.id, t)
+ return sequelizeTypescript.transaction(async t => {
+ let videoChannel = await VideoChannelModel.loadByUUIDOrUrl(videoChannelToCreateData.uuid, videoChannelToCreateData.id, t)
if (videoChannel) return videoChannel
const videoChannelData = videoChannelActivityObjectToDBAttributes(videoChannelToCreateData, account)
- videoChannel = db.VideoChannel.build(videoChannelData)
+ videoChannel = new VideoChannelModel(videoChannelData)
videoChannel.url = getVideoChannelActivityPubUrl(videoChannel)
videoChannel = await videoChannel.save({ transaction: t })
})
}
-function processCreateVideoAbuse (account: AccountInstance, videoAbuseToCreateData: VideoAbuseObject) {
+function processCreateVideoAbuse (account: AccountModel, videoAbuseToCreateData: VideoAbuseObject) {
const options = {
arguments: [ account, videoAbuseToCreateData ],
errorMessage: 'Cannot insert the remote video abuse with many retries.'
return retryTransactionWrapper(addRemoteVideoAbuse, options)
}
-function addRemoteVideoAbuse (account: AccountInstance, videoAbuseToCreateData: VideoAbuseObject) {
+function addRemoteVideoAbuse (account: AccountModel, videoAbuseToCreateData: VideoAbuseObject) {
logger.debug('Reporting remote abuse for video %s.', videoAbuseToCreateData.object)
- return db.sequelize.transaction(async t => {
- const video = await db.Video.loadByUrlAndPopulateAccount(videoAbuseToCreateData.object, t)
+ return sequelizeTypescript.transaction(async t => {
+ const video = await VideoModel.loadByUrlAndPopulateAccount(videoAbuseToCreateData.object, t)
if (!video) {
logger.warn('Unknown video %s for remote video abuse.', videoAbuseToCreateData.object)
return undefined
videoId: video.id
}
- await db.VideoAbuse.create(videoAbuseData)
+ await VideoAbuseModel.create(videoAbuseData)
logger.info('Remote abuse for video uuid %s created', videoAbuseToCreateData.object)
})
-import { ActivityDelete } from '../../../../shared/models/activitypub/activity'
-import { retryTransactionWrapper } from '../../../helpers/database-utils'
-import { logger } from '../../../helpers/logger'
-import { database as db } from '../../../initializers'
-import { AccountInstance } from '../../../models/account/account-interface'
-import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
-import { VideoInstance } from '../../../models/video/video-interface'
+import { ActivityDelete } from '../../../../shared/models/activitypub'
+import { logger, retryTransactionWrapper } from '../../../helpers'
+import { sequelizeTypescript } from '../../../initializers'
+import { AccountModel } from '../../../models/account/account'
+import { VideoModel } from '../../../models/video/video'
+import { VideoChannelModel } from '../../../models/video/video-channel'
import { getOrCreateAccountAndServer } from '../account'
async function processDeleteActivity (activity: ActivityDelete) {
}
{
- let videoObject = await db.Video.loadByUrlAndPopulateAccount(activity.id)
+ let videoObject = await VideoModel.loadByUrlAndPopulateAccount(activity.id)
if (videoObject !== undefined) {
return processDeleteVideo(account, videoObject)
}
}
{
- let videoChannelObject = await db.VideoChannel.loadByUrl(activity.id)
+ let videoChannelObject = await VideoChannelModel.loadByUrl(activity.id)
if (videoChannelObject !== undefined) {
return processDeleteVideoChannel(account, videoChannelObject)
}
// ---------------------------------------------------------------------------
-async function processDeleteVideo (account: AccountInstance, videoToDelete: VideoInstance) {
+async function processDeleteVideo (account: AccountModel, videoToDelete: VideoModel) {
const options = {
arguments: [ account, videoToDelete ],
errorMessage: 'Cannot remove the remote video with many retries.'
await retryTransactionWrapper(deleteRemoteVideo, options)
}
-async function deleteRemoteVideo (account: AccountInstance, videoToDelete: VideoInstance) {
+async function deleteRemoteVideo (account: AccountModel, videoToDelete: VideoModel) {
logger.debug('Removing remote video "%s".', videoToDelete.uuid)
- await db.sequelize.transaction(async t => {
+ await sequelizeTypescript.transaction(async t => {
if (videoToDelete.VideoChannel.Account.id !== account.id) {
throw new Error('Account ' + account.url + ' does not own video channel ' + videoToDelete.VideoChannel.url)
}
logger.info('Remote video with uuid %s removed.', videoToDelete.uuid)
}
-async function processDeleteVideoChannel (account: AccountInstance, videoChannelToRemove: VideoChannelInstance) {
+async function processDeleteVideoChannel (account: AccountModel, videoChannelToRemove: VideoChannelModel) {
const options = {
arguments: [ account, videoChannelToRemove ],
errorMessage: 'Cannot remove the remote video channel with many retries.'
await retryTransactionWrapper(deleteRemoteVideoChannel, options)
}
-async function deleteRemoteVideoChannel (account: AccountInstance, videoChannelToRemove: VideoChannelInstance) {
+async function deleteRemoteVideoChannel (account: AccountModel, videoChannelToRemove: VideoChannelModel) {
logger.debug('Removing remote video channel "%s".', videoChannelToRemove.uuid)
- await db.sequelize.transaction(async t => {
+ await sequelizeTypescript.transaction(async t => {
if (videoChannelToRemove.Account.id !== account.id) {
throw new Error('Account ' + account.url + ' does not own video channel ' + videoChannelToRemove.url)
}
logger.info('Remote video channel with uuid %s removed.', videoChannelToRemove.uuid)
}
-async function processDeleteAccount (accountToRemove: AccountInstance) {
+async function processDeleteAccount (accountToRemove: AccountModel) {
const options = {
arguments: [ accountToRemove ],
errorMessage: 'Cannot remove the remote account with many retries.'
await retryTransactionWrapper(deleteRemoteAccount, options)
}
-async function deleteRemoteAccount (accountToRemove: AccountInstance) {
+async function deleteRemoteAccount (accountToRemove: AccountModel) {
logger.debug('Removing remote account "%s".', accountToRemove.uuid)
- await db.sequelize.transaction(async t => {
+ await sequelizeTypescript.transaction(async t => {
await accountToRemove.destroy({ transaction: t })
})
-import { ActivityFollow } from '../../../../shared/models/activitypub/activity'
-import { retryTransactionWrapper } from '../../../helpers'
-import { database as db } from '../../../initializers'
-import { AccountInstance } from '../../../models/account/account-interface'
-import { logger } from '../../../helpers/logger'
-import { sendAccept } from '../send/send-accept'
+import { ActivityFollow } from '../../../../shared/models/activitypub'
+import { logger, retryTransactionWrapper } from '../../../helpers'
+import { sequelizeTypescript } from '../../../initializers'
+import { AccountModel } from '../../../models/account/account'
+import { AccountFollowModel } from '../../../models/account/account-follow'
import { getOrCreateAccountAndServer } from '../account'
+import { sendAccept } from '../send'
async function processFollowActivity (activity: ActivityFollow) {
const activityObject = activity.object
// ---------------------------------------------------------------------------
-function processFollow (account: AccountInstance, targetAccountURL: string) {
+function processFollow (account: AccountModel, targetAccountURL: string) {
const options = {
arguments: [ account, targetAccountURL ],
errorMessage: 'Cannot follow with many retries.'
return retryTransactionWrapper(follow, options)
}
-async function follow (account: AccountInstance, targetAccountURL: string) {
- await db.sequelize.transaction(async t => {
- const targetAccount = await db.Account.loadByUrl(targetAccountURL, t)
+async function follow (account: AccountModel, targetAccountURL: string) {
+ await sequelizeTypescript.transaction(async t => {
+ const targetAccount = await AccountModel.loadByUrl(targetAccountURL, t)
if (!targetAccount) throw new Error('Unknown account')
if (targetAccount.isOwned() === false) throw new Error('This is not a local account.')
- const [ accountFollow ] = await db.AccountFollow.findOrCreate({
+ const [ accountFollow ] = await AccountFollowModel.findOrCreate({
where: {
accountId: account.id,
targetAccountId: targetAccount.id
-import { ActivityLike } from '../../../../shared/models/activitypub/activity'
-import { retryTransactionWrapper } from '../../../helpers/database-utils'
-import { database as db } from '../../../initializers'
-import { AccountInstance } from '../../../models/account/account-interface'
+import { ActivityLike } from '../../../../shared/models/activitypub'
+import { retryTransactionWrapper } from '../../../helpers'
+import { sequelizeTypescript } from '../../../initializers'
+import { AccountModel } from '../../../models/account/account'
+import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
+import { VideoModel } from '../../../models/video/video'
import { getOrCreateAccountAndServer } from '../account'
import { forwardActivity } from '../send/misc'
// ---------------------------------------------------------------------------
-async function processLikeVideo (byAccount: AccountInstance, activity: ActivityLike) {
+async function processLikeVideo (byAccount: AccountModel, activity: ActivityLike) {
const options = {
arguments: [ byAccount, activity ],
errorMessage: 'Cannot like the video with many retries.'
return retryTransactionWrapper(createVideoLike, options)
}
-function createVideoLike (byAccount: AccountInstance, activity: ActivityLike) {
+function createVideoLike (byAccount: AccountModel, activity: ActivityLike) {
const videoUrl = activity.object
- return db.sequelize.transaction(async t => {
- const video = await db.Video.loadByUrlAndPopulateAccount(videoUrl)
+ return sequelizeTypescript.transaction(async t => {
+ const video = await VideoModel.loadByUrlAndPopulateAccount(videoUrl)
if (!video) throw new Error('Unknown video ' + videoUrl)
videoId: video.id,
accountId: byAccount.id
}
- const [ , created ] = await db.AccountVideoRate.findOrCreate({
+ const [ , created ] = await AccountVideoRateModel.findOrCreate({
where: rate,
defaults: rate,
transaction: t
-import { ActivityFollow, ActivityLike, ActivityUndo } from '../../../../shared/models/activitypub/activity'
-import { DislikeObject } from '../../../../shared/models/activitypub/objects/dislike-object'
-import { retryTransactionWrapper } from '../../../helpers/database-utils'
-import { logger } from '../../../helpers/logger'
-import { database as db } from '../../../initializers'
+import { ActivityFollow, ActivityLike, ActivityUndo } from '../../../../shared/models/activitypub'
+import { DislikeObject } from '../../../../shared/models/activitypub/objects'
+import { logger, retryTransactionWrapper } from '../../../helpers'
+import { sequelizeTypescript } from '../../../initializers'
+import { AccountModel } from '../../../models/account/account'
+import { AccountFollowModel } from '../../../models/account/account-follow'
+import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
+import { VideoModel } from '../../../models/video/video'
import { forwardActivity } from '../send/misc'
async function processUndoActivity (activity: ActivityUndo) {
function undoLike (actor: string, activity: ActivityUndo) {
const likeActivity = activity.object as ActivityLike
- return db.sequelize.transaction(async t => {
- const byAccount = await db.Account.loadByUrl(actor, t)
+ return sequelizeTypescript.transaction(async t => {
+ const byAccount = await AccountModel.loadByUrl(actor, t)
if (!byAccount) throw new Error('Unknown account ' + actor)
- const video = await db.Video.loadByUrlAndPopulateAccount(likeActivity.object, t)
+ const video = await VideoModel.loadByUrlAndPopulateAccount(likeActivity.object, t)
if (!video) throw new Error('Unknown video ' + likeActivity.actor)
- const rate = await db.AccountVideoRate.load(byAccount.id, video.id, t)
+ const rate = await AccountVideoRateModel.load(byAccount.id, video.id, t)
if (!rate) throw new Error(`Unknown rate by account ${byAccount.id} for video ${video.id}.`)
await rate.destroy({ transaction: t })
function undoDislike (actor: string, activity: ActivityUndo) {
const dislike = activity.object.object as DislikeObject
- return db.sequelize.transaction(async t => {
- const byAccount = await db.Account.loadByUrl(actor, t)
+ return sequelizeTypescript.transaction(async t => {
+ const byAccount = await AccountModel.loadByUrl(actor, t)
if (!byAccount) throw new Error('Unknown account ' + actor)
- const video = await db.Video.loadByUrlAndPopulateAccount(dislike.object, t)
+ const video = await VideoModel.loadByUrlAndPopulateAccount(dislike.object, t)
if (!video) throw new Error('Unknown video ' + dislike.actor)
- const rate = await db.AccountVideoRate.load(byAccount.id, video.id, t)
+ const rate = await AccountVideoRateModel.load(byAccount.id, video.id, t)
if (!rate) throw new Error(`Unknown rate by account ${byAccount.id} for video ${video.id}.`)
await rate.destroy({ transaction: t })
}
function undoFollow (actor: string, followActivity: ActivityFollow) {
- return db.sequelize.transaction(async t => {
- const follower = await db.Account.loadByUrl(actor, t)
- const following = await db.Account.loadByUrl(followActivity.object, t)
- const accountFollow = await db.AccountFollow.loadByAccountAndTarget(follower.id, following.id, t)
+ return sequelizeTypescript.transaction(async t => {
+ const follower = await AccountModel.loadByUrl(actor, t)
+ const following = await AccountModel.loadByUrl(followActivity.object, t)
+ const accountFollow = await AccountFollowModel.loadByAccountAndTarget(follower.id, following.id, t)
if (!accountFollow) throw new Error(`'Unknown account follow ${follower.id} -> ${following.id}.`)
import * as Bluebird from 'bluebird'
import { VideoChannelObject, VideoTorrentObject } from '../../../../shared'
-import { ActivityUpdate } from '../../../../shared/models/activitypub/activity'
-import { retryTransactionWrapper } from '../../../helpers/database-utils'
-import { logger } from '../../../helpers/logger'
-import { resetSequelizeInstance } from '../../../helpers/utils'
-import { database as db } from '../../../initializers'
-import { AccountInstance } from '../../../models/account/account-interface'
-import { VideoInstance } from '../../../models/video/video-interface'
+import { ActivityUpdate } from '../../../../shared/models/activitypub'
+import { logger, resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers'
+import { sequelizeTypescript } from '../../../initializers'
+import { AccountModel } from '../../../models/account/account'
+import { TagModel } from '../../../models/video/tag'
+import { VideoModel } from '../../../models/video/video'
+import { VideoChannelModel } from '../../../models/video/video-channel'
+import { VideoFileModel } from '../../../models/video/video-file'
import { getOrCreateAccountAndServer } from '../account'
import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc'
// ---------------------------------------------------------------------------
-function processUpdateVideo (account: AccountInstance, video: VideoTorrentObject) {
+function processUpdateVideo (account: AccountModel, video: VideoTorrentObject) {
const options = {
arguments: [ account, video ],
errorMessage: 'Cannot update the remote video with many retries'
return retryTransactionWrapper(updateRemoteVideo, options)
}
-async function updateRemoteVideo (account: AccountInstance, videoAttributesToUpdate: VideoTorrentObject) {
+async function updateRemoteVideo (account: AccountModel, videoAttributesToUpdate: VideoTorrentObject) {
logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid)
- let videoInstance: VideoInstance
+ let videoInstance: VideoModel
let videoFieldsSave: object
try {
- await db.sequelize.transaction(async t => {
+ await sequelizeTypescript.transaction(async t => {
const sequelizeOptions = {
transaction: t
}
- const videoInstance = await db.Video.loadByUrlAndPopulateAccount(videoAttributesToUpdate.id, t)
+ const videoInstance = await VideoModel.loadByUrlAndPopulateAccount(videoAttributesToUpdate.id, t)
if (!videoInstance) throw new Error('Video ' + videoAttributesToUpdate.id + ' not found.')
if (videoInstance.VideoChannel.Account.id !== account.id) {
await Promise.all(videoFileDestroyTasks)
const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoInstance, videoAttributesToUpdate)
- const tasks: Bluebird<any>[] = videoFileAttributes.map(f => db.VideoFile.create(f))
+ const tasks: Bluebird<any>[] = videoFileAttributes.map(f => VideoFileModel.create(f))
await Promise.all(tasks)
const tags = videoAttributesToUpdate.tag.map(t => t.name)
- const tagInstances = await db.Tag.findOrCreateTags(tags, t)
- await videoInstance.setTags(tagInstances, sequelizeOptions)
+ const tagInstances = await TagModel.findOrCreateTags(tags, t)
+ await videoInstance.$set('Tags', tagInstances, sequelizeOptions)
})
logger.info('Remote video with uuid %s updated', videoAttributesToUpdate.uuid)
}
}
-async function processUpdateVideoChannel (account: AccountInstance, videoChannel: VideoChannelObject) {
+async function processUpdateVideoChannel (account: AccountModel, videoChannel: VideoChannelObject) {
const options = {
arguments: [ account, videoChannel ],
errorMessage: 'Cannot update the remote video channel with many retries.'
await retryTransactionWrapper(updateRemoteVideoChannel, options)
}
-async function updateRemoteVideoChannel (account: AccountInstance, videoChannel: VideoChannelObject) {
+async function updateRemoteVideoChannel (account: AccountModel, videoChannel: VideoChannelObject) {
logger.debug('Updating remote video channel "%s".', videoChannel.uuid)
- await db.sequelize.transaction(async t => {
+ await sequelizeTypescript.transaction(async t => {
const sequelizeOptions = { transaction: t }
- const videoChannelInstance = await db.VideoChannel.loadByUrl(videoChannel.id)
+ const videoChannelInstance = await VideoChannelModel.loadByUrl(videoChannel.id)
if (!videoChannelInstance) throw new Error('Video ' + videoChannel.id + ' not found.')
if (videoChannelInstance.Account.id !== account.id) {
-import { Activity, ActivityType } from '../../../../shared/models/activitypub/activity'
-import { logger } from '../../../helpers/logger'
-import { AccountInstance } from '../../../models/account/account-interface'
+import { Activity, ActivityType } from '../../../../shared/models/activitypub'
+import { logger } from '../../../helpers'
+import { AccountModel } from '../../../models/account/account'
import { processAcceptActivity } from './process-accept'
import { processAddActivity } from './process-add'
import { processAnnounceActivity } from './process-announce'
import { processUndoActivity } from './process-undo'
import { processUpdateActivity } from './process-update'
-const processActivity: { [ P in ActivityType ]: (activity: Activity, inboxAccount?: AccountInstance) => Promise<any> } = {
+const processActivity: { [ P in ActivityType ]: (activity: Activity, inboxAccount?: AccountModel) => Promise<any> } = {
Create: processCreateActivity,
Add: processAddActivity,
Update: processUpdateActivity,
Like: processLikeActivity
}
-async function processActivities (activities: Activity[], signatureAccount?: AccountInstance, inboxAccount?: AccountInstance) {
+async function processActivities (activities: Activity[], signatureAccount?: AccountModel, inboxAccount?: AccountModel) {
for (const activity of activities) {
// When we fetch remote data, we don't have signature
if (signatureAccount && activity.actor !== signatureAccount.url) {
import { Transaction } from 'sequelize'
-import { Activity } from '../../../../shared/models/activitypub/activity'
-import { logger } from '../../../helpers/logger'
-import { ACTIVITY_PUB, database as db } from '../../../initializers'
-import { AccountInstance } from '../../../models/account/account-interface'
-import { VideoChannelInstance } from '../../../models/index'
-import { VideoInstance } from '../../../models/video/video-interface'
-import {
- activitypubHttpJobScheduler,
- ActivityPubHttpPayload
-} from '../../jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler'
+import { Activity } from '../../../../shared/models/activitypub'
+import { logger } from '../../../helpers'
+import { ACTIVITY_PUB } from '../../../initializers'
+import { AccountModel } from '../../../models/account/account'
+import { AccountFollowModel } from '../../../models/account/account-follow'
+import { VideoModel } from '../../../models/video/video'
+import { VideoChannelModel } from '../../../models/video/video-channel'
+import { VideoChannelShareModel } from '../../../models/video/video-channel-share'
+import { VideoShareModel } from '../../../models/video/video-share'
+import { activitypubHttpJobScheduler, ActivityPubHttpPayload } from '../../jobs/activitypub-http-job-scheduler'
async function forwardActivity (
activity: Activity,
t: Transaction,
- followersException: AccountInstance[] = []
+ followersException: AccountModel[] = []
) {
const to = activity.to || []
const cc = activity.cc || []
}
}
- const toAccountFollowers = await db.Account.listByFollowersUrls(followersUrls, t)
+ const toAccountFollowers = await AccountModel.listByFollowersUrls(followersUrls, t)
const uris = await computeFollowerUris(toAccountFollowers, followersException, t)
if (uris.length === 0) {
async function broadcastToFollowers (
data: any,
- byAccount: AccountInstance,
- toAccountFollowers: AccountInstance[],
+ byAccount: AccountModel,
+ toAccountFollowers: AccountModel[],
t: Transaction,
- followersException: AccountInstance[] = []
+ followersException: AccountModel[] = []
) {
const uris = await computeFollowerUris(toAccountFollowers, followersException, t)
if (uris.length === 0) {
return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpBroadcastHandler', jobPayload)
}
-async function unicastTo (data: any, byAccount: AccountInstance, toAccountUrl: string, t: Transaction) {
+async function unicastTo (data: any, byAccount: AccountModel, toAccountUrl: string, t: Transaction) {
logger.debug('Creating unicast job.', { uri: toAccountUrl })
const jobPayload: ActivityPubHttpPayload = {
return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpUnicastHandler', jobPayload)
}
-function getOriginVideoAudience (video: VideoInstance, accountsInvolvedInVideo: AccountInstance[]) {
+function getOriginVideoAudience (video: VideoModel, accountsInvolvedInVideo: AccountModel[]) {
return {
to: [ video.VideoChannel.Account.url ],
cc: accountsInvolvedInVideo.map(a => a.followersUrl)
}
}
-function getOriginVideoChannelAudience (videoChannel: VideoChannelInstance, accountsInvolved: AccountInstance[]) {
+function getOriginVideoChannelAudience (videoChannel: VideoChannelModel, accountsInvolved: AccountModel[]) {
return {
to: [ videoChannel.Account.url ],
cc: accountsInvolved.map(a => a.followersUrl)
}
}
-function getObjectFollowersAudience (accountsInvolvedInObject: AccountInstance[]) {
+function getObjectFollowersAudience (accountsInvolvedInObject: AccountModel[]) {
return {
to: accountsInvolvedInObject.map(a => a.followersUrl),
cc: []
}
}
-async function getAccountsInvolvedInVideo (video: VideoInstance, t: Transaction) {
- const accountsToForwardView = await db.VideoShare.loadAccountsByShare(video.id, t)
+async function getAccountsInvolvedInVideo (video: VideoModel, t: Transaction) {
+ const accountsToForwardView = await VideoShareModel.loadAccountsByShare(video.id, t)
accountsToForwardView.push(video.VideoChannel.Account)
return accountsToForwardView
}
-async function getAccountsInvolvedInVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) {
- const accountsToForwardView = await db.VideoChannelShare.loadAccountsByShare(videoChannel.id, t)
+async function getAccountsInvolvedInVideoChannel (videoChannel: VideoChannelModel, t: Transaction) {
+ const accountsToForwardView = await VideoChannelShareModel.loadAccountsByShare(videoChannel.id, t)
accountsToForwardView.push(videoChannel.Account)
return accountsToForwardView
}
-async function getAudience (accountSender: AccountInstance, t: Transaction, isPublic = true) {
+async function getAudience (accountSender: AccountModel, t: Transaction, isPublic = true) {
const followerInboxUrls = await accountSender.getFollowerSharedInboxUrls(t)
// Thanks Mastodon: https://github.com/tootsuite/mastodon/blob/master/app/lib/activitypub/tag_manager.rb#L47
return { to, cc }
}
-async function computeFollowerUris (toAccountFollower: AccountInstance[], followersException: AccountInstance[], t: Transaction) {
+async function computeFollowerUris (toAccountFollower: AccountModel[], followersException: AccountModel[], t: Transaction) {
const toAccountFollowerIds = toAccountFollower.map(a => a.id)
- const result = await db.AccountFollow.listAcceptedFollowerSharedInboxUrls(toAccountFollowerIds, t)
+ const result = await AccountFollowModel.listAcceptedFollowerSharedInboxUrls(toAccountFollowerIds, t)
const followersSharedInboxException = followersException.map(f => f.sharedInboxUrl)
- const uris = result.data.filter(sharedInbox => followersSharedInboxException.indexOf(sharedInbox) === -1)
-
- return uris
+ return result.data.filter(sharedInbox => followersSharedInboxException.indexOf(sharedInbox) === -1)
}
// ---------------------------------------------------------------------------
import { Transaction } from 'sequelize'
-import { ActivityAccept } from '../../../../shared/models/activitypub/activity'
-import { AccountInstance } from '../../../models'
-import { AccountFollowInstance } from '../../../models/account/account-follow-interface'
-import { unicastTo } from './misc'
+import { ActivityAccept } from '../../../../shared/models/activitypub'
+import { AccountModel } from '../../../models/account/account'
+import { AccountFollowModel } from '../../../models/account/account-follow'
import { getAccountFollowAcceptActivityPubUrl } from '../url'
+import { unicastTo } from './misc'
-async function sendAccept (accountFollow: AccountFollowInstance, t: Transaction) {
+async function sendAccept (accountFollow: AccountFollowModel, t: Transaction) {
const follower = accountFollow.AccountFollower
const me = accountFollow.AccountFollowing
// ---------------------------------------------------------------------------
-function acceptActivityData (url: string, byAccount: AccountInstance) {
+function acceptActivityData (url: string, byAccount: AccountModel) {
const activity: ActivityAccept = {
type: 'Accept',
id: url,
import { Transaction } from 'sequelize'
-import { ActivityAdd } from '../../../../shared/models/activitypub/activity'
-import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum'
-import { AccountInstance, VideoInstance } from '../../../models'
+import { ActivityAdd } from '../../../../shared/models/activitypub'
+import { VideoPrivacy } from '../../../../shared/models/videos'
+import { AccountModel } from '../../../models/account/account'
+import { VideoModel } from '../../../models/video/video'
import { broadcastToFollowers, getAudience } from './misc'
-async function sendAddVideo (video: VideoInstance, t: Transaction) {
+async function sendAddVideo (video: VideoModel, t: Transaction) {
const byAccount = video.VideoChannel.Account
const videoObject = video.toActivityPubObject()
async function addActivityData (
url: string,
- byAccount: AccountInstance,
- video: VideoInstance,
+ byAccount: AccountModel,
+ video: VideoModel,
target: string,
object: any,
t: Transaction
-) {
+): Promise<ActivityAdd> {
const videoPublic = video.privacy === VideoPrivacy.PUBLIC
const { to, cc } = await getAudience(byAccount, t, videoPublic)
- const activity: ActivityAdd = {
+
+ return {
type: 'Add',
id: url,
actor: byAccount.url,
object,
target
}
-
- return activity
}
// ---------------------------------------------------------------------------
import { Transaction } from 'sequelize'
import { ActivityAdd } from '../../../../shared/index'
-import { ActivityAnnounce, ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub/activity'
-import { AccountInstance, VideoInstance } from '../../../models'
-import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
+import { ActivityAnnounce, ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub'
+import { AccountModel } from '../../../models/account/account'
+import { VideoModel } from '../../../models/video/video'
+import { VideoChannelModel } from '../../../models/video/video-channel'
import { getAnnounceActivityPubUrl } from '../url'
import {
broadcastToFollowers,
import { addActivityData } from './send-add'
import { createActivityData } from './send-create'
-async function buildVideoAnnounceToFollowers (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
+async function buildVideoAnnounceToFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) {
const url = getAnnounceActivityPubUrl(video.url, byAccount)
const videoChannel = video.VideoChannel
const accountsToForwardView = await getAccountsInvolvedInVideo(video, t)
const audience = getObjectFollowersAudience(accountsToForwardView)
- const data = await announceActivityData(url, byAccount, announcedActivity, t, audience)
-
- return data
+ return announceActivityData(url, byAccount, announcedActivity, t, audience)
}
-async function sendVideoAnnounceToFollowers (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
+async function sendVideoAnnounceToFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) {
const data = await buildVideoAnnounceToFollowers(byAccount, video, t)
return broadcastToFollowers(data, byAccount, [ byAccount ], t)
}
-async function sendVideoAnnounceToOrigin (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
+async function sendVideoAnnounceToOrigin (byAccount: AccountModel, video: VideoModel, t: Transaction) {
const url = getAnnounceActivityPubUrl(video.url, byAccount)
const videoChannel = video.VideoChannel
return unicastTo(data, byAccount, videoChannel.Account.sharedInboxUrl, t)
}
-async function buildVideoChannelAnnounceToFollowers (byAccount: AccountInstance, videoChannel: VideoChannelInstance, t: Transaction) {
+async function buildVideoChannelAnnounceToFollowers (byAccount: AccountModel, videoChannel: VideoChannelModel, t: Transaction) {
const url = getAnnounceActivityPubUrl(videoChannel.url, byAccount)
const announcedActivity = await createActivityData(url, videoChannel.Account, videoChannel.toActivityPubObject(), t)
const accountsToForwardView = await getAccountsInvolvedInVideoChannel(videoChannel, t)
const audience = getObjectFollowersAudience(accountsToForwardView)
- const data = await announceActivityData(url, byAccount, announcedActivity, t, audience)
-
- return data
+ return announceActivityData(url, byAccount, announcedActivity, t, audience)
}
-async function sendVideoChannelAnnounceToFollowers (byAccount: AccountInstance, videoChannel: VideoChannelInstance, t: Transaction) {
+async function sendVideoChannelAnnounceToFollowers (byAccount: AccountModel, videoChannel: VideoChannelModel, t: Transaction) {
const data = await buildVideoChannelAnnounceToFollowers(byAccount, videoChannel, t)
return broadcastToFollowers(data, byAccount, [ byAccount ], t)
}
-async function sendVideoChannelAnnounceToOrigin (byAccount: AccountInstance, videoChannel: VideoChannelInstance, t: Transaction) {
+async function sendVideoChannelAnnounceToOrigin (byAccount: AccountModel, videoChannel: VideoChannelModel, t: Transaction) {
const url = getAnnounceActivityPubUrl(videoChannel.url, byAccount)
const announcedActivity = await createActivityData(url, videoChannel.Account, videoChannel.toActivityPubObject(), t)
async function announceActivityData (
url: string,
- byAccount: AccountInstance,
+ byAccount: AccountModel,
object: ActivityCreate | ActivityAdd,
t: Transaction,
audience?: ActivityAudience
-) {
+): Promise<ActivityAnnounce> {
if (!audience) {
audience = await getAudience(byAccount, t)
}
- const activity: ActivityAnnounce = {
+ return {
type: 'Announce',
to: audience.to,
cc: audience.cc,
actor: byAccount.url,
object
}
-
- return activity
}
// ---------------------------------------------------------------------------
import { Transaction } from 'sequelize'
-import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub/activity'
-import { getServerAccount } from '../../../helpers/utils'
-import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models'
-import { VideoAbuseInstance } from '../../../models/video/video-abuse-interface'
+import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub'
+import { getServerAccount } from '../../../helpers'
+import { AccountModel } from '../../../models/account/account'
+import { VideoModel } from '../../../models/video/video'
+import { VideoAbuseModel } from '../../../models/video/video-abuse'
+import { VideoChannelModel } from '../../../models/video/video-channel'
import { getVideoAbuseActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoViewActivityPubUrl } from '../url'
import {
broadcastToFollowers,
unicastTo
} from './misc'
-async function sendCreateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) {
+async function sendCreateVideoChannel (videoChannel: VideoChannelModel, t: Transaction) {
const byAccount = videoChannel.Account
const videoChannelObject = videoChannel.toActivityPubObject()
return broadcastToFollowers(data, byAccount, [ byAccount ], t)
}
-async function sendVideoAbuse (byAccount: AccountInstance, videoAbuse: VideoAbuseInstance, video: VideoInstance, t: Transaction) {
+async function sendVideoAbuse (byAccount: AccountModel, videoAbuse: VideoAbuseModel, video: VideoModel, t: Transaction) {
const url = getVideoAbuseActivityPubUrl(videoAbuse)
const audience = { to: [ video.VideoChannel.Account.url ], cc: [] }
return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t)
}
-async function sendCreateViewToOrigin (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
+async function sendCreateViewToOrigin (byAccount: AccountModel, video: VideoModel, t: Transaction) {
const url = getVideoViewActivityPubUrl(byAccount, video)
const viewActivity = createViewActivityData(byAccount, video)
return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t)
}
-async function sendCreateViewToVideoFollowers (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
+async function sendCreateViewToVideoFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) {
const url = getVideoViewActivityPubUrl(byAccount, video)
const viewActivity = createViewActivityData(byAccount, video)
return broadcastToFollowers(data, serverAccount, accountsToForwardView, t, followersException)
}
-async function sendCreateDislikeToOrigin (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
+async function sendCreateDislikeToOrigin (byAccount: AccountModel, video: VideoModel, t: Transaction) {
const url = getVideoDislikeActivityPubUrl(byAccount, video)
const dislikeActivity = createDislikeActivityData(byAccount, video)
return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t)
}
-async function sendCreateDislikeToVideoFollowers (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
+async function sendCreateDislikeToVideoFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) {
const url = getVideoDislikeActivityPubUrl(byAccount, video)
const dislikeActivity = createDislikeActivityData(byAccount, video)
return broadcastToFollowers(data, byAccount, accountsToForwardView, t, followersException)
}
-async function createActivityData (url: string, byAccount: AccountInstance, object: any, t: Transaction, audience?: ActivityAudience) {
+async function createActivityData (
+ url: string,
+ byAccount: AccountModel,
+ object: any,
+ t: Transaction,
+ audience?: ActivityAudience
+): Promise<ActivityCreate> {
if (!audience) {
audience = await getAudience(byAccount, t)
}
- const activity: ActivityCreate = {
+ return {
type: 'Create',
id: url,
actor: byAccount.url,
cc: audience.cc,
object
}
-
- return activity
}
-function createDislikeActivityData (byAccount: AccountInstance, video: VideoInstance) {
- const obj = {
+function createDislikeActivityData (byAccount: AccountModel, video: VideoModel) {
+ return {
type: 'Dislike',
actor: byAccount.url,
object: video.url
}
-
- return obj
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
-function createViewActivityData (byAccount: AccountInstance, video: VideoInstance) {
- const obj = {
+function createViewActivityData (byAccount: AccountModel, video: VideoModel) {
+ return {
type: 'View',
actor: byAccount.url,
object: video.url
}
-
- return obj
}
import { Transaction } from 'sequelize'
-import { ActivityDelete } from '../../../../shared/models/activitypub/activity'
-import { database as db } from '../../../initializers'
-import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models'
+import { ActivityDelete } from '../../../../shared/models/activitypub'
+import { AccountModel } from '../../../models/account/account'
+import { VideoModel } from '../../../models/video/video'
+import { VideoChannelModel } from '../../../models/video/video-channel'
+import { VideoChannelShareModel } from '../../../models/video/video-channel-share'
+import { VideoShareModel } from '../../../models/video/video-share'
import { broadcastToFollowers } from './misc'
-async function sendDeleteVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) {
+async function sendDeleteVideoChannel (videoChannel: VideoChannelModel, t: Transaction) {
const byAccount = videoChannel.Account
const data = deleteActivityData(videoChannel.url, byAccount)
- const accountsInvolved = await db.VideoChannelShare.loadAccountsByShare(videoChannel.id, t)
+ const accountsInvolved = await VideoChannelShareModel.loadAccountsByShare(videoChannel.id, t)
accountsInvolved.push(byAccount)
return broadcastToFollowers(data, byAccount, accountsInvolved, t)
}
-async function sendDeleteVideo (video: VideoInstance, t: Transaction) {
+async function sendDeleteVideo (video: VideoModel, t: Transaction) {
const byAccount = video.VideoChannel.Account
const data = deleteActivityData(video.url, byAccount)
- const accountsInvolved = await db.VideoShare.loadAccountsByShare(video.id, t)
+ const accountsInvolved = await VideoShareModel.loadAccountsByShare(video.id, t)
accountsInvolved.push(byAccount)
return broadcastToFollowers(data, byAccount, accountsInvolved, t)
}
-async function sendDeleteAccount (account: AccountInstance, t: Transaction) {
+async function sendDeleteAccount (account: AccountModel, t: Transaction) {
const data = deleteActivityData(account.url, account)
return broadcastToFollowers(data, account, [ account ], t)
// ---------------------------------------------------------------------------
-function deleteActivityData (url: string, byAccount: AccountInstance) {
- const activity: ActivityDelete = {
+function deleteActivityData (url: string, byAccount: AccountModel): ActivityDelete {
+ return {
type: 'Delete',
id: url,
actor: byAccount.url
}
-
- return activity
}
import { Transaction } from 'sequelize'
-import { ActivityFollow } from '../../../../shared/models/activitypub/activity'
-import { AccountInstance } from '../../../models'
-import { AccountFollowInstance } from '../../../models/account/account-follow-interface'
+import { ActivityFollow } from '../../../../shared/models/activitypub'
+import { AccountModel } from '../../../models/account/account'
+import { AccountFollowModel } from '../../../models/account/account-follow'
import { getAccountFollowActivityPubUrl } from '../url'
import { unicastTo } from './misc'
-function sendFollow (accountFollow: AccountFollowInstance, t: Transaction) {
+function sendFollow (accountFollow: AccountFollowModel, t: Transaction) {
const me = accountFollow.AccountFollower
const following = accountFollow.AccountFollowing
return unicastTo(data, me, following.inboxUrl, t)
}
-function followActivityData (url: string, byAccount: AccountInstance, targetAccount: AccountInstance) {
- const activity: ActivityFollow = {
+function followActivityData (url: string, byAccount: AccountModel, targetAccount: AccountModel): ActivityFollow {
+ return {
type: 'Follow',
id: url,
actor: byAccount.url,
object: targetAccount.url
}
-
- return activity
}
// ---------------------------------------------------------------------------
import { Transaction } from 'sequelize'
-import { ActivityAudience, ActivityLike } from '../../../../shared/models/activitypub/activity'
-import { AccountInstance, VideoInstance } from '../../../models'
+import { ActivityAudience, ActivityLike } from '../../../../shared/models/activitypub'
+import { AccountModel } from '../../../models/account/account'
+import { VideoModel } from '../../../models/video/video'
import { getVideoLikeActivityPubUrl } from '../url'
import {
broadcastToFollowers,
unicastTo
} from './misc'
-async function sendLikeToOrigin (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
+async function sendLikeToOrigin (byAccount: AccountModel, video: VideoModel, t: Transaction) {
const url = getVideoLikeActivityPubUrl(byAccount, video)
const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video, t)
return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t)
}
-async function sendLikeToVideoFollowers (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
+async function sendLikeToVideoFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) {
const url = getVideoLikeActivityPubUrl(byAccount, video)
const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video, t)
async function likeActivityData (
url: string,
- byAccount: AccountInstance,
- video: VideoInstance,
+ byAccount: AccountModel,
+ video: VideoModel,
t: Transaction,
audience?: ActivityAudience
-) {
+): Promise<ActivityLike> {
if (!audience) {
audience = await getAudience(byAccount, t)
}
- const activity: ActivityLike = {
+ return {
type: 'Like',
id: url,
actor: byAccount.url,
cc: audience.cc,
object: video.url
}
-
- return activity
}
// ---------------------------------------------------------------------------
ActivityFollow,
ActivityLike,
ActivityUndo
-} from '../../../../shared/models/activitypub/activity'
-import { AccountInstance } from '../../../models'
-import { AccountFollowInstance } from '../../../models/account/account-follow-interface'
-import { VideoInstance } from '../../../models/video/video-interface'
+} from '../../../../shared/models/activitypub'
+import { AccountModel } from '../../../models/account/account'
+import { AccountFollowModel } from '../../../models/account/account-follow'
+import { VideoModel } from '../../../models/video/video'
import { getAccountFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url'
import {
broadcastToFollowers,
import { followActivityData } from './send-follow'
import { likeActivityData } from './send-like'
-async function sendUndoFollow (accountFollow: AccountFollowInstance, t: Transaction) {
+async function sendUndoFollow (accountFollow: AccountFollowModel, t: Transaction) {
const me = accountFollow.AccountFollower
const following = accountFollow.AccountFollowing
return unicastTo(data, me, following.inboxUrl, t)
}
-async function sendUndoLikeToOrigin (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
+async function sendUndoLikeToOrigin (byAccount: AccountModel, video: VideoModel, t: Transaction) {
const likeUrl = getVideoLikeActivityPubUrl(byAccount, video)
const undoUrl = getUndoActivityPubUrl(likeUrl)
return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t)
}
-async function sendUndoLikeToVideoFollowers (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
+async function sendUndoLikeToVideoFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) {
const likeUrl = getVideoLikeActivityPubUrl(byAccount, video)
const undoUrl = getUndoActivityPubUrl(likeUrl)
return broadcastToFollowers(data, byAccount, toAccountsFollowers, t, followersException)
}
-async function sendUndoDislikeToOrigin (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
+async function sendUndoDislikeToOrigin (byAccount: AccountModel, video: VideoModel, t: Transaction) {
const dislikeUrl = getVideoDislikeActivityPubUrl(byAccount, video)
const undoUrl = getUndoActivityPubUrl(dislikeUrl)
return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t)
}
-async function sendUndoDislikeToVideoFollowers (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
+async function sendUndoDislikeToVideoFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) {
const dislikeUrl = getVideoDislikeActivityPubUrl(byAccount, video)
const undoUrl = getUndoActivityPubUrl(dislikeUrl)
async function undoActivityData (
url: string,
- byAccount: AccountInstance,
+ byAccount: AccountModel,
object: ActivityFollow | ActivityLike | ActivityCreate,
t: Transaction,
audience?: ActivityAudience
-) {
+): Promise<ActivityUndo> {
if (!audience) {
audience = await getAudience(byAccount, t)
}
- const activity: ActivityUndo = {
+ return {
type: 'Undo',
id: url,
actor: byAccount.url,
cc: audience.cc,
object
}
-
- return activity
}
import { Transaction } from 'sequelize'
-import { ActivityUpdate } from '../../../../shared/models/activitypub/activity'
-import { database as db } from '../../../initializers'
-import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models'
+import { ActivityUpdate } from '../../../../shared/models/activitypub'
+import { AccountModel } from '../../../models/account/account'
+import { VideoModel } from '../../../models/video/video'
+import { VideoChannelModel } from '../../../models/video/video-channel'
+import { VideoChannelShareModel } from '../../../models/video/video-channel-share'
+import { VideoShareModel } from '../../../models/video/video-share'
import { getUpdateActivityPubUrl } from '../url'
import { broadcastToFollowers, getAudience } from './misc'
-async function sendUpdateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) {
+async function sendUpdateVideoChannel (videoChannel: VideoChannelModel, t: Transaction) {
const byAccount = videoChannel.Account
const url = getUpdateActivityPubUrl(videoChannel.url, videoChannel.updatedAt.toISOString())
const videoChannelObject = videoChannel.toActivityPubObject()
const data = await updateActivityData(url, byAccount, videoChannelObject, t)
- const accountsInvolved = await db.VideoChannelShare.loadAccountsByShare(videoChannel.id, t)
+ const accountsInvolved = await VideoChannelShareModel.loadAccountsByShare(videoChannel.id, t)
accountsInvolved.push(byAccount)
return broadcastToFollowers(data, byAccount, accountsInvolved, t)
}
-async function sendUpdateVideo (video: VideoInstance, t: Transaction) {
+async function sendUpdateVideo (video: VideoModel, t: Transaction) {
const byAccount = video.VideoChannel.Account
const url = getUpdateActivityPubUrl(video.url, video.updatedAt.toISOString())
const videoObject = video.toActivityPubObject()
const data = await updateActivityData(url, byAccount, videoObject, t)
- const accountsInvolved = await db.VideoShare.loadAccountsByShare(video.id, t)
+ const accountsInvolved = await VideoShareModel.loadAccountsByShare(video.id, t)
accountsInvolved.push(byAccount)
return broadcastToFollowers(data, byAccount, accountsInvolved, t)
// ---------------------------------------------------------------------------
-async function updateActivityData (url: string, byAccount: AccountInstance, object: any, t: Transaction) {
+async function updateActivityData (url: string, byAccount: AccountModel, object: any, t: Transaction): Promise<ActivityUpdate> {
const { to, cc } = await getAudience(byAccount, t)
- const activity: ActivityUpdate = {
+ return {
type: 'Update',
id: url,
actor: byAccount.url,
cc,
object
}
-
- return activity
}
import { Transaction } from 'sequelize'
-import { getServerAccount } from '../../helpers/utils'
-import { database as db } from '../../initializers'
-import { VideoChannelInstance } from '../../models/index'
-import { VideoInstance } from '../../models/video/video-interface'
-import { sendVideoAnnounceToFollowers, sendVideoChannelAnnounceToFollowers } from './send/send-announce'
+import { getServerAccount } from '../../helpers'
+import { VideoModel } from '../../models/video/video'
+import { VideoChannelModel } from '../../models/video/video-channel'
+import { VideoChannelShareModel } from '../../models/video/video-channel-share'
+import { VideoShareModel } from '../../models/video/video-share'
+import { sendVideoAnnounceToFollowers, sendVideoChannelAnnounceToFollowers } from './send'
-async function shareVideoChannelByServer (videoChannel: VideoChannelInstance, t: Transaction) {
+async function shareVideoChannelByServer (videoChannel: VideoChannelModel, t: Transaction) {
const serverAccount = await getServerAccount()
- await db.VideoChannelShare.create({
+ await VideoChannelShareModel.create({
accountId: serverAccount.id,
videoChannelId: videoChannel.id
}, { transaction: t })
return sendVideoChannelAnnounceToFollowers(serverAccount, videoChannel, t)
}
-async function shareVideoByServer (video: VideoInstance, t: Transaction) {
+async function shareVideoByServer (video: VideoModel, t: Transaction) {
const serverAccount = await getServerAccount()
- await db.VideoShare.create({
+ await VideoShareModel.create({
accountId: serverAccount.id,
videoId: video.id
}, { transaction: t })
-import { CONFIG } from '../../initializers/constants'
-import { VideoInstance } from '../../models/video/video-interface'
-import { VideoChannelInstance } from '../../models/video/video-channel-interface'
-import { VideoAbuseInstance } from '../../models/video/video-abuse-interface'
-import { AccountFollowInstance } from '../../models/account/account-follow-interface'
-import { AccountInstance } from '../../models/account/account-interface'
+import { CONFIG } from '../../initializers'
+import { AccountModel } from '../../models/account/account'
+import { AccountFollowModel } from '../../models/account/account-follow'
+import { VideoModel } from '../../models/video/video'
+import { VideoAbuseModel } from '../../models/video/video-abuse'
+import { VideoChannelModel } from '../../models/video/video-channel'
-function getVideoActivityPubUrl (video: VideoInstance) {
+function getVideoActivityPubUrl (video: VideoModel) {
return CONFIG.WEBSERVER.URL + '/videos/watch/' + video.uuid
}
-function getVideoChannelActivityPubUrl (videoChannel: VideoChannelInstance) {
+function getVideoChannelActivityPubUrl (videoChannel: VideoChannelModel) {
return CONFIG.WEBSERVER.URL + '/video-channels/' + videoChannel.uuid
}
return CONFIG.WEBSERVER.URL + '/account/' + accountName
}
-function getVideoAbuseActivityPubUrl (videoAbuse: VideoAbuseInstance) {
+function getVideoAbuseActivityPubUrl (videoAbuse: VideoAbuseModel) {
return CONFIG.WEBSERVER.URL + '/admin/video-abuses/' + videoAbuse.id
}
-function getVideoViewActivityPubUrl (byAccount: AccountInstance, video: VideoInstance) {
+function getVideoViewActivityPubUrl (byAccount: AccountModel, video: VideoModel) {
return video.url + '/views/' + byAccount.uuid + '/' + new Date().toISOString()
}
-function getVideoLikeActivityPubUrl (byAccount: AccountInstance, video: VideoInstance) {
+function getVideoLikeActivityPubUrl (byAccount: AccountModel, video: VideoModel) {
return byAccount.url + '/likes/' + video.id
}
-function getVideoDislikeActivityPubUrl (byAccount: AccountInstance, video: VideoInstance) {
+function getVideoDislikeActivityPubUrl (byAccount: AccountModel, video: VideoModel) {
return byAccount.url + '/dislikes/' + video.id
}
-function getAccountFollowActivityPubUrl (accountFollow: AccountFollowInstance) {
+function getAccountFollowActivityPubUrl (accountFollow: AccountFollowModel) {
const me = accountFollow.AccountFollower
const following = accountFollow.AccountFollowing
return me.url + '/follows/' + following.id
}
-function getAccountFollowAcceptActivityPubUrl (accountFollow: AccountFollowInstance) {
+function getAccountFollowAcceptActivityPubUrl (accountFollow: AccountFollowModel) {
const follower = accountFollow.AccountFollower
const me = accountFollow.AccountFollowing
return follower.url + '/accepts/follows/' + me.id
}
-function getAnnounceActivityPubUrl (originalUrl: string, byAccount: AccountInstance) {
+function getAnnounceActivityPubUrl (originalUrl: string, byAccount: AccountModel) {
return originalUrl + '/announces/' + byAccount.id
}
-import { VideoChannelObject } from '../../../shared/models/activitypub/objects/video-channel-object'
-import { isVideoChannelObjectValid } from '../../helpers/custom-validators/activitypub/video-channels'
-import { logger } from '../../helpers/logger'
-import { doRequest } from '../../helpers/requests'
-import { database as db } from '../../initializers'
-import { ACTIVITY_PUB } from '../../initializers/constants'
-import { AccountInstance } from '../../models/account/account-interface'
+import { VideoChannelObject } from '../../../shared/models/activitypub/objects'
+import { doRequest, logger } from '../../helpers'
+import { isVideoChannelObjectValid } from '../../helpers/custom-validators/activitypub'
+import { ACTIVITY_PUB } from '../../initializers'
+import { AccountModel } from '../../models/account/account'
+import { VideoChannelModel } from '../../models/video/video-channel'
import { videoChannelActivityObjectToDBAttributes } from './process/misc'
-async function getOrCreateVideoChannel (ownerAccount: AccountInstance, videoChannelUrl: string) {
- let videoChannel = await db.VideoChannel.loadByUrl(videoChannelUrl)
+async function getOrCreateVideoChannel (ownerAccount: AccountModel, videoChannelUrl: string) {
+ let videoChannel = await VideoChannelModel.loadByUrl(videoChannelUrl)
// We don't have this account in our database, fetch it on remote
if (!videoChannel) {
return videoChannel
}
-async function fetchRemoteVideoChannel (ownerAccount: AccountInstance, videoChannelUrl: string) {
+async function fetchRemoteVideoChannel (ownerAccount: AccountModel, videoChannelUrl: string) {
const options = {
uri: videoChannelUrl,
method: 'GET',
}
const videoChannelAttributes = videoChannelActivityObjectToDBAttributes(videoChannelJSON, ownerAccount)
- const videoChannel = db.VideoChannel.build(videoChannelAttributes)
+ const videoChannel = new VideoChannelModel(videoChannelAttributes)
videoChannel.Account = ownerAccount
return videoChannel
import * as request from 'request'
import { Transaction } from 'sequelize'
import { ActivityIconObject } from '../../../shared/index'
-import { doRequest, doRequestAndSaveToFile } from '../../helpers/requests'
-import { CONFIG, REMOTE_SCHEME, STATIC_PATHS } from '../../initializers/constants'
-import { AccountInstance } from '../../models/account/account-interface'
-import { VideoInstance } from '../../models/video/video-interface'
-import { sendLikeToOrigin } from './index'
-import { sendCreateDislikeToOrigin, sendCreateDislikeToVideoFollowers } from './send/send-create'
-import { sendLikeToVideoFollowers } from './send/send-like'
+import { doRequest, doRequestAndSaveToFile } from '../../helpers'
+import { CONFIG, REMOTE_SCHEME, STATIC_PATHS } from '../../initializers'
+import { AccountModel } from '../../models/account/account'
+import { VideoModel } from '../../models/video/video'
import {
+ sendCreateDislikeToOrigin,
+ sendCreateDislikeToVideoFollowers,
+ sendLikeToOrigin,
+ sendLikeToVideoFollowers,
sendUndoDislikeToOrigin,
sendUndoDislikeToVideoFollowers,
sendUndoLikeToOrigin,
sendUndoLikeToVideoFollowers
-} from './send/send-undo'
+} from './send'
-function fetchRemoteVideoPreview (video: VideoInstance) {
+function fetchRemoteVideoPreview (video: VideoModel) {
// FIXME: use url
const host = video.VideoChannel.Account.Server.host
const path = join(STATIC_PATHS.PREVIEWS, video.getPreviewName())
return request.get(REMOTE_SCHEME.HTTP + '://' + host + path)
}
-async function fetchRemoteVideoDescription (video: VideoInstance) {
+async function fetchRemoteVideoDescription (video: VideoModel) {
// FIXME: use url
const host = video.VideoChannel.Account.Server.host
const path = video.getDescriptionPath()
return body.description ? body.description : ''
}
-function generateThumbnailFromUrl (video: VideoInstance, icon: ActivityIconObject) {
+function generateThumbnailFromUrl (video: VideoModel, icon: ActivityIconObject) {
const thumbnailName = video.getThumbnailName()
const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, thumbnailName)
}
async function sendVideoRateChangeToFollowers (
- account: AccountInstance,
- video: VideoInstance,
+ account: AccountModel,
+ video: VideoModel,
likes: number,
dislikes: number,
t: Transaction
}
async function sendVideoRateChangeToOrigin (
- account: AccountInstance,
- video: VideoInstance,
+ account: AccountModel,
+ video: VideoModel,
likes: number,
dislikes: number,
t: Transaction
import * as asyncLRU from 'async-lru'
-import { join } from 'path'
import { createWriteStream } from 'fs'
-
-import { database as db, CONFIG, CACHE } from '../../initializers'
+import { join } from 'path'
import { logger, unlinkPromise } from '../../helpers'
-import { VideoInstance } from '../../models'
-import { fetchRemoteVideoPreview } from '../activitypub/videos'
+import { CACHE, CONFIG } from '../../initializers'
+import { VideoModel } from '../../models/video/video'
+import { fetchRemoteVideoPreview } from '../activitypub'
class VideosPreviewCache {
}
private async loadPreviews (key: string) {
- const video = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(key)
+ const video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(key)
if (!video) return undefined
if (video.isOwned()) return join(CONFIG.STORAGE.PREVIEWS_DIR, video.getPreviewName())
return res
}
- private saveRemotePreviewAndReturnPath (video: VideoInstance) {
+ private saveRemotePreviewAndReturnPath (video: VideoModel) {
const req = fetchRemoteVideoPreview(video)
return new Promise<string>((res, rej) => {
-import { logger } from '../../../helpers'
-import { doRequest } from '../../../helpers/requests'
+import { doRequest, logger } from '../../../helpers'
import { ActivityPubHttpPayload, computeBody, maybeRetryRequestLater } from './activitypub-http-job-scheduler'
async function process (payload: ActivityPubHttpPayload, jobId: number) {
-import { logger } from '../../../helpers'
-import { doRequest } from '../../../helpers/requests'
-import { ACTIVITY_PUB } from '../../../initializers/constants'
-import { processActivities } from '../../activitypub/process/process'
+import { doRequest, logger } from '../../../helpers'
+import { ACTIVITY_PUB } from '../../../initializers'
+import { processActivities } from '../../activitypub/process'
import { ActivityPubHttpPayload } from './activitypub-http-job-scheduler'
async function process (payload: ActivityPubHttpPayload, jobId: number) {
import { JobCategory } from '../../../../shared'
-import { buildSignedActivity } from '../../../helpers/activitypub'
-import { logger } from '../../../helpers/logger'
-import { ACTIVITY_PUB } from '../../../initializers/constants'
-import { database as db } from '../../../initializers/database'
+import { buildSignedActivity, logger } from '../../../helpers'
+import { ACTIVITY_PUB } from '../../../initializers'
+import { AccountModel } from '../../../models/account/account'
import { JobHandler, JobScheduler } from '../job-scheduler'
import * as activitypubHttpBroadcastHandler from './activitypub-http-broadcast-handler'
let body = payload.body
if (payload.signatureAccountId) {
- const accountSignature = await db.Account.load(payload.signatureAccountId)
+ const accountSignature = await AccountModel.load(payload.signatureAccountId)
if (!accountSignature) throw new Error('Unknown signature account id.')
body = await buildSignedActivity(accountSignature, payload.body)
}
-import { logger } from '../../../helpers'
-import { doRequest } from '../../../helpers/requests'
+import { doRequest, logger } from '../../../helpers'
import { ActivityPubHttpPayload, computeBody, maybeRetryRequestLater } from './activitypub-http-job-scheduler'
async function process (payload: ActivityPubHttpPayload, jobId: number) {
import * as Sequelize from 'sequelize'
import { JobCategory } from '../../../shared'
import { logger } from '../../helpers'
-import { database as db, JOB_STATES, JOBS_FETCH_LIMIT_PER_CYCLE, JOBS_FETCHING_INTERVAL } from '../../initializers'
-import { JobInstance } from '../../models'
+import { JOB_STATES, JOBS_FETCH_LIMIT_PER_CYCLE, JOBS_FETCHING_INTERVAL } from '../../initializers'
+import { JobModel } from '../../models/job/job'
export interface JobHandler<P, T> {
process (data: object, jobId: number): Promise<T>
logger.info('Jobs scheduler %s activated.', this.jobCategory)
- const jobsQueue = queue<JobInstance, JobQueueCallback>(this.processJob.bind(this))
+ const jobsQueue = queue<JobModel, JobQueueCallback>(this.processJob.bind(this))
// Finish processing jobs from a previous start
const state = JOB_STATES.PROCESSING
try {
- const jobs = await db.Job.listWithLimitByCategory(limit, state, this.jobCategory)
+ const jobs = await JobModel.listWithLimitByCategory(limit, state, this.jobCategory)
this.enqueueJobs(jobsQueue, jobs)
} catch (err) {
const state = JOB_STATES.PENDING
try {
- const jobs = await db.Job.listWithLimitByCategory(limit, state, this.jobCategory)
+ const jobs = await JobModel.listWithLimitByCategory(limit, state, this.jobCategory)
this.enqueueJobs(jobsQueue, jobs)
} catch (err) {
const options = { transaction }
- return db.Job.create(createQuery, options)
+ return JobModel.create(createQuery, options)
}
- private enqueueJobs (jobsQueue: AsyncQueue<JobInstance>, jobs: JobInstance[]) {
+ private enqueueJobs (jobsQueue: AsyncQueue<JobModel>, jobs: JobModel[]) {
jobs.forEach(job => jobsQueue.push(job))
}
- private async processJob (job: JobInstance, callback: (err: Error) => void) {
+ private async processJob (job: JobModel, callback: (err: Error) => void) {
const jobHandler = this.jobHandlers[job.handlerName]
if (jobHandler === undefined) {
const errorString = 'Unknown job handler ' + job.handlerName + ' for job ' + job.id
return callback(null)
}
- private async onJobError (jobHandler: JobHandler<P, T>, job: JobInstance, err: Error) {
+ private async onJobError (jobHandler: JobHandler<P, T>, job: JobModel, err: Error) {
job.state = JOB_STATES.ERROR
try {
}
}
- private async onJobSuccess (jobHandler: JobHandler<P, T>, job: JobInstance, jobResult: T) {
+ private async onJobSuccess (jobHandler: JobHandler<P, T>, job: JobModel, jobResult: T) {
job.state = JOB_STATES.SUCCESS
try {
import { JobCategory } from '../../../../shared'
+import { VideoModel } from '../../../models/video/video'
import { JobHandler, JobScheduler } from '../job-scheduler'
+
import * as videoFileOptimizer from './video-file-optimizer-handler'
import * as videoFileTranscoder from './video-file-transcoder-handler'
-import { VideoInstance } from '../../../models/video/video-interface'
type TranscodingJobPayload = {
videoUUID: string
resolution?: number
}
-const jobHandlers: { [ handlerName: string ]: JobHandler<TranscodingJobPayload, VideoInstance> } = {
+const jobHandlers: { [ handlerName: string ]: JobHandler<TranscodingJobPayload, VideoModel> } = {
videoFileOptimizer,
videoFileTranscoder
}
import * as Bluebird from 'bluebird'
import { computeResolutionsToTranscode, logger } from '../../../helpers'
-import { database as db } from '../../../initializers/database'
-import { VideoInstance } from '../../../models'
-import { sendAddVideo } from '../../activitypub/send/send-add'
+import { sequelizeTypescript } from '../../../initializers'
+import { VideoModel } from '../../../models/video/video'
+import { shareVideoByServer } from '../../activitypub'
+import { sendAddVideo } from '../../activitypub/send'
import { JobScheduler } from '../job-scheduler'
import { TranscodingJobPayload } from './transcoding-job-scheduler'
-import { shareVideoByServer } from '../../activitypub/share'
async function process (data: TranscodingJobPayload, jobId: number) {
- const video = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(data.videoUUID)
+ const video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(data.videoUUID)
// No video, maybe deleted?
if (!video) {
logger.info('Do not process job %d, video does not exist.', jobId, { videoUUID: video.uuid })
return Promise.resolve()
}
-async function onSuccess (jobId: number, video: VideoInstance, jobScheduler: JobScheduler<TranscodingJobPayload, VideoInstance>) {
+async function onSuccess (jobId: number, video: VideoModel, jobScheduler: JobScheduler<TranscodingJobPayload, VideoModel>) {
if (video === undefined) return undefined
logger.info('Job %d is a success.', jobId)
// Maybe the video changed in database, refresh it
- const videoDatabase = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(video.uuid)
+ const videoDatabase = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(video.uuid)
// Video does not exist anymore
if (!videoDatabase) return undefined
if (resolutionsEnabled.length !== 0) {
try {
- await db.sequelize.transaction(async t => {
+ await sequelizeTypescript.transaction(async t => {
const tasks: Bluebird<any>[] = []
for (const resolution of resolutionsEnabled) {
import { VideoResolution } from '../../../../shared'
import { logger } from '../../../helpers'
-import { database as db } from '../../../initializers/database'
-import { VideoInstance } from '../../../models'
-import { sendUpdateVideo } from '../../activitypub/send/send-update'
+import { VideoModel } from '../../../models/video/video'
+import { sendUpdateVideo } from '../../activitypub/send'
async function process (data: { videoUUID: string, resolution: VideoResolution }, jobId: number) {
- const video = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(data.videoUUID)
+ const video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(data.videoUUID)
// No video, maybe deleted?
if (!video) {
logger.info('Do not process job %d, video does not exist.', jobId, { videoUUID: video.uuid })
return Promise.resolve()
}
-async function onSuccess (jobId: number, video: VideoInstance) {
+async function onSuccess (jobId: number, video: VideoModel) {
if (video === undefined) return undefined
logger.info('Job %d is a success.', jobId)
// Maybe the video changed in database, refresh it
- const videoDatabase = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(video.uuid)
+ const videoDatabase = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(video.uuid)
// Video does not exist anymore
if (!videoDatabase) return undefined
-import { OAuthClientInstance, UserInstance } from '../models'
-import { database as db } from '../initializers/database'
import { logger } from '../helpers'
+import { UserModel } from '../models/account/user'
+import { OAuthClientModel } from '../models/oauth/oauth-client'
+import { OAuthTokenModel } from '../models/oauth/oauth-token'
type TokenInfo = { accessToken: string, refreshToken: string, accessTokenExpiresAt: Date, refreshTokenExpiresAt: Date }
function getAccessToken (bearerToken: string) {
logger.debug('Getting access token (bearerToken: ' + bearerToken + ').')
- return db.OAuthToken.getByTokenAndPopulateUser(bearerToken)
+ return OAuthTokenModel.getByTokenAndPopulateUser(bearerToken)
}
function getClient (clientId: string, clientSecret: string) {
logger.debug('Getting Client (clientId: ' + clientId + ', clientSecret: ' + clientSecret + ').')
- return db.OAuthClient.getByIdAndSecret(clientId, clientSecret)
+ return OAuthClientModel.getByIdAndSecret(clientId, clientSecret)
}
function getRefreshToken (refreshToken: string) {
logger.debug('Getting RefreshToken (refreshToken: ' + refreshToken + ').')
- return db.OAuthToken.getByRefreshTokenAndPopulateClient(refreshToken)
+ return OAuthTokenModel.getByRefreshTokenAndPopulateClient(refreshToken)
}
async function getUser (username: string, password: string) {
logger.debug('Getting User (username: ' + username + ', password: ******).')
- const user = await db.User.getByUsername(username)
+ const user = await UserModel.getByUsername(username)
if (!user) return null
const passwordMatch = await user.isPasswordMatch(password)
}
async function revokeToken (tokenInfo: TokenInfo) {
- const token = await db.OAuthToken.getByRefreshTokenAndPopulateUser(tokenInfo.refreshToken)
+ const token = await OAuthTokenModel.getByRefreshTokenAndPopulateUser(tokenInfo.refreshToken)
if (token) token.destroy()
/*
return expiredToken
}
-async function saveToken (token: TokenInfo, client: OAuthClientInstance, user: UserInstance) {
+async function saveToken (token: TokenInfo, client: OAuthClientModel, user: UserModel) {
logger.debug('Saving token ' + token.accessToken + ' for client ' + client.id + ' and user ' + user.id + '.')
const tokenToCreate = {
userId: user.id
}
- const tokenCreated = await db.OAuthToken.create(tokenToCreate)
+ const tokenCreated = await OAuthTokenModel.create(tokenToCreate)
const tokenToReturn = Object.assign(tokenCreated, { client, user })
return tokenToReturn
import * as Sequelize from 'sequelize'
-import { createPrivateAndPublicKeys } from '../helpers/peertube-crypto'
-import { database as db } from '../initializers'
-import { CONFIG } from '../initializers/constants'
-import { UserInstance } from '../models'
+import { createPrivateAndPublicKeys, logger } from '../helpers'
+import { CONFIG, sequelizeTypescript } from '../initializers'
+import { AccountModel } from '../models/account/account'
+import { UserModel } from '../models/account/user'
+import { getAccountActivityPubUrl } from './activitypub'
import { createVideoChannel } from './video-channel'
-import { logger } from '../helpers/logger'
-import { getAccountActivityPubUrl } from './activitypub/url'
-async function createUserAccountAndChannel (user: UserInstance, validateUser = true) {
- const { account, videoChannel } = await db.sequelize.transaction(async t => {
+async function createUserAccountAndChannel (user: UserModel, validateUser = true) {
+ const { account, videoChannel } = await sequelizeTypescript.transaction(async t => {
const userOptions = {
transaction: t,
validate: validateUser
async function createLocalAccountWithoutKeys (name: string, userId: number, applicationId: number, t: Sequelize.Transaction) {
const url = getAccountActivityPubUrl(name)
- const accountInstance = db.Account.build({
+ const accountInstance = new AccountModel({
name,
url,
publicKey: null,
import * as Sequelize from 'sequelize'
import { VideoChannelCreate } from '../../shared/models'
-import { database as db } from '../initializers'
-import { AccountInstance } from '../models'
-import { getVideoChannelActivityPubUrl } from './activitypub/url'
+import { AccountModel } from '../models/account/account'
+import { VideoChannelModel } from '../models/video/video-channel'
+import { getVideoChannelActivityPubUrl } from './activitypub'
-async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountInstance, t: Sequelize.Transaction) {
+async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountModel, t: Sequelize.Transaction) {
const videoChannelData = {
name: videoChannelInfo.name,
description: videoChannelInfo.description,
accountId: account.id
}
- const videoChannel = db.VideoChannel.build(videoChannelData)
+ const videoChannel = VideoChannelModel.build(videoChannelData)
videoChannel.set('url', getVideoChannelActivityPubUrl(videoChannel))
const options = { transaction: t }
import { NextFunction, Request, RequestHandler, Response } from 'express'
import { ActivityPubSignature } from '../../shared'
import { isSignatureVerified, logger } from '../helpers'
-import { database as db } from '../initializers'
-import { ACCEPT_HEADERS, ACTIVITY_PUB } from '../initializers/constants'
-import { fetchRemoteAccount, saveAccountAndServerIfNotExist } from '../lib/activitypub/account'
+import { ACCEPT_HEADERS, ACTIVITY_PUB } from '../initializers'
+import { fetchRemoteAccount, saveAccountAndServerIfNotExist } from '../lib/activitypub'
+import { AccountModel } from '../models/account/account'
async function checkSignature (req: Request, res: Response, next: NextFunction) {
const signatureObject: ActivityPubSignature = req.body.signature
logger.debug('Checking signature of account %s...', signatureObject.creator)
- let account = await db.Account.loadByUrl(signatureObject.creator)
+ let account = await AccountModel.loadByUrl(signatureObject.creator)
// We don't have this account in our database, fetch it on remote
if (!account) {
-import 'express-validator'
import * as express from 'express'
-
+import 'express-validator'
import { SortType } from '../helpers'
-import { database } from '../initializers'
function setUsersSort (req: express.Request, res: express.Response, next: express.NextFunction) {
if (!req.query.sort) req.query.sort = '-createdAt'
// If we want to sort onto the BlacklistedVideos relation, we won't specify it in the query parameter ...
newSort.sortModel = undefined
} else {
- newSort.sortModel = database.Video
+ newSort.sortModel = 'Video'
}
newSort.sortValue = req.query.sort
-import 'express-validator'
import * as express from 'express'
-
-import { UserInstance } from '../models'
+import 'express-validator'
import { UserRight } from '../../shared'
import { logger } from '../helpers'
+import { UserModel } from '../models/account/user'
function ensureUserHasRight (userRight: UserRight) {
return function (req: express.Request, res: express.Response, next: express.NextFunction) {
- const user: UserInstance = res.locals.oauth.token.user
+ const user = res.locals.oauth.token.user as UserModel
if (user.hasRight(userRight) === false) {
logger.info('User %s does not have right %s to access to %s.', user.username, UserRight[userRight], req.path)
return res.sendStatus(403)
import * as express from 'express'
import { param } from 'express-validator/check'
-import { logger, isLocalAccountNameExist } from '../../helpers'
-import { isAccountNameValid } from '../../helpers/custom-validators/accounts'
+import { logger } from '../../helpers'
+import { isAccountNameValid, isLocalAccountNameExist } from '../../helpers/custom-validators/accounts'
import { areValidationErrors } from './utils'
const localAccountValidator = [
import * as express from 'express'
import { body } from 'express-validator/check'
-import { isRootActivityValid, logger } from '../../../helpers'
+import { logger } from '../../../helpers'
+import { isRootActivityValid } from '../../../helpers/custom-validators/activitypub'
import { areValidationErrors } from '../utils'
const activityPubValidator = [
import * as express from 'express'
import { body } from 'express-validator/check'
-import { isDateValid, isSignatureCreatorValid, isSignatureTypeValid, isSignatureValueValid, logger } from '../../../helpers'
+import { logger } from '../../../helpers'
+import { isSignatureCreatorValid, isSignatureTypeValid, isSignatureValueValid } from '../../../helpers/custom-validators/activitypub'
+import { isDateValid } from '../../../helpers/custom-validators/misc'
import { areValidationErrors } from '../utils'
const signatureValidator = [
import * as express from 'express'
import { body, param } from 'express-validator/check'
-import { isTestInstance } from '../../helpers/core-utils'
+import { getServerAccount, isTestInstance, logger } from '../../helpers'
+import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
import { isEachUniqueHostValid } from '../../helpers/custom-validators/servers'
-import { logger } from '../../helpers/logger'
-import { CONFIG, database as db } from '../../initializers'
+import { CONFIG } from '../../initializers'
+import { AccountFollowModel } from '../../models/account/account-follow'
import { areValidationErrors } from './utils'
-import { getServerAccount } from '../../helpers/utils'
-import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
const followValidator = [
body('hosts').custom(isEachUniqueHostValid).withMessage('Should have an array of unique hosts'),
if (areValidationErrors(req, res)) return
const serverAccount = await getServerAccount()
- const follow = await db.AccountFollow.loadByAccountAndTarget(serverAccount.id, req.params.accountId)
+ const follow = await AccountFollowModel.loadByAccountAndTarget(serverAccount.id, req.params.accountId)
if (!follow) {
return res.status(404)
import * as express from 'express'
import { query } from 'express-validator/check'
import { join } from 'path'
-import { isIdOrUUIDValid, isTestInstance, logger } from '../../helpers'
+import { isTestInstance, logger } from '../../helpers'
+import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
+import { isVideoExist } from '../../helpers/custom-validators/videos'
import { CONFIG } from '../../initializers'
import { areValidationErrors } from './utils'
-import { isVideoExist } from '../../helpers/custom-validators/videos'
const urlShouldStartWith = CONFIG.WEBSERVER.SCHEME + '://' + join(CONFIG.WEBSERVER.HOST, 'videos', 'watch') + '/'
const videoWatchRegex = new RegExp('([^/]+)$')
import { query } from 'express-validator/check'
import * as express from 'express'
-
import { logger } from '../../helpers'
import { SORTABLE_COLUMNS } from '../../initializers'
import { areValidationErrors } from './utils'
import * as express from 'express'
import 'express-validator'
import { body, param } from 'express-validator/check'
+import { isSignupAllowed, logger } from '../../helpers'
+import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
import {
- isIdOrUUIDValid,
- isSignupAllowed,
isUserDisplayNSFWValid,
isUserPasswordValid,
isUserRoleValid,
isUserUsernameValid,
- isUserVideoQuotaValid,
- logger
-} from '../../helpers'
+ isUserVideoQuotaValid
+} from '../../helpers/custom-validators/users'
import { isVideoExist } from '../../helpers/custom-validators/videos'
-import { database as db } from '../../initializers/database'
+import { UserModel } from '../../models/account/user'
import { areValidationErrors } from './utils'
const usersAddValidator = [
// ---------------------------------------------------------------------------
async function checkUserIdExist (id: number, res: express.Response) {
- const user = await db.User.loadById(id)
+ const user = await UserModel.loadById(id)
if (!user) {
res.status(404)
}
async function checkUserNameOrEmailDoesNotAlreadyExist (username: string, email: string, res: express.Response) {
- const user = await db.User.loadByUsernameOrEmail(username, email)
+ const user = await UserModel.loadByUsernameOrEmail(username, email)
if (user) {
res.status(409)
import * as express from 'express'
import { validationResult } from 'express-validator/check'
-
import { logger } from '../../helpers'
function areValidationErrors (req: express.Request, res: express.Response) {
import * as express from 'express'
import { param } from 'express-validator/check'
-import { isIdOrUUIDValid, logger } from '../../helpers'
+import { logger } from '../../helpers'
+import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
import { isVideoExist } from '../../helpers/custom-validators/videos'
-import { database as db } from '../../initializers/database'
-import { VideoInstance } from '../../models/video/video-interface'
+import { VideoModel } from '../../models/video/video'
+import { VideoBlacklistModel } from '../../models/video/video-blacklist'
import { areValidationErrors } from './utils'
const videosBlacklistRemoveValidator = [
}
// ---------------------------------------------------------------------------
-function checkVideoIsBlacklistable (video: VideoInstance, res: express.Response) {
+function checkVideoIsBlacklistable (video: VideoModel, res: express.Response) {
if (video.isOwned() === true) {
res.status(403)
.json({ error: 'Cannot blacklist a local video' })
return true
}
-async function checkVideoIsBlacklisted (video: VideoInstance, res: express.Response) {
- const blacklistedVideo = await db.BlacklistedVideo.loadByVideoId(video.id)
+async function checkVideoIsBlacklisted (video: VideoModel, res: express.Response) {
+ const blacklistedVideo = await VideoBlacklistModel.loadByVideoId(video.id)
if (!blacklistedVideo) {
res.status(404)
.send('Blacklisted video not found')
import * as express from 'express'
import { body, param } from 'express-validator/check'
import { UserRight } from '../../../shared'
-import { isIdValid } from '../../helpers/custom-validators/misc'
+import { logger } from '../../helpers'
+import { isAccountIdExist } from '../../helpers/custom-validators/accounts'
+import { isIdOrUUIDValid, isIdValid } from '../../helpers/custom-validators/misc'
import {
isVideoChannelDescriptionValid,
isVideoChannelExist,
isVideoChannelNameValid
} from '../../helpers/custom-validators/video-channels'
-import { isIdOrUUIDValid } from '../../helpers/index'
-import { logger } from '../../helpers/logger'
-import { database as db } from '../../initializers'
-import { UserInstance } from '../../models'
+import { UserModel } from '../../models/account/user'
+import { VideoChannelModel } from '../../models/video/video-channel'
+import { VideoChannelShareModel } from '../../models/video/video-channel-share'
import { areValidationErrors } from './utils'
-import { isAccountIdExist } from '../../helpers/custom-validators/accounts'
-import { VideoChannelInstance } from '../../models/video/video-channel-interface'
const listVideoAccountChannelsValidator = [
param('accountId').custom(isIdOrUUIDValid).withMessage('Should have a valid account id'),
if (areValidationErrors(req, res)) return
if (!await isVideoChannelExist(req.params.id, res)) return
- const share = await db.VideoChannelShare.load(res.locals.video.id, req.params.accountId, undefined)
+ const share = await VideoChannelShareModel.load(res.locals.video.id, req.params.accountId, undefined)
if (!share) {
return res.status(404)
.end()
// ---------------------------------------------------------------------------
-function checkUserCanDeleteVideoChannel (user: UserInstance, videoChannel: VideoChannelInstance, res: express.Response) {
+function checkUserCanDeleteVideoChannel (user: UserModel, videoChannel: VideoChannelModel, res: express.Response) {
// Retrieve the user who did the request
if (videoChannel.isOwned() === false) {
res.status(403)
}
async function checkVideoChannelIsNotTheLastOne (res: express.Response) {
- const count = await db.VideoChannel.countByAccount(res.locals.oauth.token.User.Account.id)
+ const count = await VideoChannelModel.countByAccount(res.locals.oauth.token.User.Account.id)
if (count <= 1) {
res.status(409)
import * as express from 'express'
+import 'express-validator'
import { body, param, query } from 'express-validator/check'
import { UserRight, VideoPrivacy } from '../../../shared'
+import { getDurationFromVideoFile, logger } from '../../helpers'
import { isIdOrUUIDValid, isIdValid } from '../../helpers/custom-validators/misc'
import {
isVideoAbuseReasonValid,
isVideoRatingTypeValid,
isVideoTagsValid
} from '../../helpers/custom-validators/videos'
-import { getDurationFromVideoFile } from '../../helpers/ffmpeg-utils'
-import { logger } from '../../helpers/logger'
import { CONSTRAINTS_FIELDS } from '../../initializers'
-import { database as db } from '../../initializers/database'
-import { UserInstance } from '../../models/account/user-interface'
-import { VideoInstance } from '../../models/video/video-interface'
+import { UserModel } from '../../models/account/user'
+import { VideoModel } from '../../models/video/video'
+import { VideoChannelModel } from '../../models/video/video-channel'
+import { VideoShareModel } from '../../models/video/video-share'
import { authenticate } from '../oauth'
import { areValidationErrors } from './utils'
const videoFile: Express.Multer.File = req.files['videofile'][0]
const user = res.locals.oauth.token.User
- const videoChannel = await db.VideoChannel.loadByIdAndAccount(req.body.channelId, user.Account.id)
+ const videoChannel = await VideoChannelModel.loadByIdAndAccount(req.body.channelId, user.Account.id)
if (!videoChannel) {
res.status(400)
.json({ error: 'Unknown video video channel for this account.' })
if (areValidationErrors(req, res)) return
if (!await isVideoExist(req.params.id, res)) return
- const share = await db.VideoShare.load(req.params.accountId, res.locals.video.id, undefined)
+ const share = await VideoShareModel.load(req.params.accountId, res.locals.video.id, undefined)
if (!share) {
return res.status(404)
.end()
// ---------------------------------------------------------------------------
-function checkUserCanDeleteVideo (user: UserInstance, video: VideoInstance, res: express.Response) {
+function checkUserCanDeleteVideo (user: UserModel, video: VideoModel, res: express.Response) {
// Retrieve the user who did the request
if (video.isOwned() === false) {
res.status(403)
import * as express from 'express'
import { query } from 'express-validator/check'
+import { logger } from '../../helpers'
import { isWebfingerResourceValid } from '../../helpers/custom-validators/webfinger'
-import { logger } from '../../helpers/logger'
-import { database as db } from '../../initializers'
+import { AccountModel } from '../../models/account/account'
import { areValidationErrors } from './utils'
const webfingerValidator = [
const nameWithHost = req.query.resource.substr(5)
const [ name ] = nameWithHost.split('@')
- const account = await db.Account.loadLocalByName(name)
+ const account = await AccountModel.loadLocalByName(name)
if (!account) {
return res.status(404)
.send({ error: 'Account not found' })
+++ /dev/null
-import * as Bluebird from 'bluebird'
-import * as Sequelize from 'sequelize'
-import { AccountFollow, FollowState } from '../../../shared/models/accounts/follow.model'
-import { ResultList } from '../../../shared/models/result-list.model'
-import { AccountInstance } from './account-interface'
-
-export namespace AccountFollowMethods {
- export type LoadByAccountAndTarget = (
- accountId: number,
- targetAccountId: number,
- t?: Sequelize.Transaction
- ) => Bluebird<AccountFollowInstance>
-
- export type ListFollowingForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountFollowInstance>>
- export type ListFollowersForApi = (id: number, start: number, count: number, sort: string) => Bluebird< ResultList<AccountFollowInstance>>
-
- export type ListAcceptedFollowerUrlsForApi = (
- accountId: number[],
- t: Sequelize.Transaction,
- start?: number,
- count?: number
- ) => Promise< ResultList<string> >
- export type ListAcceptedFollowingUrlsForApi = (
- accountId: number[],
- t: Sequelize.Transaction,
- start?: number,
- count?: number
- ) => Promise< ResultList<string> >
- export type ListAcceptedFollowerSharedInboxUrls = (accountId: number[], t: Sequelize.Transaction) => Promise< ResultList<string> >
- export type ToFormattedJSON = (this: AccountFollowInstance) => AccountFollow
-}
-
-export interface AccountFollowClass {
- loadByAccountAndTarget: AccountFollowMethods.LoadByAccountAndTarget
- listFollowersForApi: AccountFollowMethods.ListFollowersForApi
- listFollowingForApi: AccountFollowMethods.ListFollowingForApi
-
- listAcceptedFollowerUrlsForApi: AccountFollowMethods.ListAcceptedFollowerUrlsForApi
- listAcceptedFollowingUrlsForApi: AccountFollowMethods.ListAcceptedFollowingUrlsForApi
- listAcceptedFollowerSharedInboxUrls: AccountFollowMethods.ListAcceptedFollowerSharedInboxUrls
-}
-
-export interface AccountFollowAttributes {
- accountId: number
- targetAccountId: number
- state: FollowState
-}
-
-export interface AccountFollowInstance extends AccountFollowClass, AccountFollowAttributes, Sequelize.Instance<AccountFollowAttributes> {
- id: number
- createdAt: Date
- updatedAt: Date
-
- AccountFollower?: AccountInstance
- AccountFollowing?: AccountInstance
-
- toFormattedJSON: AccountFollowMethods.ToFormattedJSON
-}
-
-export interface AccountFollowModel extends AccountFollowClass, Sequelize.Model<AccountFollowInstance, AccountFollowAttributes> {}
+import * as Bluebird from 'bluebird'
import { values } from 'lodash'
import * as Sequelize from 'sequelize'
-
-import { addMethodsToModel, getSort } from '../utils'
-import { AccountFollowAttributes, AccountFollowInstance, AccountFollowMethods } from './account-follow-interface'
+import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
+import { FollowState } from '../../../shared/models/accounts'
import { FOLLOW_STATES } from '../../initializers/constants'
+import { ServerModel } from '../server/server'
+import { getSort } from '../utils'
+import { AccountModel } from './account'
-let AccountFollow: Sequelize.Model<AccountFollowInstance, AccountFollowAttributes>
-let loadByAccountAndTarget: AccountFollowMethods.LoadByAccountAndTarget
-let listFollowingForApi: AccountFollowMethods.ListFollowingForApi
-let listFollowersForApi: AccountFollowMethods.ListFollowersForApi
-let listAcceptedFollowerUrlsForApi: AccountFollowMethods.ListAcceptedFollowerUrlsForApi
-let listAcceptedFollowingUrlsForApi: AccountFollowMethods.ListAcceptedFollowingUrlsForApi
-let listAcceptedFollowerSharedInboxUrls: AccountFollowMethods.ListAcceptedFollowerSharedInboxUrls
-let toFormattedJSON: AccountFollowMethods.ToFormattedJSON
-
-export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
- AccountFollow = sequelize.define<AccountFollowInstance, AccountFollowAttributes>('AccountFollow',
+@Table({
+ tableName: 'accountFollow',
+ indexes: [
{
- state: {
- type: DataTypes.ENUM(values(FOLLOW_STATES)),
- allowNull: false
- }
+ fields: [ 'accountId' ]
},
{
- indexes: [
- {
- fields: [ 'accountId' ]
- },
- {
- fields: [ 'targetAccountId' ]
- },
- {
- fields: [ 'accountId', 'targetAccountId' ],
- unique: true
- }
- ]
+ fields: [ 'targetAccountId' ]
+ },
+ {
+ fields: [ 'accountId', 'targetAccountId' ],
+ unique: true
}
- )
-
- const classMethods = [
- associate,
- loadByAccountAndTarget,
- listFollowingForApi,
- listFollowersForApi,
- listAcceptedFollowerUrlsForApi,
- listAcceptedFollowingUrlsForApi,
- listAcceptedFollowerSharedInboxUrls
]
- const instanceMethods = [
- toFormattedJSON
- ]
- addMethodsToModel(AccountFollow, classMethods, instanceMethods)
+})
+export class AccountFollowModel extends Model<AccountFollowModel> {
- return AccountFollow
-}
+ @AllowNull(false)
+ @Column(DataType.ENUM(values(FOLLOW_STATES)))
+ state: FollowState
-// ------------------------------ STATICS ------------------------------
+ @CreatedAt
+ createdAt: Date
-function associate (models) {
- AccountFollow.belongsTo(models.Account, {
+ @UpdatedAt
+ updatedAt: Date
+
+ @ForeignKey(() => AccountModel)
+ @Column
+ accountId: number
+
+ @BelongsTo(() => AccountModel, {
foreignKey: {
name: 'accountId',
allowNull: false
as: 'AccountFollower',
onDelete: 'CASCADE'
})
+ AccountFollower: AccountModel
- AccountFollow.belongsTo(models.Account, {
+ @ForeignKey(() => AccountModel)
+ @Column
+ targetAccountId: number
+
+ @BelongsTo(() => AccountModel, {
foreignKey: {
name: 'targetAccountId',
allowNull: false
as: 'AccountFollowing',
onDelete: 'CASCADE'
})
-}
+ AccountFollowing: AccountModel
-toFormattedJSON = function (this: AccountFollowInstance) {
- const follower = this.AccountFollower.toFormattedJSON()
- const following = this.AccountFollowing.toFormattedJSON()
-
- const json = {
- id: this.id,
- follower,
- following,
- state: this.state,
- createdAt: this.createdAt,
- updatedAt: this.updatedAt
- }
-
- return json
-}
-
-loadByAccountAndTarget = function (accountId: number, targetAccountId: number, t?: Sequelize.Transaction) {
- const query = {
- where: {
- accountId,
- targetAccountId
- },
- include: [
- {
- model: AccountFollow[ 'sequelize' ].models.Account,
- required: true,
- as: 'AccountFollower'
+ static loadByAccountAndTarget (accountId: number, targetAccountId: number, t?: Sequelize.Transaction) {
+ const query = {
+ where: {
+ accountId,
+ targetAccountId
},
- {
- model: AccountFollow['sequelize'].models.Account,
- required: true,
- as: 'AccountFollowing'
- }
- ],
- transaction: t
+ include: [
+ {
+ model: AccountModel,
+ required: true,
+ as: 'AccountFollower'
+ },
+ {
+ model: AccountModel,
+ required: true,
+ as: 'AccountFollowing'
+ }
+ ],
+ transaction: t
+ }
+
+ return AccountFollowModel.findOne(query)
}
- return AccountFollow.findOne(query)
-}
+ static listFollowingForApi (id: number, start: number, count: number, sort: string) {
+ const query = {
+ distinct: true,
+ offset: start,
+ limit: count,
+ order: [ getSort(sort) ],
+ include: [
+ {
+ model: AccountModel,
+ required: true,
+ as: 'AccountFollower',
+ where: {
+ id
+ }
+ },
+ {
+ model: AccountModel,
+ as: 'AccountFollowing',
+ required: true,
+ include: [ ServerModel ]
+ }
+ ]
+ }
-listFollowingForApi = function (id: number, start: number, count: number, sort: string) {
- const query = {
- distinct: true,
- offset: start,
- limit: count,
- order: [ getSort(sort) ],
- include: [
- {
- model: AccountFollow[ 'sequelize' ].models.Account,
- required: true,
- as: 'AccountFollower',
- where: {
- id
+ return AccountFollowModel.findAndCountAll(query)
+ .then(({ rows, count }) => {
+ return {
+ data: rows,
+ total: count
}
- },
- {
- model: AccountFollow['sequelize'].models.Account,
- as: 'AccountFollowing',
- required: true,
- include: [ AccountFollow['sequelize'].models.Server ]
- }
- ]
+ })
}
- return AccountFollow.findAndCountAll(query).then(({ rows, count }) => {
- return {
- data: rows,
- total: count
+ static listFollowersForApi (id: number, start: number, count: number, sort: string) {
+ const query = {
+ distinct: true,
+ offset: start,
+ limit: count,
+ order: [ getSort(sort) ],
+ include: [
+ {
+ model: AccountModel,
+ required: true,
+ as: 'AccountFollower',
+ include: [ ServerModel ]
+ },
+ {
+ model: AccountModel,
+ as: 'AccountFollowing',
+ required: true,
+ where: {
+ id
+ }
+ }
+ ]
}
- })
-}
-listFollowersForApi = function (id: number, start: number, count: number, sort: string) {
- const query = {
- distinct: true,
- offset: start,
- limit: count,
- order: [ getSort(sort) ],
- include: [
- {
- model: AccountFollow[ 'sequelize' ].models.Account,
- required: true,
- as: 'AccountFollower',
- include: [ AccountFollow['sequelize'].models.Server ]
- },
- {
- model: AccountFollow['sequelize'].models.Account,
- as: 'AccountFollowing',
- required: true,
- where: {
- id
+ return AccountFollowModel.findAndCountAll(query)
+ .then(({ rows, count }) => {
+ return {
+ data: rows,
+ total: count
}
- }
- ]
+ })
}
- return AccountFollow.findAndCountAll(query).then(({ rows, count }) => {
- return {
- data: rows,
- total: count
- }
- })
-}
+ static listAcceptedFollowerUrlsForApi (accountIds: number[], t: Sequelize.Transaction, start?: number, count?: number) {
+ return AccountFollowModel.createListAcceptedFollowForApiQuery('followers', accountIds, t, start, count)
+ }
-listAcceptedFollowerUrlsForApi = function (accountIds: number[], t: Sequelize.Transaction, start?: number, count?: number) {
- return createListAcceptedFollowForApiQuery('followers', accountIds, t, start, count)
-}
+ static listAcceptedFollowerSharedInboxUrls (accountIds: number[], t: Sequelize.Transaction) {
+ return AccountFollowModel.createListAcceptedFollowForApiQuery('followers', accountIds, t, undefined, undefined, 'sharedInboxUrl')
+ }
-listAcceptedFollowerSharedInboxUrls = function (accountIds: number[], t: Sequelize.Transaction) {
- return createListAcceptedFollowForApiQuery('followers', accountIds, t, undefined, undefined, 'sharedInboxUrl')
-}
+ static listAcceptedFollowingUrlsForApi (accountIds: number[], t: Sequelize.Transaction, start?: number, count?: number) {
+ return AccountFollowModel.createListAcceptedFollowForApiQuery('following', accountIds, t, start, count)
+ }
-listAcceptedFollowingUrlsForApi = function (accountIds: number[], t: Sequelize.Transaction, start?: number, count?: number) {
- return createListAcceptedFollowForApiQuery('following', accountIds, t, start, count)
-}
+ private static async createListAcceptedFollowForApiQuery (type: 'followers' | 'following',
+ accountIds: number[],
+ t: Sequelize.Transaction,
+ start?: number,
+ count?: number,
+ columnUrl = 'url') {
+ let firstJoin: string
+ let secondJoin: string
+
+ if (type === 'followers') {
+ firstJoin = 'targetAccountId'
+ secondJoin = 'accountId'
+ } else {
+ firstJoin = 'accountId'
+ secondJoin = 'targetAccountId'
+ }
-// ------------------------------ UTILS ------------------------------
-
-async function createListAcceptedFollowForApiQuery (
- type: 'followers' | 'following',
- accountIds: number[],
- t: Sequelize.Transaction,
- start?: number,
- count?: number,
- columnUrl = 'url'
-) {
- let firstJoin: string
- let secondJoin: string
-
- if (type === 'followers') {
- firstJoin = 'targetAccountId'
- secondJoin = 'accountId'
- } else {
- firstJoin = 'accountId'
- secondJoin = 'targetAccountId'
- }
+ const selections = [ '"Follows"."' + columnUrl + '" AS "url"', 'COUNT(*) AS "total"' ]
+ const tasks: Bluebird<any>[] = []
- const selections = [ '"Follows"."' + columnUrl + '" AS "url"', 'COUNT(*) AS "total"' ]
- const tasks: Promise<any>[] = []
+ for (const selection of selections) {
+ let query = 'SELECT ' + selection + ' FROM "account" ' +
+ 'INNER JOIN "accountFollow" ON "accountFollow"."' + firstJoin + '" = "account"."id" ' +
+ 'INNER JOIN "account" AS "Follows" ON "accountFollow"."' + secondJoin + '" = "Follows"."id" ' +
+ 'WHERE "account"."id" = ANY ($accountIds) AND "accountFollow"."state" = \'accepted\' '
- for (const selection of selections) {
- let query = 'SELECT ' + selection + ' FROM "Accounts" ' +
- 'INNER JOIN "AccountFollows" ON "AccountFollows"."' + firstJoin + '" = "Accounts"."id" ' +
- 'INNER JOIN "Accounts" AS "Follows" ON "AccountFollows"."' + secondJoin + '" = "Follows"."id" ' +
- 'WHERE "Accounts"."id" = ANY ($accountIds) AND "AccountFollows"."state" = \'accepted\' '
+ if (count !== undefined) query += 'LIMIT ' + count
+ if (start !== undefined) query += ' OFFSET ' + start
- if (count !== undefined) query += 'LIMIT ' + count
- if (start !== undefined) query += ' OFFSET ' + start
+ const options = {
+ bind: { accountIds },
+ type: Sequelize.QueryTypes.SELECT,
+ transaction: t
+ }
+ tasks.push(AccountFollowModel.sequelize.query(query, options))
+ }
- const options = {
- bind: { accountIds },
- type: Sequelize.QueryTypes.SELECT,
- transaction: t
+ const [ followers, [ { total } ] ] = await
+ Promise.all(tasks)
+ const urls: string[] = followers.map(f => f.url)
+
+ return {
+ data: urls,
+ total: parseInt(total, 10)
}
- tasks.push(AccountFollow['sequelize'].query(query, options))
}
- const [ followers, [ { total } ]] = await Promise.all(tasks)
- const urls: string[] = followers.map(f => f.url)
+ toFormattedJSON () {
+ const follower = this.AccountFollower.toFormattedJSON()
+ const following = this.AccountFollowing.toFormattedJSON()
- return {
- data: urls,
- total: parseInt(total, 10)
+ return {
+ id: this.id,
+ follower,
+ following,
+ state: this.state,
+ createdAt: this.createdAt,
+ updatedAt: this.updatedAt
+ }
}
}
+++ /dev/null
-import * as Bluebird from 'bluebird'
-import * as Sequelize from 'sequelize'
-import { Account as FormattedAccount, ActivityPubActor } from '../../../shared'
-import { AvatarInstance } from '../avatar'
-import { ServerInstance } from '../server/server-interface'
-import { VideoChannelInstance } from '../video/video-channel-interface'
-
-export namespace AccountMethods {
- export type LoadApplication = () => Bluebird<AccountInstance>
-
- export type Load = (id: number) => Bluebird<AccountInstance>
- export type LoadByUUID = (uuid: string) => Bluebird<AccountInstance>
- export type LoadByUrl = (url: string, transaction?: Sequelize.Transaction) => Bluebird<AccountInstance>
- export type LoadLocalByName = (name: string) => Bluebird<AccountInstance>
- export type LoadByNameAndHost = (name: string, host: string) => Bluebird<AccountInstance>
- export type ListByFollowersUrls = (followerUrls: string[], transaction: Sequelize.Transaction) => Bluebird<AccountInstance[]>
-
- export type ToActivityPubObject = (this: AccountInstance) => ActivityPubActor
- export type ToFormattedJSON = (this: AccountInstance) => FormattedAccount
- export type IsOwned = (this: AccountInstance) => boolean
- export type GetFollowerSharedInboxUrls = (this: AccountInstance, t: Sequelize.Transaction) => Bluebird<string[]>
- export type GetFollowingUrl = (this: AccountInstance) => string
- export type GetFollowersUrl = (this: AccountInstance) => string
- export type GetPublicKeyUrl = (this: AccountInstance) => string
-}
-
-export interface AccountClass {
- loadApplication: AccountMethods.LoadApplication
- load: AccountMethods.Load
- loadByUUID: AccountMethods.LoadByUUID
- loadByUrl: AccountMethods.LoadByUrl
- loadLocalByName: AccountMethods.LoadLocalByName
- loadByNameAndHost: AccountMethods.LoadByNameAndHost
- listByFollowersUrls: AccountMethods.ListByFollowersUrls
-}
-
-export interface AccountAttributes {
- name: string
- url?: string
- publicKey: string
- privateKey: string
- followersCount: number
- followingCount: number
- inboxUrl: string
- outboxUrl: string
- sharedInboxUrl: string
- followersUrl: string
- followingUrl: string
-
- uuid?: string
-
- serverId?: number
- userId?: number
- applicationId?: number
- avatarId?: number
-}
-
-export interface AccountInstance extends AccountClass, AccountAttributes, Sequelize.Instance<AccountAttributes> {
- isOwned: AccountMethods.IsOwned
- toActivityPubObject: AccountMethods.ToActivityPubObject
- toFormattedJSON: AccountMethods.ToFormattedJSON
- getFollowerSharedInboxUrls: AccountMethods.GetFollowerSharedInboxUrls
- getFollowingUrl: AccountMethods.GetFollowingUrl
- getFollowersUrl: AccountMethods.GetFollowersUrl
- getPublicKeyUrl: AccountMethods.GetPublicKeyUrl
-
- id: number
- createdAt: Date
- updatedAt: Date
-
- Server: ServerInstance
- VideoChannels: VideoChannelInstance[]
- Avatar: AvatarInstance
-}
-
-export interface AccountModel extends AccountClass, Sequelize.Model<AccountInstance, AccountAttributes> {}
+++ /dev/null
-import * as Sequelize from 'sequelize'
-import * as Promise from 'bluebird'
-
-import { VideoRateType } from '../../../shared/models/videos/video-rate.type'
-import { AccountInstance } from './account-interface'
-
-export namespace AccountVideoRateMethods {
- export type Load = (accountId: number, videoId: number, transaction: Sequelize.Transaction) => Promise<AccountVideoRateInstance>
-}
-
-export interface AccountVideoRateClass {
- load: AccountVideoRateMethods.Load
-}
-
-export interface AccountVideoRateAttributes {
- type: VideoRateType
- accountId: number
- videoId: number
-
- Account?: AccountInstance
-}
-
-export interface AccountVideoRateInstance
- extends AccountVideoRateClass, AccountVideoRateAttributes, Sequelize.Instance<AccountVideoRateAttributes> {
- id: number
- createdAt: Date
- updatedAt: Date
-}
-
-export interface AccountVideoRateModel
- extends AccountVideoRateClass, Sequelize.Model<AccountVideoRateInstance, AccountVideoRateAttributes> {}
-/*
- Account rates per video.
-*/
import { values } from 'lodash'
-import * as Sequelize from 'sequelize'
-
+import { Transaction } from 'sequelize'
+import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
+import { IFindOptions } from 'sequelize-typescript/lib/interfaces/IFindOptions'
+import { VideoRateType } from '../../../shared/models/videos'
import { VIDEO_RATE_TYPES } from '../../initializers'
+import { VideoModel } from '../video/video'
+import { AccountModel } from './account'
-import { addMethodsToModel } from '../utils'
-import {
- AccountVideoRateInstance,
- AccountVideoRateAttributes,
-
- AccountVideoRateMethods
-} from './account-video-rate-interface'
-
-let AccountVideoRate: Sequelize.Model<AccountVideoRateInstance, AccountVideoRateAttributes>
-let load: AccountVideoRateMethods.Load
-
-export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
- AccountVideoRate = sequelize.define<AccountVideoRateInstance, AccountVideoRateAttributes>('AccountVideoRate',
- {
- type: {
- type: DataTypes.ENUM(values(VIDEO_RATE_TYPES)),
- allowNull: false
- }
- },
+/*
+ Account rates per video.
+*/
+@Table({
+ tableName: 'accountVideoRate',
+ indexes: [
{
- indexes: [
- {
- fields: [ 'videoId', 'accountId' ],
- unique: true
- }
- ]
+ fields: [ 'videoId', 'accountId' ],
+ unique: true
}
- )
+ ]
+})
+export class AccountVideoRateModel extends Model<AccountVideoRateModel> {
- const classMethods = [
- associate,
+ @AllowNull(false)
+ @Column(DataType.ENUM(values(VIDEO_RATE_TYPES)))
+ type: VideoRateType
- load
- ]
- addMethodsToModel(AccountVideoRate, classMethods)
+ @CreatedAt
+ createdAt: Date
- return AccountVideoRate
-}
+ @UpdatedAt
+ updatedAt: Date
-// ------------------------------ STATICS ------------------------------
+ @ForeignKey(() => VideoModel)
+ @Column
+ videoId: number
-function associate (models) {
- AccountVideoRate.belongsTo(models.Video, {
+ @BelongsTo(() => VideoModel, {
foreignKey: {
- name: 'videoId',
allowNull: false
},
onDelete: 'CASCADE'
})
+ Video: VideoModel
- AccountVideoRate.belongsTo(models.Account, {
+ @ForeignKey(() => AccountModel)
+ @Column
+ accountId: number
+
+ @BelongsTo(() => AccountModel, {
foreignKey: {
- name: 'accountId',
allowNull: false
},
onDelete: 'CASCADE'
})
-}
+ Account: AccountModel
-load = function (accountId: number, videoId: number, transaction: Sequelize.Transaction) {
- const options: Sequelize.FindOptions<AccountVideoRateAttributes> = {
- where: {
- accountId,
- videoId
+ static load (accountId: number, videoId: number, transaction: Transaction) {
+ const options: IFindOptions<AccountVideoRateModel> = {
+ where: {
+ accountId,
+ videoId
+ }
}
- }
- if (transaction) options.transaction = transaction
+ if (transaction) options.transaction = transaction
- return AccountVideoRate.findOne(options)
+ return AccountVideoRateModel.findOne(options)
+ }
}
import { join } from 'path'
import * as Sequelize from 'sequelize'
+import {
+ AfterDestroy,
+ AllowNull,
+ BelongsTo,
+ Column,
+ CreatedAt,
+ DataType,
+ Default,
+ ForeignKey,
+ HasMany,
+ Is,
+ IsUUID,
+ Model,
+ Table,
+ UpdatedAt
+} from 'sequelize-typescript'
import { Avatar } from '../../../shared/models/avatars/avatar.model'
+import { activityPubContextify } from '../../helpers'
import {
- activityPubContextify,
isAccountFollowersCountValid,
isAccountFollowingCountValid,
isAccountPrivateKeyValid,
isAccountPublicKeyValid,
- isUserUsernameValid
-} from '../../helpers'
-import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
-import { AVATARS_DIR } from '../../initializers'
-import { CONFIG, CONSTRAINTS_FIELDS } from '../../initializers/constants'
-import { sendDeleteAccount } from '../../lib/activitypub/send/send-delete'
-import { addMethodsToModel } from '../utils'
-import { AccountAttributes, AccountInstance, AccountMethods } from './account-interface'
-
-let Account: Sequelize.Model<AccountInstance, AccountAttributes>
-let load: AccountMethods.Load
-let loadApplication: AccountMethods.LoadApplication
-let loadByUUID: AccountMethods.LoadByUUID
-let loadByUrl: AccountMethods.LoadByUrl
-let loadLocalByName: AccountMethods.LoadLocalByName
-let loadByNameAndHost: AccountMethods.LoadByNameAndHost
-let listByFollowersUrls: AccountMethods.ListByFollowersUrls
-let isOwned: AccountMethods.IsOwned
-let toActivityPubObject: AccountMethods.ToActivityPubObject
-let toFormattedJSON: AccountMethods.ToFormattedJSON
-let getFollowerSharedInboxUrls: AccountMethods.GetFollowerSharedInboxUrls
-let getFollowingUrl: AccountMethods.GetFollowingUrl
-let getFollowersUrl: AccountMethods.GetFollowersUrl
-let getPublicKeyUrl: AccountMethods.GetPublicKeyUrl
-
-export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
- Account = sequelize.define<AccountInstance, AccountAttributes>('Account',
+ isActivityPubUrlValid
+} from '../../helpers/custom-validators/activitypub'
+import { isUserUsernameValid } from '../../helpers/custom-validators/users'
+import { AVATARS_DIR, CONFIG, CONSTRAINTS_FIELDS } from '../../initializers'
+import { sendDeleteAccount } from '../../lib/activitypub/send'
+import { ApplicationModel } from '../application/application'
+import { AvatarModel } from '../avatar/avatar'
+import { ServerModel } from '../server/server'
+import { throwIfNotValid } from '../utils'
+import { VideoChannelModel } from '../video/video-channel'
+import { AccountFollowModel } from './account-follow'
+import { UserModel } from './user'
+
+@Table({
+ tableName: 'account',
+ indexes: [
{
- uuid: {
- type: DataTypes.UUID,
- defaultValue: DataTypes.UUIDV4,
- allowNull: false,
- validate: {
- isUUID: 4
- }
- },
- name: {
- type: DataTypes.STRING,
- allowNull: false,
- validate: {
- nameValid: value => {
- const res = isUserUsernameValid(value)
- if (res === false) throw new Error('Name is not valid.')
- }
- }
- },
- url: {
- type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max),
- allowNull: false,
- validate: {
- urlValid: value => {
- const res = isActivityPubUrlValid(value)
- if (res === false) throw new Error('URL is not valid.')
- }
- }
- },
- publicKey: {
- type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.PUBLIC_KEY.max),
- allowNull: true,
- validate: {
- publicKeyValid: value => {
- const res = isAccountPublicKeyValid(value)
- if (res === false) throw new Error('Public key is not valid.')
- }
- }
- },
- privateKey: {
- type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.PRIVATE_KEY.max),
- allowNull: true,
- validate: {
- privateKeyValid: value => {
- const res = isAccountPrivateKeyValid(value)
- if (res === false) throw new Error('Private key is not valid.')
- }
- }
- },
- followersCount: {
- type: DataTypes.INTEGER,
- allowNull: false,
- validate: {
- followersCountValid: value => {
- const res = isAccountFollowersCountValid(value)
- if (res === false) throw new Error('Followers count is not valid.')
- }
- }
- },
- followingCount: {
- type: DataTypes.INTEGER,
- allowNull: false,
- validate: {
- followingCountValid: value => {
- const res = isAccountFollowingCountValid(value)
- if (res === false) throw new Error('Following count is not valid.')
- }
- }
- },
- inboxUrl: {
- type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max),
- allowNull: false,
- validate: {
- inboxUrlValid: value => {
- const res = isActivityPubUrlValid(value)
- if (res === false) throw new Error('Inbox URL is not valid.')
- }
- }
- },
- outboxUrl: {
- type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max),
- allowNull: false,
- validate: {
- outboxUrlValid: value => {
- const res = isActivityPubUrlValid(value)
- if (res === false) throw new Error('Outbox URL is not valid.')
- }
- }
- },
- sharedInboxUrl: {
- type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max),
- allowNull: false,
- validate: {
- sharedInboxUrlValid: value => {
- const res = isActivityPubUrlValid(value)
- if (res === false) throw new Error('Shared inbox URL is not valid.')
- }
- }
- },
- followersUrl: {
- type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max),
- allowNull: false,
- validate: {
- followersUrlValid: value => {
- const res = isActivityPubUrlValid(value)
- if (res === false) throw new Error('Followers URL is not valid.')
- }
- }
- },
- followingUrl: {
- type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max),
- allowNull: false,
- validate: {
- followingUrlValid: value => {
- const res = isActivityPubUrlValid(value)
- if (res === false) throw new Error('Following URL is not valid.')
- }
- }
- }
+ fields: [ 'name' ]
},
{
- indexes: [
- {
- fields: [ 'name' ]
- },
- {
- fields: [ 'serverId' ]
- },
- {
- fields: [ 'userId' ],
- unique: true
- },
- {
- fields: [ 'applicationId' ],
- unique: true
- },
- {
- fields: [ 'name', 'serverId', 'applicationId' ],
- unique: true
- }
- ],
- hooks: { afterDestroy }
+ fields: [ 'serverId' ]
+ },
+ {
+ fields: [ 'userId' ],
+ unique: true
+ },
+ {
+ fields: [ 'applicationId' ],
+ unique: true
+ },
+ {
+ fields: [ 'name', 'serverId', 'applicationId' ],
+ unique: true
}
- )
-
- const classMethods = [
- associate,
- loadApplication,
- load,
- loadByUUID,
- loadByUrl,
- loadLocalByName,
- loadByNameAndHost,
- listByFollowersUrls
- ]
- const instanceMethods = [
- isOwned,
- toActivityPubObject,
- toFormattedJSON,
- getFollowerSharedInboxUrls,
- getFollowingUrl,
- getFollowersUrl,
- getPublicKeyUrl
]
- addMethodsToModel(Account, classMethods, instanceMethods)
-
- return Account
-}
+})
+export class AccountModel extends Model<Account> {
+
+ @AllowNull(false)
+ @Default(DataType.UUIDV4)
+ @IsUUID(4)
+ @Column(DataType.UUID)
+ uuid: string
+
+ @AllowNull(false)
+ @Is('AccountName', value => throwIfNotValid(value, isUserUsernameValid, 'account name'))
+ @Column
+ name: string
+
+ @AllowNull(false)
+ @Is('AccountUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url'))
+ @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max))
+ url: string
+
+ @AllowNull(true)
+ @Is('AccountPublicKey', value => throwIfNotValid(value, isAccountPublicKeyValid, 'public key'))
+ @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.PUBLIC_KEY.max))
+ publicKey: string
+
+ @AllowNull(true)
+ @Is('AccountPublicKey', value => throwIfNotValid(value, isAccountPrivateKeyValid, 'private key'))
+ @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.PRIVATE_KEY.max))
+ privateKey: string
+
+ @AllowNull(false)
+ @Is('AccountFollowersCount', value => throwIfNotValid(value, isAccountFollowersCountValid, 'followers count'))
+ @Column
+ followersCount: number
+
+ @AllowNull(false)
+ @Is('AccountFollowersCount', value => throwIfNotValid(value, isAccountFollowingCountValid, 'following count'))
+ @Column
+ followingCount: number
+
+ @AllowNull(false)
+ @Is('AccountInboxUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'inbox url'))
+ @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max))
+ inboxUrl: string
+
+ @AllowNull(false)
+ @Is('AccountOutboxUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'outbox url'))
+ @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max))
+ outboxUrl: string
+
+ @AllowNull(false)
+ @Is('AccountSharedInboxUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'shared inbox url'))
+ @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max))
+ sharedInboxUrl: string
+
+ @AllowNull(false)
+ @Is('AccountFollowersUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'followers url'))
+ @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max))
+ followersUrl: string
+
+ @AllowNull(false)
+ @Is('AccountFollowingUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'following url'))
+ @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max))
+ followingUrl: string
+
+ @CreatedAt
+ createdAt: Date
+
+ @UpdatedAt
+ updatedAt: Date
+
+ @ForeignKey(() => AvatarModel)
+ @Column
+ avatarId: number
+
+ @BelongsTo(() => AvatarModel, {
+ foreignKey: {
+ allowNull: true
+ },
+ onDelete: 'cascade'
+ })
+ Avatar: AvatarModel
-// ---------------------------------------------------------------------------
+ @ForeignKey(() => ServerModel)
+ @Column
+ serverId: number
-function associate (models) {
- Account.belongsTo(models.Server, {
+ @BelongsTo(() => ServerModel, {
foreignKey: {
- name: 'serverId',
allowNull: true
},
onDelete: 'cascade'
})
+ Server: ServerModel
- Account.belongsTo(models.User, {
+ @ForeignKey(() => UserModel)
+ @Column
+ userId: number
+
+ @BelongsTo(() => UserModel, {
foreignKey: {
- name: 'userId',
allowNull: true
},
onDelete: 'cascade'
})
+ User: UserModel
+
+ @ForeignKey(() => ApplicationModel)
+ @Column
+ applicationId: number
- Account.belongsTo(models.Application, {
+ @BelongsTo(() => ApplicationModel, {
foreignKey: {
- name: 'applicationId',
allowNull: true
},
onDelete: 'cascade'
})
+ Application: ApplicationModel
- Account.hasMany(models.VideoChannel, {
+ @HasMany(() => VideoChannelModel, {
foreignKey: {
- name: 'accountId',
allowNull: false
},
onDelete: 'cascade',
hooks: true
})
+ VideoChannels: VideoChannelModel[]
- Account.hasMany(models.AccountFollow, {
+ @HasMany(() => AccountFollowModel, {
foreignKey: {
name: 'accountId',
allowNull: false
},
onDelete: 'cascade'
})
+ AccountFollowing: AccountFollowModel[]
- Account.hasMany(models.AccountFollow, {
+ @HasMany(() => AccountFollowModel, {
foreignKey: {
name: 'targetAccountId',
allowNull: false
as: 'followers',
onDelete: 'cascade'
})
+ AccountFollowers: AccountFollowModel[]
- Account.hasOne(models.Avatar, {
- foreignKey: {
- name: 'avatarId',
- allowNull: true
- },
- onDelete: 'cascade'
- })
-}
+ @AfterDestroy
+ static sendDeleteIfOwned (instance: AccountModel) {
+ if (instance.isOwned()) {
+ return sendDeleteAccount(instance, undefined)
+ }
-function afterDestroy (account: AccountInstance) {
- if (account.isOwned()) {
- return sendDeleteAccount(account, undefined)
+ return undefined
}
- return undefined
-}
+ static loadApplication () {
+ return AccountModel.findOne({
+ include: [
+ {
+ model: ApplicationModel,
+ required: true
+ }
+ ]
+ })
+ }
-toFormattedJSON = function (this: AccountInstance) {
- let host = CONFIG.WEBSERVER.HOST
- let score: number
- let avatar: Avatar = null
+ static load (id: number) {
+ return AccountModel.findById(id)
+ }
- if (this.Avatar) {
- avatar = {
- path: join(AVATARS_DIR.ACCOUNT, this.Avatar.filename),
- createdAt: this.Avatar.createdAt,
- updatedAt: this.Avatar.updatedAt
+ static loadByUUID (uuid: string) {
+ const query = {
+ where: {
+ uuid
+ }
}
- }
- if (this.Server) {
- host = this.Server.host
- score = this.Server.score as number
+ return AccountModel.findOne(query)
}
- const json = {
- id: this.id,
- uuid: this.uuid,
- host,
- score,
- name: this.name,
- followingCount: this.followingCount,
- followersCount: this.followersCount,
- createdAt: this.createdAt,
- updatedAt: this.updatedAt,
- avatar
- }
+ static loadLocalByName (name: string) {
+ const query = {
+ where: {
+ name,
+ [ Sequelize.Op.or ]: [
+ {
+ userId: {
+ [ Sequelize.Op.ne ]: null
+ }
+ },
+ {
+ applicationId: {
+ [ Sequelize.Op.ne ]: null
+ }
+ }
+ ]
+ }
+ }
- return json
-}
+ return AccountModel.findOne(query)
+ }
-toActivityPubObject = function (this: AccountInstance) {
- const type = this.serverId ? 'Application' as 'Application' : 'Person' as 'Person'
-
- const json = {
- type,
- id: this.url,
- following: this.getFollowingUrl(),
- followers: this.getFollowersUrl(),
- inbox: this.inboxUrl,
- outbox: this.outboxUrl,
- preferredUsername: this.name,
- url: this.url,
- name: this.name,
- endpoints: {
- sharedInbox: this.sharedInboxUrl
- },
- uuid: this.uuid,
- publicKey: {
- id: this.getPublicKeyUrl(),
- owner: this.url,
- publicKeyPem: this.publicKey
+ static loadByNameAndHost (name: string, host: string) {
+ const query = {
+ where: {
+ name
+ },
+ include: [
+ {
+ model: ServerModel,
+ required: true,
+ where: {
+ host
+ }
+ }
+ ]
}
+
+ return AccountModel.findOne(query)
}
- return activityPubContextify(json)
-}
+ static loadByUrl (url: string, transaction?: Sequelize.Transaction) {
+ const query = {
+ where: {
+ url
+ },
+ transaction
+ }
-isOwned = function (this: AccountInstance) {
- return this.serverId === null
-}
+ return AccountModel.findOne(query)
+ }
-getFollowerSharedInboxUrls = function (this: AccountInstance, t: Sequelize.Transaction) {
- const query: Sequelize.FindOptions<AccountAttributes> = {
- attributes: [ 'sharedInboxUrl' ],
- include: [
- {
- model: Account['sequelize'].models.AccountFollow,
- required: true,
- as: 'followers',
- where: {
- targetAccountId: this.id
+ static listByFollowersUrls (followersUrls: string[], transaction?: Sequelize.Transaction) {
+ const query = {
+ where: {
+ followersUrl: {
+ [ Sequelize.Op.in ]: followersUrls
}
- }
- ],
- transaction: t
- }
+ },
+ transaction
+ }
- return Account.findAll(query)
- .then(accounts => accounts.map(a => a.sharedInboxUrl))
-}
+ return AccountModel.findAll(query)
+ }
-getFollowingUrl = function (this: AccountInstance) {
- return this.url + '/following'
-}
+ toFormattedJSON () {
+ let host = CONFIG.WEBSERVER.HOST
+ let score: number
+ let avatar: Avatar = null
-getFollowersUrl = function (this: AccountInstance) {
- return this.url + '/followers'
-}
+ if (this.Avatar) {
+ avatar = {
+ path: join(AVATARS_DIR.ACCOUNT, this.Avatar.filename),
+ createdAt: this.Avatar.createdAt,
+ updatedAt: this.Avatar.updatedAt
+ }
+ }
-getPublicKeyUrl = function (this: AccountInstance) {
- return this.url + '#main-key'
-}
+ if (this.Server) {
+ host = this.Server.host
+ score = this.Server.score
+ }
-// ------------------------------ STATICS ------------------------------
+ return {
+ id: this.id,
+ uuid: this.uuid,
+ host,
+ score,
+ name: this.name,
+ followingCount: this.followingCount,
+ followersCount: this.followersCount,
+ createdAt: this.createdAt,
+ updatedAt: this.updatedAt,
+ avatar
+ }
+ }
-loadApplication = function () {
- return Account.findOne({
- include: [
- {
- model: Account['sequelize'].models.Application,
- required: true
+ toActivityPubObject () {
+ const type = this.serverId ? 'Application' as 'Application' : 'Person' as 'Person'
+
+ const json = {
+ type,
+ id: this.url,
+ following: this.getFollowingUrl(),
+ followers: this.getFollowersUrl(),
+ inbox: this.inboxUrl,
+ outbox: this.outboxUrl,
+ preferredUsername: this.name,
+ url: this.url,
+ name: this.name,
+ endpoints: {
+ sharedInbox: this.sharedInboxUrl
+ },
+ uuid: this.uuid,
+ publicKey: {
+ id: this.getPublicKeyUrl(),
+ owner: this.url,
+ publicKeyPem: this.publicKey
}
- ]
- })
-}
-
-load = function (id: number) {
- return Account.findById(id)
-}
-
-loadByUUID = function (uuid: string) {
- const query: Sequelize.FindOptions<AccountAttributes> = {
- where: {
- uuid
}
+
+ return activityPubContextify(json)
}
- return Account.findOne(query)
-}
+ isOwned () {
+ return this.serverId === null
+ }
-loadLocalByName = function (name: string) {
- const query: Sequelize.FindOptions<AccountAttributes> = {
- where: {
- name,
- [Sequelize.Op.or]: [
- {
- userId: {
- [Sequelize.Op.ne]: null
- }
- },
+ getFollowerSharedInboxUrls (t: Sequelize.Transaction) {
+ const query = {
+ attributes: [ 'sharedInboxUrl' ],
+ include: [
{
- applicationId: {
- [Sequelize.Op.ne]: null
+ model: AccountFollowModel,
+ required: true,
+ as: 'followers',
+ where: {
+ targetAccountId: this.id
}
}
- ]
+ ],
+ transaction: t
}
- }
- return Account.findOne(query)
-}
-
-loadByNameAndHost = function (name: string, host: string) {
- const query: Sequelize.FindOptions<AccountAttributes> = {
- where: {
- name
- },
- include: [
- {
- model: Account['sequelize'].models.Server,
- required: true,
- where: {
- host
- }
- }
- ]
+ return AccountModel.findAll(query)
+ .then(accounts => accounts.map(a => a.sharedInboxUrl))
}
- return Account.findOne(query)
-}
-
-loadByUrl = function (url: string, transaction?: Sequelize.Transaction) {
- const query: Sequelize.FindOptions<AccountAttributes> = {
- where: {
- url
- },
- transaction
+ getFollowingUrl () {
+ return this.url + '/following'
}
- return Account.findOne(query)
-}
-
-listByFollowersUrls = function (followersUrls: string[], transaction?: Sequelize.Transaction) {
- const query: Sequelize.FindOptions<AccountAttributes> = {
- where: {
- followersUrl: {
- [Sequelize.Op.in]: followersUrls
- }
- },
- transaction
+ getFollowersUrl () {
+ return this.url + '/followers'
}
- return Account.findAll(query)
+ getPublicKeyUrl () {
+ return this.url + '#main-key'
+ }
}
+++ /dev/null
-export * from './account-interface'
-export * from './account-follow-interface'
-export * from './account-video-rate-interface'
-export * from './user-interface'
+++ /dev/null
-import * as Bluebird from 'bluebird'
-import * as Sequelize from 'sequelize'
-import { ResultList } from '../../../shared/models/result-list.model'
-import { UserRight } from '../../../shared/models/users/user-right.enum'
-import { UserRole } from '../../../shared/models/users/user-role'
-import { User as FormattedUser } from '../../../shared/models/users/user.model'
-import { AccountInstance } from './account-interface'
-
-export namespace UserMethods {
- export type HasRight = (this: UserInstance, right: UserRight) => boolean
- export type IsPasswordMatch = (this: UserInstance, password: string) => Promise<boolean>
-
- export type ToFormattedJSON = (this: UserInstance) => FormattedUser
- export type IsAbleToUploadVideo = (this: UserInstance, videoFile: Express.Multer.File) => Promise<boolean>
-
- export type CountTotal = () => Bluebird<number>
-
- export type GetByUsername = (username: string) => Bluebird<UserInstance>
-
- export type ListForApi = (start: number, count: number, sort: string) => Bluebird< ResultList<UserInstance> >
-
- export type LoadById = (id: number) => Bluebird<UserInstance>
-
- export type LoadByUsername = (username: string) => Bluebird<UserInstance>
- export type LoadByUsernameAndPopulateChannels = (username: string) => Bluebird<UserInstance>
-
- export type LoadByUsernameOrEmail = (username: string, email: string) => Bluebird<UserInstance>
-}
-
-export interface UserClass {
- isPasswordMatch: UserMethods.IsPasswordMatch,
- toFormattedJSON: UserMethods.ToFormattedJSON,
- hasRight: UserMethods.HasRight,
- isAbleToUploadVideo: UserMethods.IsAbleToUploadVideo,
-
- countTotal: UserMethods.CountTotal,
- getByUsername: UserMethods.GetByUsername,
- listForApi: UserMethods.ListForApi,
- loadById: UserMethods.LoadById,
- loadByUsername: UserMethods.LoadByUsername,
- loadByUsernameAndPopulateChannels: UserMethods.LoadByUsernameAndPopulateChannels,
- loadByUsernameOrEmail: UserMethods.LoadByUsernameOrEmail
-}
-
-export interface UserAttributes {
- id?: number
- password: string
- username: string
- email: string
- displayNSFW?: boolean
- role: UserRole
- videoQuota: number
-
- Account?: AccountInstance
-}
-
-export interface UserInstance extends UserClass, UserAttributes, Sequelize.Instance<UserAttributes> {
- id: number
- createdAt: Date
- updatedAt: Date
-
- isPasswordMatch: UserMethods.IsPasswordMatch
- toFormattedJSON: UserMethods.ToFormattedJSON
- hasRight: UserMethods.HasRight
-}
-
-export interface UserModel extends UserClass, Sequelize.Model<UserInstance, UserAttributes> {}
import * as Sequelize from 'sequelize'
+import {
+ AllowNull,
+ BeforeCreate,
+ BeforeUpdate,
+ Column, CreatedAt,
+ DataType,
+ Default,
+ HasMany,
+ HasOne,
+ Is,
+ IsEmail,
+ Model,
+ Table, UpdatedAt
+} from 'sequelize-typescript'
import { hasUserRight, USER_ROLE_LABELS, UserRight } from '../../../shared'
import {
comparePassword,
- cryptPassword,
- isUserDisplayNSFWValid,
- isUserPasswordValid,
- isUserRoleValid,
- isUserUsernameValid,
- isUserVideoQuotaValid
+ cryptPassword
} from '../../helpers'
-import { addMethodsToModel, getSort } from '../utils'
-import { UserAttributes, UserInstance, UserMethods } from './user-interface'
-
-let User: Sequelize.Model<UserInstance, UserAttributes>
-let isPasswordMatch: UserMethods.IsPasswordMatch
-let hasRight: UserMethods.HasRight
-let toFormattedJSON: UserMethods.ToFormattedJSON
-let countTotal: UserMethods.CountTotal
-let getByUsername: UserMethods.GetByUsername
-let listForApi: UserMethods.ListForApi
-let loadById: UserMethods.LoadById
-let loadByUsername: UserMethods.LoadByUsername
-let loadByUsernameAndPopulateChannels: UserMethods.LoadByUsernameAndPopulateChannels
-let loadByUsernameOrEmail: UserMethods.LoadByUsernameOrEmail
-let isAbleToUploadVideo: UserMethods.IsAbleToUploadVideo
-
-export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
- User = sequelize.define<UserInstance, UserAttributes>('User',
+import {
+ isUserDisplayNSFWValid, isUserPasswordValid, isUserRoleValid, isUserUsernameValid,
+ isUserVideoQuotaValid
+} from '../../helpers/custom-validators/users'
+import { OAuthTokenModel } from '../oauth/oauth-token'
+import { getSort, throwIfNotValid } from '../utils'
+import { VideoChannelModel } from '../video/video-channel'
+import { AccountModel } from './account'
+
+@Table({
+ tableName: 'user',
+ indexes: [
{
- password: {
- type: DataTypes.STRING,
- allowNull: false,
- validate: {
- passwordValid: value => {
- const res = isUserPasswordValid(value)
- if (res === false) throw new Error('Password not valid.')
- }
- }
- },
- username: {
- type: DataTypes.STRING,
- allowNull: false,
- validate: {
- usernameValid: value => {
- const res = isUserUsernameValid(value)
- if (res === false) throw new Error('Username not valid.')
- }
- }
- },
- email: {
- type: DataTypes.STRING(400),
- allowNull: false,
- validate: {
- isEmail: true
- }
- },
- displayNSFW: {
- type: DataTypes.BOOLEAN,
- allowNull: false,
- defaultValue: false,
- validate: {
- nsfwValid: value => {
- const res = isUserDisplayNSFWValid(value)
- if (res === false) throw new Error('Display NSFW is not valid.')
- }
- }
- },
- role: {
- type: DataTypes.INTEGER,
- allowNull: false,
- validate: {
- roleValid: value => {
- const res = isUserRoleValid(value)
- if (res === false) throw new Error('Role is not valid.')
- }
- }
- },
- videoQuota: {
- type: DataTypes.BIGINT,
- allowNull: false,
- validate: {
- videoQuotaValid: value => {
- const res = isUserVideoQuotaValid(value)
- if (res === false) throw new Error('Video quota is not valid.')
- }
- }
- }
+ fields: [ 'username' ],
+ unique: true
},
{
- indexes: [
- {
- fields: [ 'username' ],
- unique: true
- },
- {
- fields: [ 'email' ],
- unique: true
- }
- ],
- hooks: {
- beforeCreate: beforeCreateOrUpdate,
- beforeUpdate: beforeCreateOrUpdate
- }
+ fields: [ 'email' ],
+ unique: true
}
- )
-
- const classMethods = [
- associate,
-
- countTotal,
- getByUsername,
- listForApi,
- loadById,
- loadByUsername,
- loadByUsernameAndPopulateChannels,
- loadByUsernameOrEmail
]
- const instanceMethods = [
- hasRight,
- isPasswordMatch,
- toFormattedJSON,
- isAbleToUploadVideo
- ]
- addMethodsToModel(User, classMethods, instanceMethods)
-
- return User
-}
+})
+export class UserModel extends Model<UserModel> {
+
+ @AllowNull(false)
+ @Is('UserPassword', value => throwIfNotValid(value, isUserPasswordValid, 'user password'))
+ @Column
+ password: string
+
+ @AllowNull(false)
+ @Is('UserPassword', value => throwIfNotValid(value, isUserUsernameValid, 'user name'))
+ @Column
+ username: string
+
+ @AllowNull(false)
+ @IsEmail
+ @Column(DataType.STRING(400))
+ email: string
+
+ @AllowNull(false)
+ @Default(false)
+ @Is('UserDisplayNSFW', value => throwIfNotValid(value, isUserDisplayNSFWValid, 'display NSFW boolean'))
+ @Column
+ displayNSFW: boolean
+
+ @AllowNull(false)
+ @Is('UserRole', value => throwIfNotValid(value, isUserRoleValid, 'role'))
+ @Column
+ role: number
+
+ @AllowNull(false)
+ @Is('UserVideoQuota', value => throwIfNotValid(value, isUserVideoQuotaValid, 'video quota'))
+ @Column(DataType.BIGINT)
+ videoQuota: number
+
+ @CreatedAt
+ createdAt: Date
+
+ @UpdatedAt
+ updatedAt: Date
+
+ @HasOne(() => AccountModel, {
+ foreignKey: 'userId',
+ onDelete: 'cascade'
+ })
+ Account: AccountModel
-function beforeCreateOrUpdate (user: UserInstance) {
- if (user.changed('password')) {
- return cryptPassword(user.password)
- .then(hash => {
- user.password = hash
- return undefined
- })
+ @HasMany(() => OAuthTokenModel, {
+ foreignKey: 'userId',
+ onDelete: 'cascade'
+ })
+ OAuthTokens: OAuthTokenModel[]
+
+ @BeforeCreate
+ @BeforeUpdate
+ static cryptPasswordIfNeeded (instance: UserModel) {
+ if (instance.changed('password')) {
+ return cryptPassword(instance.password)
+ .then(hash => {
+ instance.password = hash
+ return undefined
+ })
+ }
}
-}
-
-// ------------------------------ METHODS ------------------------------
-hasRight = function (this: UserInstance, right: UserRight) {
- return hasUserRight(this.role, right)
-}
+ static countTotal () {
+ return this.count()
+ }
-isPasswordMatch = function (this: UserInstance, password: string) {
- return comparePassword(password, this.password)
-}
+ static getByUsername (username: string) {
+ const query = {
+ where: {
+ username: username
+ },
+ include: [ { model: AccountModel, required: true } ]
+ }
-toFormattedJSON = function (this: UserInstance) {
- const json = {
- id: this.id,
- username: this.username,
- email: this.email,
- displayNSFW: this.displayNSFW,
- role: this.role,
- roleLabel: USER_ROLE_LABELS[this.role],
- videoQuota: this.videoQuota,
- createdAt: this.createdAt,
- account: this.Account.toFormattedJSON()
+ return UserModel.findOne(query)
}
- if (Array.isArray(this.Account.VideoChannels) === true) {
- const videoChannels = this.Account.VideoChannels
- .map(c => c.toFormattedJSON())
- .sort((v1, v2) => {
- if (v1.createdAt < v2.createdAt) return -1
- if (v1.createdAt === v2.createdAt) return 0
+ static listForApi (start: number, count: number, sort: string) {
+ const query = {
+ offset: start,
+ limit: count,
+ order: [ getSort(sort) ],
+ include: [ { model: AccountModel, required: true } ]
+ }
- return 1
+ return UserModel.findAndCountAll(query)
+ .then(({ rows, count }) => {
+ return {
+ data: rows,
+ total: count
+ }
})
-
- json['videoChannels'] = videoChannels
}
- return json
-}
-
-isAbleToUploadVideo = function (this: UserInstance, videoFile: Express.Multer.File) {
- if (this.videoQuota === -1) return Promise.resolve(true)
-
- return getOriginalVideoFileTotalFromUser(this).then(totalBytes => {
- return (videoFile.size + totalBytes) < this.videoQuota
- })
-}
-
-// ------------------------------ STATICS ------------------------------
-
-function associate (models) {
- User.hasOne(models.Account, {
- foreignKey: 'userId',
- onDelete: 'cascade'
- })
+ static loadById (id: number) {
+ const options = {
+ include: [ { model: AccountModel, required: true } ]
+ }
- User.hasMany(models.OAuthToken, {
- foreignKey: 'userId',
- onDelete: 'cascade'
- })
-}
+ return UserModel.findById(id, options)
+ }
-countTotal = function () {
- return this.count()
-}
+ static loadByUsername (username: string) {
+ const query = {
+ where: {
+ username
+ },
+ include: [ { model: AccountModel, required: true } ]
+ }
-getByUsername = function (username: string) {
- const query = {
- where: {
- username: username
- },
- include: [ { model: User['sequelize'].models.Account, required: true } ]
+ return UserModel.findOne(query)
}
- return User.findOne(query)
-}
+ static loadByUsernameAndPopulateChannels (username: string) {
+ const query = {
+ where: {
+ username
+ },
+ include: [
+ {
+ model: AccountModel,
+ required: true,
+ include: [ VideoChannelModel ]
+ }
+ ]
+ }
-listForApi = function (start: number, count: number, sort: string) {
- const query = {
- offset: start,
- limit: count,
- order: [ getSort(sort) ],
- include: [ { model: User['sequelize'].models.Account, required: true } ]
+ return UserModel.findOne(query)
}
- return User.findAndCountAll(query).then(({ rows, count }) => {
- return {
- data: rows,
- total: count
+ static loadByUsernameOrEmail (username: string, email: string) {
+ const query = {
+ include: [ { model: AccountModel, required: true } ],
+ where: {
+ [ Sequelize.Op.or ]: [ { username }, { email } ]
+ }
}
- })
-}
-loadById = function (id: number) {
- const options = {
- include: [ { model: User['sequelize'].models.Account, required: true } ]
+ // FIXME: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18387
+ return (UserModel as any).findOne(query)
}
- return User.findById(id, options)
-}
+ private static getOriginalVideoFileTotalFromUser (user: UserModel) {
+ // Don't use sequelize because we need to use a sub query
+ const query = 'SELECT SUM("size") AS "total" FROM ' +
+ '(SELECT MAX("videoFile"."size") AS "size" FROM "videoFile" ' +
+ 'INNER JOIN "video" ON "videoFile"."videoId" = "video"."id" ' +
+ 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' +
+ 'INNER JOIN "account" ON "videoChannel"."accountId" = "account"."id" ' +
+ 'INNER JOIN "user" ON "account"."userId" = "user"."id" ' +
+ 'WHERE "user"."id" = $userId GROUP BY "video"."id") t'
+
+ const options = {
+ bind: { userId: user.id },
+ type: Sequelize.QueryTypes.SELECT
+ }
+ return UserModel.sequelize.query(query, options)
+ .then(([ { total } ]) => {
+ if (total === null) return 0
-loadByUsername = function (username: string) {
- const query = {
- where: {
- username
- },
- include: [ { model: User['sequelize'].models.Account, required: true } ]
+ return parseInt(total, 10)
+ })
}
- return User.findOne(query)
-}
+ hasRight (right: UserRight) {
+ return hasUserRight(this.role, right)
+ }
-loadByUsernameAndPopulateChannels = function (username: string) {
- const query = {
- where: {
- username
- },
- include: [
- {
- model: User['sequelize'].models.Account,
- required: true,
- include: [ User['sequelize'].models.VideoChannel ]
- }
- ]
+ isPasswordMatch (password: string) {
+ return comparePassword(password, this.password)
}
- return User.findOne(query)
-}
+ toFormattedJSON () {
+ const json = {
+ id: this.id,
+ username: this.username,
+ email: this.email,
+ displayNSFW: this.displayNSFW,
+ role: this.role,
+ roleLabel: USER_ROLE_LABELS[ this.role ],
+ videoQuota: this.videoQuota,
+ createdAt: this.createdAt,
+ account: this.Account.toFormattedJSON()
+ }
+
+ if (Array.isArray(this.Account.VideoChannels) === true) {
+ json['videoChannels'] = this.Account.VideoChannels
+ .map(c => c.toFormattedJSON())
+ .sort((v1, v2) => {
+ if (v1.createdAt < v2.createdAt) return -1
+ if (v1.createdAt === v2.createdAt) return 0
-loadByUsernameOrEmail = function (username: string, email: string) {
- const query = {
- include: [ { model: User['sequelize'].models.Account, required: true } ],
- where: {
- [Sequelize.Op.or]: [ { username }, { email } ]
+ return 1
+ })
}
+
+ return json
}
- // FIXME: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18387
- return (User as any).findOne(query)
-}
+ isAbleToUploadVideo (videoFile: Express.Multer.File) {
+ if (this.videoQuota === -1) return Promise.resolve(true)
-// ---------------------------------------------------------------------------
-
-function getOriginalVideoFileTotalFromUser (user: UserInstance) {
- // Don't use sequelize because we need to use a sub query
- const query = 'SELECT SUM("size") AS "total" FROM ' +
- '(SELECT MAX("VideoFiles"."size") AS "size" FROM "VideoFiles" ' +
- 'INNER JOIN "Videos" ON "VideoFiles"."videoId" = "Videos"."id" ' +
- 'INNER JOIN "VideoChannels" ON "VideoChannels"."id" = "Videos"."channelId" ' +
- 'INNER JOIN "Accounts" ON "VideoChannels"."accountId" = "Accounts"."id" ' +
- 'INNER JOIN "Users" ON "Accounts"."userId" = "Users"."id" ' +
- 'WHERE "Users"."id" = $userId GROUP BY "Videos"."id") t'
-
- const options = {
- bind: { userId: user.id },
- type: Sequelize.QueryTypes.SELECT
+ return UserModel.getOriginalVideoFileTotalFromUser(this)
+ .then(totalBytes => {
+ return (videoFile.size + totalBytes) < this.videoQuota
+ })
}
- return User['sequelize'].query(query, options).then(([ { total } ]) => {
- if (total === null) return 0
-
- return parseInt(total, 10)
- })
}
+++ /dev/null
-import * as Sequelize from 'sequelize'
-import * as Bluebird from 'bluebird'
-
-export namespace ApplicationMethods {
- export type LoadMigrationVersion = () => Bluebird<number>
-
- export type UpdateMigrationVersion = (
- newVersion: number,
- transaction: Sequelize.Transaction
- ) => Bluebird<[ number, ApplicationInstance[] ]>
-
- export type CountTotal = () => Bluebird<number>
-}
-
-export interface ApplicationClass {
- loadMigrationVersion: ApplicationMethods.LoadMigrationVersion
- updateMigrationVersion: ApplicationMethods.UpdateMigrationVersion
- countTotal: ApplicationMethods.CountTotal
-}
-
-export interface ApplicationAttributes {
- migrationVersion: number
-}
-
-export interface ApplicationInstance extends ApplicationClass, ApplicationAttributes, Sequelize.Instance<ApplicationAttributes> {
- id: number
- createdAt: Date
- updatedAt: Date
-}
-
-export interface ApplicationModel extends ApplicationClass, Sequelize.Model<ApplicationInstance, ApplicationAttributes> {}
-import * as Sequelize from 'sequelize'
-
-import { addMethodsToModel } from '../utils'
-import {
- ApplicationAttributes,
- ApplicationInstance,
-
- ApplicationMethods
-} from './application-interface'
-
-let Application: Sequelize.Model<ApplicationInstance, ApplicationAttributes>
-let loadMigrationVersion: ApplicationMethods.LoadMigrationVersion
-let updateMigrationVersion: ApplicationMethods.UpdateMigrationVersion
-let countTotal: ApplicationMethods.CountTotal
+import { Transaction } from 'sequelize'
+import { AllowNull, Column, Default, IsInt, Model, Table } from 'sequelize-typescript'
+
+@Table({
+ tableName: 'application'
+})
+export class ApplicationModel extends Model<ApplicationModel> {
+
+ @AllowNull(false)
+ @Default(0)
+ @IsInt
+ @Column
+ migrationVersion: number
+
+ static countTotal () {
+ return ApplicationModel.count()
+ }
-export default function defineApplication (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
- Application = sequelize.define<ApplicationInstance, ApplicationAttributes>('Application',
- {
- migrationVersion: {
- type: DataTypes.INTEGER,
- defaultValue: 0,
- allowNull: false,
- validate: {
- isInt: true
- }
- }
+ static loadMigrationVersion () {
+ const query = {
+ attributes: [ 'migrationVersion' ]
}
- )
-
- const classMethods = [
- countTotal,
- loadMigrationVersion,
- updateMigrationVersion
- ]
- addMethodsToModel(Application, classMethods)
-
- return Application
-}
-// ---------------------------------------------------------------------------
-
-countTotal = function () {
- return this.count()
-}
-
-loadMigrationVersion = function () {
- const query = {
- attributes: [ 'migrationVersion' ]
+ return ApplicationModel.findOne(query).then(data => data ? data.migrationVersion : null)
}
- return Application.findOne(query).then(data => data ? data.migrationVersion : null)
-}
+ static updateMigrationVersion (newVersion: number, transaction: Transaction) {
+ const options = {
+ where: {},
+ transaction: transaction
+ }
-updateMigrationVersion = function (newVersion: number, transaction: Sequelize.Transaction) {
- const options: Sequelize.UpdateOptions = {
- where: {},
- transaction: transaction
+ return ApplicationModel.update({ migrationVersion: newVersion }, options)
}
-
- return Application.update({ migrationVersion: newVersion }, options)
}
+++ /dev/null
-export * from './application-interface'
+++ /dev/null
-import * as Sequelize from 'sequelize'
-
-export namespace AvatarMethods {}
-
-export interface AvatarClass {}
-
-export interface AvatarAttributes {
- filename: string
-}
-
-export interface AvatarInstance extends AvatarClass, AvatarAttributes, Sequelize.Instance<AvatarAttributes> {
- createdAt: Date
- updatedAt: Date
-}
-
-export interface AvatarModel extends AvatarClass, Sequelize.Model<AvatarInstance, AvatarAttributes> {}
-import * as Sequelize from 'sequelize'
-import { addMethodsToModel } from '../utils'
-import { AvatarAttributes, AvatarInstance } from './avatar-interface'
+import { AllowNull, Column, CreatedAt, Model, Table, UpdatedAt } from 'sequelize-typescript'
-let Avatar: Sequelize.Model<AvatarInstance, AvatarAttributes>
+@Table({
+ tableName: 'avatar'
+})
+export class AvatarModel extends Model<AvatarModel> {
-export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
- Avatar = sequelize.define<AvatarInstance, AvatarAttributes>('Avatar',
- {
- filename: {
- type: DataTypes.STRING,
- allowNull: false
- }
- },
- {}
- )
+ @AllowNull(false)
+ @Column
+ filename: string
- const classMethods = []
- addMethodsToModel(Avatar, classMethods)
+ @CreatedAt
+ createdAt: Date
- return Avatar
+ @UpdatedAt
+ updatedAt: Date
}
-
-// ------------------------------ Statics ------------------------------
+++ /dev/null
-export * from './avatar-interface'
+++ /dev/null
-export * from './application'
-export * from './avatar'
-export * from './job'
-export * from './oauth'
-export * from './server'
-export * from './account'
-export * from './video'
+++ /dev/null
-export * from './job-interface'
+++ /dev/null
-import * as Bluebird from 'bluebird'
-import * as Sequelize from 'sequelize'
-import { Job as FormattedJob, JobCategory, JobState } from '../../../shared/models/job.model'
-import { ResultList } from '../../../shared/models/result-list.model'
-
-export namespace JobMethods {
- export type ListWithLimitByCategory = (limit: number, state: JobState, category: JobCategory) => Bluebird<JobInstance[]>
- export type ListForApi = (start: number, count: number, sort: string) => Bluebird< ResultList<JobInstance> >
-
- export type ToFormattedJSON = (this: JobInstance) => FormattedJob
-}
-
-export interface JobClass {
- listWithLimitByCategory: JobMethods.ListWithLimitByCategory
- listForApi: JobMethods.ListForApi,
-}
-
-export interface JobAttributes {
- state: JobState
- category: JobCategory
- handlerName: string
- handlerInputData: any
-}
-
-export interface JobInstance extends JobClass, JobAttributes, Sequelize.Instance<JobAttributes> {
- id: number
- createdAt: Date
- updatedAt: Date
-
- toFormattedJSON: JobMethods.ToFormattedJSON
-}
-
-export interface JobModel extends JobClass, Sequelize.Model<JobInstance, JobAttributes> {}
import { values } from 'lodash'
-import * as Sequelize from 'sequelize'
-import { JobCategory, JobState } from '../../../shared/models/job.model'
+import { AllowNull, Column, CreatedAt, DataType, Model, Table, UpdatedAt } from 'sequelize-typescript'
+import { JobCategory, JobState } from '../../../shared/models'
import { JOB_CATEGORIES, JOB_STATES } from '../../initializers'
-import { addMethodsToModel, getSort } from '../utils'
-import { JobAttributes, JobInstance, JobMethods } from './job-interface'
+import { getSort } from '../utils'
-let Job: Sequelize.Model<JobInstance, JobAttributes>
-let listWithLimitByCategory: JobMethods.ListWithLimitByCategory
-let listForApi: JobMethods.ListForApi
-let toFormattedJSON: JobMethods.ToFormattedJSON
-
-export default function defineJob (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
- Job = sequelize.define<JobInstance, JobAttributes>('Job',
- {
- state: {
- type: DataTypes.ENUM(values(JOB_STATES)),
- allowNull: false
- },
- category: {
- type: DataTypes.ENUM(values(JOB_CATEGORIES)),
- allowNull: false
- },
- handlerName: {
- type: DataTypes.STRING,
- allowNull: false
- },
- handlerInputData: {
- type: DataTypes.JSON,
- allowNull: true
- }
- },
+@Table({
+ tableName: 'job',
+ indexes: [
{
- indexes: [
- {
- fields: [ 'state', 'category' ]
- }
- ]
+ fields: [ 'state', 'category' ]
}
- )
-
- const classMethods = [
- listWithLimitByCategory,
- listForApi
]
- const instanceMethods = [
- toFormattedJSON
- ]
- addMethodsToModel(Job, classMethods, instanceMethods)
+})
+export class JobModel extends Model<JobModel> {
+ @AllowNull(false)
+ @Column(DataType.ENUM(values(JOB_STATES)))
+ state: JobState
- return Job
-}
+ @AllowNull(false)
+ @Column(DataType.ENUM(values(JOB_CATEGORIES)))
+ category: JobCategory
-toFormattedJSON = function (this: JobInstance) {
- return {
- id: this.id,
- state: this.state,
- category: this.category,
- handlerName: this.handlerName,
- handlerInputData: this.handlerInputData,
- createdAt: this.createdAt,
- updatedAt: this.updatedAt
- }
-}
+ @AllowNull(false)
+ @Column
+ handlerName: string
-// ---------------------------------------------------------------------------
+ @AllowNull(true)
+ @Column(DataType.JSON)
+ handlerInputData: any
-listWithLimitByCategory = function (limit: number, state: JobState, jobCategory: JobCategory) {
- const query = {
- order: [
- [ 'id', 'ASC' ]
- ],
- limit: limit,
- where: {
- state,
- category: jobCategory
+ @CreatedAt
+ creationDate: Date
+
+ @UpdatedAt
+ updatedOn: Date
+
+ static listWithLimitByCategory (limit: number, state: JobState, jobCategory: JobCategory) {
+ const query = {
+ order: [
+ [ 'id', 'ASC' ]
+ ],
+ limit: limit,
+ where: {
+ state,
+ category: jobCategory
+ }
}
+
+ return JobModel.findAll(query)
}
- return Job.findAll(query)
-}
+ static listForApi (start: number, count: number, sort: string) {
+ const query = {
+ offset: start,
+ limit: count,
+ order: [ getSort(sort) ]
+ }
-listForApi = function (start: number, count: number, sort: string) {
- const query = {
- offset: start,
- limit: count,
- order: [ getSort(sort) ]
+ return JobModel.findAndCountAll(query).then(({ rows, count }) => {
+ return {
+ data: rows,
+ total: count
+ }
+ })
}
- return Job.findAndCountAll(query).then(({ rows, count }) => {
+ toFormattedJSON () {
return {
- data: rows,
- total: count
+ id: this.id,
+ state: this.state,
+ category: this.category,
+ handlerName: this.handlerName,
+ handlerInputData: this.handlerInputData,
+ createdAt: this.createdAt,
+ updatedAt: this.updatedAt
}
- })
+ }
}
+++ /dev/null
-export * from './oauth-client-interface'
-export * from './oauth-token-interface'
+++ /dev/null
-import * as Sequelize from 'sequelize'
-import * as Promise from 'bluebird'
-
-export namespace OAuthClientMethods {
- export type CountTotal = () => Promise<number>
-
- export type LoadFirstClient = () => Promise<OAuthClientInstance>
-
- export type GetByIdAndSecret = (clientId: string, clientSecret: string) => Promise<OAuthClientInstance>
-}
-
-export interface OAuthClientClass {
- countTotal: OAuthClientMethods.CountTotal
- loadFirstClient: OAuthClientMethods.LoadFirstClient
- getByIdAndSecret: OAuthClientMethods.GetByIdAndSecret
-}
-
-export interface OAuthClientAttributes {
- clientId: string
- clientSecret: string
- grants: string[]
- redirectUris: string[]
-}
-
-export interface OAuthClientInstance extends OAuthClientClass, OAuthClientAttributes, Sequelize.Instance<OAuthClientAttributes> {
- id: number
- createdAt: Date
- updatedAt: Date
-}
-
-export interface OAuthClientModel extends OAuthClientClass, Sequelize.Model<OAuthClientInstance, OAuthClientAttributes> {}
-import * as Sequelize from 'sequelize'
+import { AllowNull, Column, CreatedAt, DataType, HasMany, Model, Table, UpdatedAt } from 'sequelize-typescript'
+import { OAuthTokenModel } from './oauth-token'
-import { addMethodsToModel } from '../utils'
-import {
- OAuthClientInstance,
- OAuthClientAttributes,
-
- OAuthClientMethods
-} from './oauth-client-interface'
-
-let OAuthClient: Sequelize.Model<OAuthClientInstance, OAuthClientAttributes>
-let countTotal: OAuthClientMethods.CountTotal
-let loadFirstClient: OAuthClientMethods.LoadFirstClient
-let getByIdAndSecret: OAuthClientMethods.GetByIdAndSecret
-
-export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
- OAuthClient = sequelize.define<OAuthClientInstance, OAuthClientAttributes>('OAuthClient',
+@Table({
+ tableName: 'oAuthClient',
+ indexes: [
{
- clientId: {
- type: DataTypes.STRING,
- allowNull: false
- },
- clientSecret: {
- type: DataTypes.STRING,
- allowNull: false
- },
- grants: {
- type: DataTypes.ARRAY(DataTypes.STRING)
- },
- redirectUris: {
- type: DataTypes.ARRAY(DataTypes.STRING)
- }
+ fields: [ 'clientId' ],
+ unique: true
},
{
- indexes: [
- {
- fields: [ 'clientId' ],
- unique: true
- },
- {
- fields: [ 'clientId', 'clientSecret' ],
- unique: true
- }
- ]
+ fields: [ 'clientId', 'clientSecret' ],
+ unique: true
}
- )
+ ]
+})
+export class OAuthClientModel extends Model<OAuthClientModel> {
- const classMethods = [
- associate,
+ @AllowNull(false)
+ @Column
+ clientId: string
- countTotal,
- getByIdAndSecret,
- loadFirstClient
- ]
- addMethodsToModel(OAuthClient, classMethods)
+ @AllowNull(false)
+ @Column
+ clientSecret: string
- return OAuthClient
-}
+ @Column(DataType.ARRAY(DataType.STRING))
+ grants: string[]
+
+ @Column(DataType.ARRAY(DataType.STRING))
+ redirectUris: string[]
+
+ @CreatedAt
+ createdAt: Date
-// ---------------------------------------------------------------------------
+ @UpdatedAt
+ updatedAt: Date
-function associate (models) {
- OAuthClient.hasMany(models.OAuthToken, {
- foreignKey: 'oAuthClientId',
+ @HasMany(() => OAuthTokenModel, {
onDelete: 'cascade'
})
-}
+ OAuthTokens: OAuthTokenModel[]
-countTotal = function () {
- return OAuthClient.count()
-}
+ static countTotal () {
+ return OAuthClientModel.count()
+ }
-loadFirstClient = function () {
- return OAuthClient.findOne()
-}
+ static loadFirstClient () {
+ return OAuthClientModel.findOne()
+ }
-getByIdAndSecret = function (clientId: string, clientSecret: string) {
- const query = {
- where: {
- clientId: clientId,
- clientSecret: clientSecret
+ static getByIdAndSecret (clientId: string, clientSecret: string) {
+ const query = {
+ where: {
+ clientId: clientId,
+ clientSecret: clientSecret
+ }
}
- }
- return OAuthClient.findOne(query)
+ return OAuthClientModel.findOne(query)
+ }
}
+++ /dev/null
-import * as Promise from 'bluebird'
-import * as Sequelize from 'sequelize'
-
-import { UserModel } from '../account/user-interface'
-
-export type OAuthTokenInfo = {
- refreshToken: string
- refreshTokenExpiresAt: Date,
- client: {
- id: number
- },
- user: {
- id: number
- }
-}
-
-export namespace OAuthTokenMethods {
- export type GetByRefreshTokenAndPopulateClient = (refreshToken: string) => Promise<OAuthTokenInfo>
- export type GetByTokenAndPopulateUser = (bearerToken: string) => Promise<OAuthTokenInstance>
- export type GetByRefreshTokenAndPopulateUser = (refreshToken: string) => Promise<OAuthTokenInstance>
-}
-
-export interface OAuthTokenClass {
- getByRefreshTokenAndPopulateClient: OAuthTokenMethods.GetByRefreshTokenAndPopulateClient
- getByTokenAndPopulateUser: OAuthTokenMethods.GetByTokenAndPopulateUser
- getByRefreshTokenAndPopulateUser: OAuthTokenMethods.GetByRefreshTokenAndPopulateUser
-}
-
-export interface OAuthTokenAttributes {
- accessToken: string
- accessTokenExpiresAt: Date
- refreshToken: string
- refreshTokenExpiresAt: Date
-
- userId?: number
- oAuthClientId?: number
- User?: UserModel
-}
-
-export interface OAuthTokenInstance extends OAuthTokenClass, OAuthTokenAttributes, Sequelize.Instance<OAuthTokenAttributes> {
- id: number
- createdAt: Date
- updatedAt: Date
-}
-
-export interface OAuthTokenModel extends OAuthTokenClass, Sequelize.Model<OAuthTokenInstance, OAuthTokenAttributes> {}
-import * as Sequelize from 'sequelize'
-
+import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { logger } from '../../helpers'
+import { AccountModel } from '../account/account'
+import { UserModel } from '../account/user'
+import { OAuthClientModel } from './oauth-client'
+
+export type OAuthTokenInfo = {
+ refreshToken: string
+ refreshTokenExpiresAt: Date,
+ client: {
+ id: number
+ },
+ user: {
+ id: number
+ }
+}
-import { addMethodsToModel } from '../utils'
-import { OAuthTokenAttributes, OAuthTokenInfo, OAuthTokenInstance, OAuthTokenMethods } from './oauth-token-interface'
-
-let OAuthToken: Sequelize.Model<OAuthTokenInstance, OAuthTokenAttributes>
-let getByRefreshTokenAndPopulateClient: OAuthTokenMethods.GetByRefreshTokenAndPopulateClient
-let getByTokenAndPopulateUser: OAuthTokenMethods.GetByTokenAndPopulateUser
-let getByRefreshTokenAndPopulateUser: OAuthTokenMethods.GetByRefreshTokenAndPopulateUser
-
-export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
- OAuthToken = sequelize.define<OAuthTokenInstance, OAuthTokenAttributes>('OAuthToken',
+@Table({
+ tableName: 'oAuthToken',
+ indexes: [
{
- accessToken: {
- type: DataTypes.STRING,
- allowNull: false
- },
- accessTokenExpiresAt: {
- type: DataTypes.DATE,
- allowNull: false
- },
- refreshToken: {
- type: DataTypes.STRING,
- allowNull: false
- },
- refreshTokenExpiresAt: {
- type: DataTypes.DATE,
- allowNull: false
- }
+ fields: [ 'refreshToken' ],
+ unique: true
},
{
- indexes: [
- {
- fields: [ 'refreshToken' ],
- unique: true
- },
- {
- fields: [ 'accessToken' ],
- unique: true
- },
- {
- fields: [ 'userId' ]
- },
- {
- fields: [ 'oAuthClientId' ]
- }
- ]
+ fields: [ 'accessToken' ],
+ unique: true
+ },
+ {
+ fields: [ 'userId' ]
+ },
+ {
+ fields: [ 'oAuthClientId' ]
}
- )
+ ]
+})
+export class OAuthTokenModel extends Model<OAuthTokenModel> {
- const classMethods = [
- associate,
+ @AllowNull(false)
+ @Column
+ accessToken: string
- getByRefreshTokenAndPopulateClient,
- getByTokenAndPopulateUser,
- getByRefreshTokenAndPopulateUser
- ]
- addMethodsToModel(OAuthToken, classMethods)
+ @AllowNull(false)
+ @Column
+ accessTokenExpiresAt: Date
- return OAuthToken
-}
+ @AllowNull(false)
+ @Column
+ refreshToken: string
-// ---------------------------------------------------------------------------
+ @AllowNull(false)
+ @Column
+ refreshTokenExpiresAt: Date
-function associate (models) {
- OAuthToken.belongsTo(models.User, {
+ @CreatedAt
+ createdAt: Date
+
+ @UpdatedAt
+ updatedAt: Date
+
+ @ForeignKey(() => UserModel)
+ @Column
+ userId: number
+
+ @BelongsTo(() => UserModel, {
foreignKey: {
- name: 'userId',
allowNull: false
},
onDelete: 'cascade'
})
+ User: UserModel
- OAuthToken.belongsTo(models.OAuthClient, {
+ @ForeignKey(() => OAuthClientModel)
+ @Column
+ oAuthClientId: number
+
+ @BelongsTo(() => OAuthClientModel, {
foreignKey: {
- name: 'oAuthClientId',
allowNull: false
},
onDelete: 'cascade'
})
-}
+ OAuthClients: OAuthClientModel[]
-getByRefreshTokenAndPopulateClient = function (refreshToken: string) {
- const query = {
- where: {
- refreshToken: refreshToken
- },
- include: [ OAuthToken['sequelize'].models.OAuthClient ]
+ static getByRefreshTokenAndPopulateClient (refreshToken: string) {
+ const query = {
+ where: {
+ refreshToken: refreshToken
+ },
+ include: [ OAuthClientModel ]
+ }
+
+ return OAuthTokenModel.findOne(query)
+ .then(token => {
+ if (!token) return null
+
+ return {
+ refreshToken: token.refreshToken,
+ refreshTokenExpiresAt: token.refreshTokenExpiresAt,
+ client: {
+ id: token.oAuthClientId
+ },
+ user: {
+ id: token.userId
+ }
+ } as OAuthTokenInfo
+ })
+ .catch(err => {
+ logger.info('getRefreshToken error.', err)
+ throw err
+ })
}
- return OAuthToken.findOne(query)
- .then(token => {
- if (!token) return null
-
- const tokenInfos: OAuthTokenInfo = {
- refreshToken: token.refreshToken,
- refreshTokenExpiresAt: token.refreshTokenExpiresAt,
- client: {
- id: token.oAuthClientId
- },
- user: {
- id: token.userId
+ static getByTokenAndPopulateUser (bearerToken: string) {
+ const query = {
+ where: {
+ accessToken: bearerToken
+ },
+ include: [
+ {
+ model: UserModel,
+ include: [
+ {
+ model: AccountModel,
+ required: true
+ }
+ ]
}
- }
+ ]
+ }
- return tokenInfos
- })
- .catch(err => {
- logger.info('getRefreshToken error.', err)
- throw err
- })
-}
+ return OAuthTokenModel.findOne(query).then(token => {
+ if (token) token['user'] = token.User
-getByTokenAndPopulateUser = function (bearerToken: string) {
- const query = {
- where: {
- accessToken: bearerToken
- },
- include: [
- {
- model: OAuthToken['sequelize'].models.User,
- include: [
- {
- model: OAuthToken['sequelize'].models.Account,
- required: true
- }
- ]
- }
- ]
+ return token
+ })
}
- return OAuthToken.findOne(query).then(token => {
- if (token) token['user'] = token.User
+ static getByRefreshTokenAndPopulateUser (refreshToken: string) {
+ const query = {
+ where: {
+ refreshToken: refreshToken
+ },
+ include: [
+ {
+ model: UserModel,
+ include: [
+ {
+ model: AccountModel,
+ required: true
+ }
+ ]
+ }
+ ]
+ }
- return token
- })
-}
+ return OAuthTokenModel.findOne(query).then(token => {
+ token['user'] = token.User
-getByRefreshTokenAndPopulateUser = function (refreshToken: string) {
- const query = {
- where: {
- refreshToken: refreshToken
- },
- include: [
- {
- model: OAuthToken['sequelize'].models.User,
- include: [
- {
- model: OAuthToken['sequelize'].models.Account,
- required: true
- }
- ]
- }
- ]
+ return token
+ })
}
-
- return OAuthToken.findOne(query).then(token => {
- token['user'] = token.User
-
- return token
- })
}
+++ /dev/null
-export * from './server-interface'
+++ /dev/null
-import * as Promise from 'bluebird'
-import * as Sequelize from 'sequelize'
-
-export namespace ServerMethods {
- export type ListBadServers = () => Promise<ServerInstance[]>
- export type UpdateServersScoreAndRemoveBadOnes = (goodServers: number[], badServers: number[]) => void
-}
-
-export interface ServerClass {
- updateServersScoreAndRemoveBadOnes: ServerMethods.UpdateServersScoreAndRemoveBadOnes
-}
-
-export interface ServerAttributes {
- id?: number
- host?: string
- score?: number | Sequelize.literal // Sequelize literal for 'score +' + value
-}
-
-export interface ServerInstance extends ServerClass, ServerAttributes, Sequelize.Instance<ServerAttributes> {
- createdAt: Date
- updatedAt: Date
-}
-
-export interface ServerModel extends ServerClass, Sequelize.Model<ServerInstance, ServerAttributes> {}
import * as Sequelize from 'sequelize'
-import { isHostValid, logger } from '../../helpers'
+import { AllowNull, Column, CreatedAt, Default, Is, IsInt, Max, Model, Table, UpdatedAt } from 'sequelize-typescript'
+import { logger } from '../../helpers'
+import { isHostValid } from '../../helpers/custom-validators/servers'
import { SERVERS_SCORE } from '../../initializers'
-import { addMethodsToModel } from '../utils'
-import { ServerAttributes, ServerInstance, ServerMethods } from './server-interface'
+import { throwIfNotValid } from '../utils'
-let Server: Sequelize.Model<ServerInstance, ServerAttributes>
-let updateServersScoreAndRemoveBadOnes: ServerMethods.UpdateServersScoreAndRemoveBadOnes
-
-export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
- Server = sequelize.define<ServerInstance, ServerAttributes>('Server',
+@Table({
+ tableName: 'server',
+ indexes: [
{
- host: {
- type: DataTypes.STRING,
- allowNull: false,
- validate: {
- isHost: value => {
- const res = isHostValid(value)
- if (res === false) throw new Error('Host not valid.')
- }
- }
- },
- score: {
- type: DataTypes.INTEGER,
- defaultValue: SERVERS_SCORE.BASE,
- allowNull: false,
- validate: {
- isInt: true,
- max: SERVERS_SCORE.MAX
- }
- }
+ fields: [ 'host' ],
+ unique: true
},
{
- indexes: [
- {
- fields: [ 'host' ],
- unique: true
- },
- {
- fields: [ 'score' ]
- }
- ]
+ fields: [ 'score' ]
}
- )
-
- const classMethods = [
- updateServersScoreAndRemoveBadOnes
]
- addMethodsToModel(Server, classMethods)
-
- return Server
-}
-
-// ------------------------------ Statics ------------------------------
-
-updateServersScoreAndRemoveBadOnes = function (goodServers: number[], badServers: number[]) {
- logger.info('Updating %d good servers and %d bad servers scores.', goodServers.length, badServers.length)
+})
+export class ServerModel extends Model<ServerModel> {
+
+ @AllowNull(false)
+ @Is('Host', value => throwIfNotValid(value, isHostValid, 'valid host'))
+ @Column
+ host: string
+
+ @AllowNull(false)
+ @Default(SERVERS_SCORE.BASE)
+ @IsInt
+ @Max(SERVERS_SCORE.MAX)
+ @Column
+ score: number
+
+ @CreatedAt
+ createdAt: Date
+
+ @UpdatedAt
+ updatedAt: Date
+
+ static updateServersScoreAndRemoveBadOnes (goodServers: number[], badServers: number[]) {
+ logger.info('Updating %d good servers and %d bad servers scores.', goodServers.length, badServers.length)
+
+ if (goodServers.length !== 0) {
+ ServerModel.incrementScores(goodServers, SERVERS_SCORE.BONUS)
+ .catch(err => {
+ logger.error('Cannot increment scores of good servers.', err)
+ })
+ }
- if (goodServers.length !== 0) {
- incrementScores(goodServers, SERVERS_SCORE.BONUS).catch(err => {
- logger.error('Cannot increment scores of good servers.', err)
- })
- }
+ if (badServers.length !== 0) {
+ ServerModel.incrementScores(badServers, SERVERS_SCORE.PENALTY)
+ .then(() => ServerModel.removeBadServers())
+ .catch(err => {
+ if (err) logger.error('Cannot decrement scores of bad servers.', err)
+ })
- if (badServers.length !== 0) {
- incrementScores(badServers, SERVERS_SCORE.PENALTY)
- .then(() => removeBadServers())
- .catch(err => {
- if (err) logger.error('Cannot decrement scores of bad servers.', err)
- })
+ }
}
-}
-
-// ---------------------------------------------------------------------------
-// Remove servers with a score of 0 (too many requests where they were unreachable)
-async function removeBadServers () {
- try {
- const servers = await listBadServers()
+ // Remove servers with a score of 0 (too many requests where they were unreachable)
+ private static async removeBadServers () {
+ try {
+ const servers = await ServerModel.listBadServers()
- const serversRemovePromises = servers.map(server => server.destroy())
- await Promise.all(serversRemovePromises)
+ const serversRemovePromises = servers.map(server => server.destroy())
+ await Promise.all(serversRemovePromises)
- const numberOfServersRemoved = servers.length
+ const numberOfServersRemoved = servers.length
- if (numberOfServersRemoved) {
- logger.info('Removed %d servers.', numberOfServersRemoved)
- } else {
- logger.info('No need to remove bad servers.')
+ if (numberOfServersRemoved) {
+ logger.info('Removed %d servers.', numberOfServersRemoved)
+ } else {
+ logger.info('No need to remove bad servers.')
+ }
+ } catch (err) {
+ logger.error('Cannot remove bad servers.', err)
}
- } catch (err) {
- logger.error('Cannot remove bad servers.', err)
}
-}
-function incrementScores (ids: number[], value: number) {
- const update = {
- score: Sequelize.literal('score +' + value)
- }
+ private static incrementScores (ids: number[], value: number) {
+ const update = {
+ score: Sequelize.literal('score +' + value)
+ }
- const options = {
- where: {
- id: {
- [Sequelize.Op.in]: ids
- }
- },
- // In this case score is a literal and not an integer so we do not validate it
- validate: false
- }
+ const options = {
+ where: {
+ id: {
+ [Sequelize.Op.in]: ids
+ }
+ },
+ // In this case score is a literal and not an integer so we do not validate it
+ validate: false
+ }
- return Server.update(update, options)
-}
+ return ServerModel.update(update, options)
+ }
-function listBadServers () {
- const query = {
- where: {
- score: {
- [Sequelize.Op.lte]: 0
+ private static listBadServers () {
+ const query = {
+ where: {
+ score: {
+ [Sequelize.Op.lte]: 0
+ }
}
}
- }
- return Server.findAll(query)
+ return ServerModel.findAll(query)
+ }
}
return [ field, direction ]
}
-function addMethodsToModel (model: any, classMethods: Function[], instanceMethods: Function[] = []) {
- classMethods.forEach(m => model[m.name] = m)
- instanceMethods.forEach(m => model.prototype[m.name] = m)
-}
-
function getSortOnModel (model: any, value: string) {
let sort = getSort(value)
- if (model) return [ { model: model }, sort[0], sort[1] ]
+ if (model) return [ model, sort[0], sort[1] ]
return sort
}
+function throwIfNotValid (value: any, validator: (value: any) => boolean, fieldName = 'value') {
+ if (validator(value) === false) {
+ throw new Error(`"${value}" is not a valid ${fieldName}.`)
+ }
+}
+
// ---------------------------------------------------------------------------
export {
- addMethodsToModel,
getSort,
- getSortOnModel
+ getSortOnModel,
+ throwIfNotValid
}
+++ /dev/null
-export * from './tag-interface'
-export * from './video-abuse-interface'
-export * from './video-blacklist-interface'
-export * from './video-channel-interface'
-export * from './video-tag-interface'
-export * from './video-file-interface'
-export * from './video-interface'
-export * from './video-share-interface'
-export * from './video-channel-share-interface'
+++ /dev/null
-import * as Sequelize from 'sequelize'
-import * as Promise from 'bluebird'
-
-export namespace TagMethods {
- export type FindOrCreateTags = (tags: string[], transaction: Sequelize.Transaction) => Promise<TagInstance[]>
-}
-
-export interface TagClass {
- findOrCreateTags: TagMethods.FindOrCreateTags
-}
-
-export interface TagAttributes {
- name: string
-}
-
-export interface TagInstance extends TagClass, TagAttributes, Sequelize.Instance<TagAttributes> {
- id: number
-}
-
-export interface TagModel extends TagClass, Sequelize.Model<TagInstance, TagAttributes> {}
-import * as Sequelize from 'sequelize'
-import * as Promise from 'bluebird'
-
-import { addMethodsToModel } from '../utils'
-import {
- TagInstance,
- TagAttributes,
-
- TagMethods
-} from './tag-interface'
-
-let Tag: Sequelize.Model<TagInstance, TagAttributes>
-let findOrCreateTags: TagMethods.FindOrCreateTags
-
-export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
- Tag = sequelize.define<TagInstance, TagAttributes>('Tag',
+import * as Bluebird from 'bluebird'
+import { Transaction } from 'sequelize'
+import { AllowNull, BelongsToMany, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
+import { isVideoTagValid } from '../../helpers/custom-validators/videos'
+import { throwIfNotValid } from '../utils'
+import { VideoModel } from './video'
+import { VideoTagModel } from './video-tag'
+
+@Table({
+ tableName: 'tag',
+ timestamps: false,
+ indexes: [
{
- name: {
- type: DataTypes.STRING,
- allowNull: false
- }
- },
- {
- timestamps: false,
- indexes: [
- {
- fields: [ 'name' ],
- unique: true
- }
- ]
+ fields: [ 'name' ],
+ unique: true
}
- )
-
- const classMethods = [
- associate,
-
- findOrCreateTags
]
- addMethodsToModel(Tag, classMethods)
+})
+export class TagModel extends Model<TagModel> {
- return Tag
-}
+ @AllowNull(false)
+ @Is('VideoTag', value => throwIfNotValid(value, isVideoTagValid, 'tag'))
+ @Column
+ name: string
-// ---------------------------------------------------------------------------
+ @CreatedAt
+ createdAt: Date
-function associate (models) {
- Tag.belongsToMany(models.Video, {
+ @UpdatedAt
+ updatedAt: Date
+
+ @BelongsToMany(() => VideoModel, {
foreignKey: 'tagId',
- through: models.VideoTag,
+ through: () => VideoTagModel,
onDelete: 'CASCADE'
})
-}
-
-findOrCreateTags = function (tags: string[], transaction: Sequelize.Transaction) {
- const tasks: Promise<TagInstance>[] = []
- tags.forEach(tag => {
- const query: Sequelize.FindOrInitializeOptions<TagAttributes> = {
- where: {
- name: tag
- },
- defaults: {
- name: tag
+ Videos: VideoModel[]
+
+ static findOrCreateTags (tags: string[], transaction: Transaction) {
+ const tasks: Bluebird<TagModel>[] = []
+ tags.forEach(tag => {
+ const query = {
+ where: {
+ name: tag
+ },
+ defaults: {
+ name: tag
+ }
}
- }
- if (transaction) query.transaction = transaction
+ if (transaction) query['transaction'] = transaction
- const promise = Tag.findOrCreate(query).then(([ tagInstance ]) => tagInstance)
- tasks.push(promise)
- })
+ const promise = TagModel.findOrCreate(query)
+ .then(([ tagInstance ]) => tagInstance)
+ tasks.push(promise)
+ })
- return Promise.all(tasks)
+ return Promise.all(tasks)
+ }
}
+++ /dev/null
-import * as Promise from 'bluebird'
-import * as Sequelize from 'sequelize'
-import { ResultList } from '../../../shared'
-import { VideoAbuse as FormattedVideoAbuse } from '../../../shared/models/videos/video-abuse.model'
-import { AccountInstance } from '../account/account-interface'
-import { ServerInstance } from '../server/server-interface'
-import { VideoInstance } from './video-interface'
-import { VideoAbuseObject } from '../../../shared/models/activitypub/objects/video-abuse-object'
-
-export namespace VideoAbuseMethods {
- export type ToFormattedJSON = (this: VideoAbuseInstance) => FormattedVideoAbuse
-
- export type ListForApi = (start: number, count: number, sort: string) => Promise< ResultList<VideoAbuseInstance> >
- export type ToActivityPubObject = () => VideoAbuseObject
-}
-
-export interface VideoAbuseClass {
- listForApi: VideoAbuseMethods.ListForApi
- toActivityPubObject: VideoAbuseMethods.ToActivityPubObject
-}
-
-export interface VideoAbuseAttributes {
- reason: string
- videoId: number
- reporterAccountId: number
-
- Account?: AccountInstance
- Video?: VideoInstance
-}
-
-export interface VideoAbuseInstance extends VideoAbuseClass, VideoAbuseAttributes, Sequelize.Instance<VideoAbuseAttributes> {
- id: number
- createdAt: Date
- updatedAt: Date
-
- Server: ServerInstance
-
- toFormattedJSON: VideoAbuseMethods.ToFormattedJSON
-}
-
-export interface VideoAbuseModel extends VideoAbuseClass, Sequelize.Model<VideoAbuseInstance, VideoAbuseAttributes> {}
-import * as Sequelize from 'sequelize'
-
+import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
+import { VideoAbuseObject } from '../../../shared/models/activitypub/objects'
+import { isVideoAbuseReasonValid } from '../../helpers/custom-validators/videos'
import { CONFIG } from '../../initializers'
-import { isVideoAbuseReasonValid } from '../../helpers'
-
-import { addMethodsToModel, getSort } from '../utils'
-import {
- VideoAbuseInstance,
- VideoAbuseAttributes,
-
- VideoAbuseMethods
-} from './video-abuse-interface'
-import { VideoAbuseObject } from '../../../shared/models/activitypub/objects/video-abuse-object'
-
-let VideoAbuse: Sequelize.Model<VideoAbuseInstance, VideoAbuseAttributes>
-let toFormattedJSON: VideoAbuseMethods.ToFormattedJSON
-let listForApi: VideoAbuseMethods.ListForApi
-let toActivityPubObject: VideoAbuseMethods.ToActivityPubObject
-
-export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
- VideoAbuse = sequelize.define<VideoAbuseInstance, VideoAbuseAttributes>('VideoAbuse',
+import { AccountModel } from '../account/account'
+import { ServerModel } from '../server/server'
+import { getSort, throwIfNotValid } from '../utils'
+import { VideoModel } from './video'
+
+@Table({
+ tableName: 'videoAbuse',
+ indexes: [
{
- reason: {
- type: DataTypes.STRING,
- allowNull: false,
- validate: {
- reasonValid: value => {
- const res = isVideoAbuseReasonValid(value)
- if (res === false) throw new Error('Video abuse reason is not valid.')
- }
- }
- }
+ fields: [ 'videoId' ]
},
{
- indexes: [
- {
- fields: [ 'videoId' ]
- },
- {
- fields: [ 'reporterAccountId' ]
- }
- ]
+ fields: [ 'reporterAccountId' ]
}
- )
-
- const classMethods = [
- associate,
-
- listForApi
- ]
- const instanceMethods = [
- toFormattedJSON,
- toActivityPubObject
]
- addMethodsToModel(VideoAbuse, classMethods, instanceMethods)
-
- return VideoAbuse
-}
-
-// ------------------------------ METHODS ------------------------------
-
-toFormattedJSON = function (this: VideoAbuseInstance) {
- let reporterServerHost
-
- if (this.Account.Server) {
- reporterServerHost = this.Account.Server.host
- } else {
- // It means it's our video
- reporterServerHost = CONFIG.WEBSERVER.HOST
- }
-
- const json = {
- id: this.id,
- reason: this.reason,
- reporterUsername: this.Account.name,
- reporterServerHost,
- videoId: this.Video.id,
- videoUUID: this.Video.uuid,
- videoName: this.Video.name,
- createdAt: this.createdAt
- }
+})
+export class VideoAbuseModel extends Model<VideoAbuseModel> {
- return json
-}
+ @AllowNull(false)
+ @Is('VideoAbuseReason', value => throwIfNotValid(value, isVideoAbuseReasonValid, 'reason'))
+ @Column
+ reason: string
-toActivityPubObject = function (this: VideoAbuseInstance) {
- const videoAbuseObject: VideoAbuseObject = {
- type: 'Flag' as 'Flag',
- content: this.reason,
- object: this.Video.url
- }
+ @CreatedAt
+ createdAt: Date
- return videoAbuseObject
-}
+ @UpdatedAt
+ updatedAt: Date
-// ------------------------------ STATICS ------------------------------
+ @ForeignKey(() => AccountModel)
+ @Column
+ reporterAccountId: number
-function associate (models) {
- VideoAbuse.belongsTo(models.Account, {
+ @BelongsTo(() => AccountModel, {
foreignKey: {
- name: 'reporterAccountId',
allowNull: false
},
- onDelete: 'CASCADE'
+ onDelete: 'cascade'
})
+ Account: AccountModel
+
+ @ForeignKey(() => VideoModel)
+ @Column
+ videoId: number
- VideoAbuse.belongsTo(models.Video, {
+ @BelongsTo(() => VideoModel, {
foreignKey: {
- name: 'videoId',
allowNull: false
},
- onDelete: 'CASCADE'
+ onDelete: 'cascade'
})
-}
+ Video: VideoModel
+
+ static listForApi (start: number, count: number, sort: string) {
+ const query = {
+ offset: start,
+ limit: count,
+ order: [ getSort(sort) ],
+ include: [
+ {
+ model: AccountModel,
+ required: true,
+ include: [
+ {
+ model: ServerModel,
+ required: false
+ }
+ ]
+ },
+ {
+ model: VideoModel,
+ required: true
+ }
+ ]
+ }
-listForApi = function (start: number, count: number, sort: string) {
- const query = {
- offset: start,
- limit: count,
- order: [ getSort(sort) ],
- include: [
- {
- model: VideoAbuse['sequelize'].models.Account,
- required: true,
- include: [
- {
- model: VideoAbuse['sequelize'].models.Server,
- required: false
- }
- ]
- },
- {
- model: VideoAbuse['sequelize'].models.Video,
- required: true
- }
- ]
+ return VideoAbuseModel.findAndCountAll(query)
+ .then(({ rows, count }) => {
+ return { total: count, data: rows }
+ })
}
- return VideoAbuse.findAndCountAll(query).then(({ rows, count }) => {
- return { total: count, data: rows }
- })
+ toFormattedJSON () {
+ let reporterServerHost
+
+ if (this.Account.Server) {
+ reporterServerHost = this.Account.Server.host
+ } else {
+ // It means it's our video
+ reporterServerHost = CONFIG.WEBSERVER.HOST
+ }
+
+ return {
+ id: this.id,
+ reason: this.reason,
+ reporterUsername: this.Account.name,
+ reporterServerHost,
+ videoId: this.Video.id,
+ videoUUID: this.Video.uuid,
+ videoName: this.Video.name,
+ createdAt: this.createdAt
+ }
+ }
+
+ toActivityPubObject (): VideoAbuseObject {
+ return {
+ type: 'Flag' as 'Flag',
+ content: this.reason,
+ object: this.Video.url
+ }
+ }
}
+++ /dev/null
-import * as Sequelize from 'sequelize'
-import * as Promise from 'bluebird'
-
-import { SortType } from '../../helpers'
-import { ResultList } from '../../../shared'
-import { VideoInstance } from './video-interface'
-
-// Don't use barrel, import just what we need
-import { BlacklistedVideo as FormattedBlacklistedVideo } from '../../../shared/models/videos/video-blacklist.model'
-
-export namespace BlacklistedVideoMethods {
- export type ToFormattedJSON = (this: BlacklistedVideoInstance) => FormattedBlacklistedVideo
- export type ListForApi = (start: number, count: number, sort: SortType) => Promise< ResultList<BlacklistedVideoInstance> >
- export type LoadByVideoId = (id: number) => Promise<BlacklistedVideoInstance>
-}
-
-export interface BlacklistedVideoClass {
- toFormattedJSON: BlacklistedVideoMethods.ToFormattedJSON
- listForApi: BlacklistedVideoMethods.ListForApi
- loadByVideoId: BlacklistedVideoMethods.LoadByVideoId
-}
-
-export interface BlacklistedVideoAttributes {
- videoId: number
-
- Video?: VideoInstance
-}
-
-export interface BlacklistedVideoInstance
- extends BlacklistedVideoClass, BlacklistedVideoAttributes, Sequelize.Instance<BlacklistedVideoAttributes> {
- id: number
- createdAt: Date
- updatedAt: Date
-
- toFormattedJSON: BlacklistedVideoMethods.ToFormattedJSON
-}
-
-export interface BlacklistedVideoModel
- extends BlacklistedVideoClass, Sequelize.Model<BlacklistedVideoInstance, BlacklistedVideoAttributes> {}
-import * as Sequelize from 'sequelize'
-
+import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { SortType } from '../../helpers'
-import { addMethodsToModel, getSortOnModel } from '../utils'
-import { VideoInstance } from './video-interface'
-import {
- BlacklistedVideoInstance,
- BlacklistedVideoAttributes,
-
- BlacklistedVideoMethods
-} from './video-blacklist-interface'
-
-let BlacklistedVideo: Sequelize.Model<BlacklistedVideoInstance, BlacklistedVideoAttributes>
-let toFormattedJSON: BlacklistedVideoMethods.ToFormattedJSON
-let listForApi: BlacklistedVideoMethods.ListForApi
-let loadByVideoId: BlacklistedVideoMethods.LoadByVideoId
+import { getSortOnModel } from '../utils'
+import { VideoModel } from './video'
-export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
- BlacklistedVideo = sequelize.define<BlacklistedVideoInstance, BlacklistedVideoAttributes>('BlacklistedVideo',
- {},
+@Table({
+ tableName: 'videoBlacklist',
+ indexes: [
{
- indexes: [
- {
- fields: [ 'videoId' ],
- unique: true
- }
- ]
+ fields: [ 'videoId' ],
+ unique: true
}
- )
-
- const classMethods = [
- associate,
-
- listForApi,
- loadByVideoId
]
- const instanceMethods = [
- toFormattedJSON
- ]
- addMethodsToModel(BlacklistedVideo, classMethods, instanceMethods)
-
- return BlacklistedVideo
-}
+})
+export class VideoBlacklistModel extends Model<VideoBlacklistModel> {
-// ------------------------------ METHODS ------------------------------
+ @CreatedAt
+ createdAt: Date
-toFormattedJSON = function (this: BlacklistedVideoInstance) {
- let video: VideoInstance
-
- video = this.Video
-
- return {
- id: this.id,
- videoId: this.videoId,
- createdAt: this.createdAt,
- updatedAt: this.updatedAt,
- name: video.name,
- uuid: video.uuid,
- description: video.description,
- duration: video.duration,
- views: video.views,
- likes: video.likes,
- dislikes: video.dislikes,
- nsfw: video.nsfw
- }
-}
+ @UpdatedAt
+ updatedAt: Date
-// ------------------------------ STATICS ------------------------------
+ @ForeignKey(() => VideoModel)
+ @Column
+ videoId: number
-function associate (models) {
- BlacklistedVideo.belongsTo(models.Video, {
+ @BelongsTo(() => VideoModel, {
foreignKey: {
- name: 'videoId',
allowNull: false
},
- onDelete: 'CASCADE'
+ onDelete: 'cascade'
})
-}
+ Video: VideoModel
+
+ static listForApi (start: number, count: number, sort: SortType) {
+ const query = {
+ offset: start,
+ limit: count,
+ order: [ getSortOnModel(sort.sortModel, sort.sortValue) ],
+ include: [ { model: VideoModel } ]
+ }
-listForApi = function (start: number, count: number, sort: SortType) {
- const query = {
- offset: start,
- limit: count,
- order: [ getSortOnModel(sort.sortModel, sort.sortValue) ],
- include: [ { model: BlacklistedVideo['sequelize'].models.Video } ]
+ return VideoBlacklistModel.findAndCountAll(query)
+ .then(({ rows, count }) => {
+ return {
+ data: rows,
+ total: count
+ }
+ })
}
- return BlacklistedVideo.findAndCountAll(query).then(({ rows, count }) => {
- return {
- data: rows,
- total: count
+ static loadByVideoId (id: number) {
+ const query = {
+ where: {
+ videoId: id
+ }
}
- })
-}
-loadByVideoId = function (id: number) {
- const query = {
- where: {
- videoId: id
- }
+ return VideoBlacklistModel.findOne(query)
}
- return BlacklistedVideo.findOne(query)
+ toFormattedJSON () {
+ const video = this.Video
+
+ return {
+ id: this.id,
+ videoId: this.videoId,
+ createdAt: this.createdAt,
+ updatedAt: this.updatedAt,
+ name: video.name,
+ uuid: video.uuid,
+ description: video.description,
+ duration: video.duration,
+ views: video.views,
+ likes: video.likes,
+ dislikes: video.dislikes,
+ nsfw: video.nsfw
+ }
+ }
}
+++ /dev/null
-import * as Promise from 'bluebird'
-import * as Sequelize from 'sequelize'
-
-import { ResultList } from '../../../shared'
-import { VideoChannelObject } from '../../../shared/models/activitypub/objects/video-channel-object'
-import { VideoChannel as FormattedVideoChannel } from '../../../shared/models/videos/video-channel.model'
-import { AccountInstance } from '../account/account-interface'
-import { VideoInstance } from './video-interface'
-import { VideoChannelShareInstance } from './video-channel-share-interface'
-
-export namespace VideoChannelMethods {
- export type ToFormattedJSON = (this: VideoChannelInstance) => FormattedVideoChannel
- export type ToActivityPubObject = (this: VideoChannelInstance) => VideoChannelObject
- export type IsOwned = (this: VideoChannelInstance) => boolean
-
- export type CountByAccount = (accountId: number) => Promise<number>
- export type ListForApi = (start: number, count: number, sort: string) => Promise< ResultList<VideoChannelInstance> >
- export type LoadByIdAndAccount = (id: number, accountId: number) => Promise<VideoChannelInstance>
- export type ListByAccount = (accountId: number) => Promise< ResultList<VideoChannelInstance> >
- export type LoadAndPopulateAccount = (id: number) => Promise<VideoChannelInstance>
- export type LoadByUUIDAndPopulateAccount = (uuid: string) => Promise<VideoChannelInstance>
- export type LoadByUUID = (uuid: string, t?: Sequelize.Transaction) => Promise<VideoChannelInstance>
- export type LoadByHostAndUUID = (uuid: string, serverHost: string, t?: Sequelize.Transaction) => Promise<VideoChannelInstance>
- export type LoadAndPopulateAccountAndVideos = (id: number) => Promise<VideoChannelInstance>
- export type LoadByUrl = (uuid: string, t?: Sequelize.Transaction) => Promise<VideoChannelInstance>
- export type LoadByUUIDOrUrl = (uuid: string, url: string, t?: Sequelize.Transaction) => Promise<VideoChannelInstance>
-}
-
-export interface VideoChannelClass {
- countByAccount: VideoChannelMethods.CountByAccount
- listForApi: VideoChannelMethods.ListForApi
- listByAccount: VideoChannelMethods.ListByAccount
- loadByIdAndAccount: VideoChannelMethods.LoadByIdAndAccount
- loadAndPopulateAccount: VideoChannelMethods.LoadAndPopulateAccount
- loadByUUIDAndPopulateAccount: VideoChannelMethods.LoadByUUIDAndPopulateAccount
- loadAndPopulateAccountAndVideos: VideoChannelMethods.LoadAndPopulateAccountAndVideos
- loadByUrl: VideoChannelMethods.LoadByUrl
- loadByUUIDOrUrl: VideoChannelMethods.LoadByUUIDOrUrl
-}
-
-export interface VideoChannelAttributes {
- id?: number
- uuid?: string
- name: string
- description: string
- remote: boolean
- url?: string
-
- Account?: AccountInstance
- Videos?: VideoInstance[]
- VideoChannelShares?: VideoChannelShareInstance[]
-}
-
-export interface VideoChannelInstance extends VideoChannelClass, VideoChannelAttributes, Sequelize.Instance<VideoChannelAttributes> {
- id: number
- createdAt: Date
- updatedAt: Date
-
- isOwned: VideoChannelMethods.IsOwned
- toFormattedJSON: VideoChannelMethods.ToFormattedJSON
- toActivityPubObject: VideoChannelMethods.ToActivityPubObject
-}
-
-export interface VideoChannelModel extends VideoChannelClass, Sequelize.Model<VideoChannelInstance, VideoChannelAttributes> {}
+++ /dev/null
-import * as Bluebird from 'bluebird'
-import * as Sequelize from 'sequelize'
-import { AccountInstance } from '../account/account-interface'
-import { VideoChannelInstance } from './video-channel-interface'
-
-export namespace VideoChannelShareMethods {
- export type LoadAccountsByShare = (videoChannelId: number, t: Sequelize.Transaction) => Bluebird<AccountInstance[]>
- export type Load = (accountId: number, videoId: number, t: Sequelize.Transaction) => Bluebird<VideoChannelShareInstance>
-}
-
-export interface VideoChannelShareClass {
- loadAccountsByShare: VideoChannelShareMethods.LoadAccountsByShare
- load: VideoChannelShareMethods.Load
-}
-
-export interface VideoChannelShareAttributes {
- accountId: number
- videoChannelId: number
-}
-
-export interface VideoChannelShareInstance
- extends VideoChannelShareClass, VideoChannelShareAttributes, Sequelize.Instance<VideoChannelShareAttributes> {
- id: number
- createdAt: Date
- updatedAt: Date
-
- Account?: AccountInstance
- VideoChannel?: VideoChannelInstance
-}
-
-export interface VideoChannelShareModel
- extends VideoChannelShareClass, Sequelize.Model<VideoChannelShareInstance, VideoChannelShareAttributes> {}
import * as Sequelize from 'sequelize'
+import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
+import { AccountModel } from '../account/account'
+import { VideoChannelModel } from './video-channel'
-import { addMethodsToModel } from '../utils'
-import { VideoChannelShareAttributes, VideoChannelShareInstance, VideoChannelShareMethods } from './video-channel-share-interface'
-
-let VideoChannelShare: Sequelize.Model<VideoChannelShareInstance, VideoChannelShareAttributes>
-let loadAccountsByShare: VideoChannelShareMethods.LoadAccountsByShare
-let load: VideoChannelShareMethods.Load
-
-export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
- VideoChannelShare = sequelize.define<VideoChannelShareInstance, VideoChannelShareAttributes>('VideoChannelShare',
- { },
+@Table({
+ tableName: 'videoChannelShare',
+ indexes: [
{
- indexes: [
- {
- fields: [ 'accountId' ]
- },
- {
- fields: [ 'videoChannelId' ]
- }
- ]
+ fields: [ 'accountId' ]
+ },
+ {
+ fields: [ 'videoChannelId' ]
}
- )
-
- const classMethods = [
- associate,
- load,
- loadAccountsByShare
]
- addMethodsToModel(VideoChannelShare, classMethods)
+})
+export class VideoChannelShareModel extends Model<VideoChannelShareModel> {
+ @CreatedAt
+ createdAt: Date
- return VideoChannelShare
-}
+ @UpdatedAt
+ updatedAt: Date
-// ------------------------------ METHODS ------------------------------
+ @ForeignKey(() => AccountModel)
+ @Column
+ accountId: number
-function associate (models) {
- VideoChannelShare.belongsTo(models.Account, {
+ @BelongsTo(() => AccountModel, {
foreignKey: {
- name: 'accountId',
allowNull: false
},
onDelete: 'cascade'
})
+ Account: AccountModel
- VideoChannelShare.belongsTo(models.VideoChannel, {
+ @ForeignKey(() => VideoChannelModel)
+ @Column
+ videoChannelId: number
+
+ @BelongsTo(() => VideoChannelModel, {
foreignKey: {
- name: 'videoChannelId',
- allowNull: true
+ allowNull: false
},
onDelete: 'cascade'
})
-}
-
-load = function (accountId: number, videoChannelId: number, t: Sequelize.Transaction) {
- return VideoChannelShare.findOne({
- where: {
- accountId,
- videoChannelId
- },
- include: [
- VideoChannelShare['sequelize'].models.Account,
- VideoChannelShare['sequelize'].models.VideoChannel
- ],
- transaction: t
- })
-}
+ VideoChannel: VideoChannelModel
-loadAccountsByShare = function (videoChannelId: number, t: Sequelize.Transaction) {
- const query = {
- where: {
- videoChannelId
- },
- include: [
- {
- model: VideoChannelShare['sequelize'].models.Account,
- required: true
- }
- ],
- transaction: t
+ static load (accountId: number, videoChannelId: number, t: Sequelize.Transaction) {
+ return VideoChannelShareModel.findOne({
+ where: {
+ accountId,
+ videoChannelId
+ },
+ include: [
+ AccountModel,
+ VideoChannelModel
+ ],
+ transaction: t
+ })
}
- return VideoChannelShare.findAll(query)
- .then(res => res.map(r => r.Account))
+ static loadAccountsByShare (videoChannelId: number, t: Sequelize.Transaction) {
+ const query = {
+ where: {
+ videoChannelId
+ },
+ include: [
+ {
+ model: AccountModel,
+ required: true
+ }
+ ],
+ transaction: t
+ }
+
+ return VideoChannelShareModel.findAll(query)
+ .then(res => res.map(r => r.Account))
+ }
}
import * as Sequelize from 'sequelize'
-import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../../helpers'
-import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
-import { sendDeleteVideoChannel } from '../../lib/activitypub/send/send-delete'
-
-import { addMethodsToModel, getSort } from '../utils'
-import { VideoChannelAttributes, VideoChannelInstance, VideoChannelMethods } from './video-channel-interface'
-import { getAnnounceActivityPubUrl } from '../../lib/activitypub/url'
-import { activityPubCollection } from '../../helpers/activitypub'
-import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
-
-let VideoChannel: Sequelize.Model<VideoChannelInstance, VideoChannelAttributes>
-let toFormattedJSON: VideoChannelMethods.ToFormattedJSON
-let toActivityPubObject: VideoChannelMethods.ToActivityPubObject
-let isOwned: VideoChannelMethods.IsOwned
-let countByAccount: VideoChannelMethods.CountByAccount
-let listForApi: VideoChannelMethods.ListForApi
-let listByAccount: VideoChannelMethods.ListByAccount
-let loadByIdAndAccount: VideoChannelMethods.LoadByIdAndAccount
-let loadByUUID: VideoChannelMethods.LoadByUUID
-let loadAndPopulateAccount: VideoChannelMethods.LoadAndPopulateAccount
-let loadByUUIDAndPopulateAccount: VideoChannelMethods.LoadByUUIDAndPopulateAccount
-let loadByHostAndUUID: VideoChannelMethods.LoadByHostAndUUID
-let loadAndPopulateAccountAndVideos: VideoChannelMethods.LoadAndPopulateAccountAndVideos
-let loadByUrl: VideoChannelMethods.LoadByUrl
-let loadByUUIDOrUrl: VideoChannelMethods.LoadByUUIDOrUrl
-
-export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
- VideoChannel = sequelize.define<VideoChannelInstance, VideoChannelAttributes>('VideoChannel',
+import {
+ AfterDestroy,
+ AllowNull,
+ BelongsTo,
+ Column,
+ CreatedAt,
+ DataType,
+ Default,
+ ForeignKey,
+ HasMany,
+ Is,
+ IsUUID,
+ Model,
+ Table,
+ UpdatedAt
+} from 'sequelize-typescript'
+import { IFindOptions } from 'sequelize-typescript/lib/interfaces/IFindOptions'
+import { activityPubCollection } from '../../helpers'
+import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub'
+import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../../helpers/custom-validators/video-channels'
+import { CONSTRAINTS_FIELDS } from '../../initializers'
+import { getAnnounceActivityPubUrl } from '../../lib/activitypub'
+import { sendDeleteVideoChannel } from '../../lib/activitypub/send'
+import { AccountModel } from '../account/account'
+import { ServerModel } from '../server/server'
+import { getSort, throwIfNotValid } from '../utils'
+import { VideoModel } from './video'
+import { VideoChannelShareModel } from './video-channel-share'
+
+@Table({
+ tableName: 'videoChannel',
+ indexes: [
{
- uuid: {
- type: DataTypes.UUID,
- defaultValue: DataTypes.UUIDV4,
- allowNull: false,
- validate: {
- isUUID: 4
- }
- },
- name: {
- type: DataTypes.STRING,
- allowNull: false,
- validate: {
- nameValid: value => {
- const res = isVideoChannelNameValid(value)
- if (res === false) throw new Error('Video channel name is not valid.')
- }
- }
- },
- description: {
- type: DataTypes.STRING,
- allowNull: true,
- validate: {
- descriptionValid: value => {
- const res = isVideoChannelDescriptionValid(value)
- if (res === false) throw new Error('Video channel description is not valid.')
- }
- }
- },
- remote: {
- type: DataTypes.BOOLEAN,
- allowNull: false,
- defaultValue: false
- },
- url: {
- type: DataTypes.STRING(CONSTRAINTS_FIELDS.VIDEO_CHANNELS.URL.max),
- allowNull: false,
- validate: {
- urlValid: value => {
- const res = isActivityPubUrlValid(value)
- if (res === false) throw new Error('Video channel URL is not valid.')
- }
- }
- }
- },
- {
- indexes: [
- {
- fields: [ 'accountId' ]
- }
- ],
- hooks: {
- afterDestroy
- }
+ fields: [ 'accountId' ]
}
- )
-
- const classMethods = [
- associate,
-
- listForApi,
- listByAccount,
- loadByIdAndAccount,
- loadAndPopulateAccount,
- loadByUUIDAndPopulateAccount,
- loadByUUID,
- loadByHostAndUUID,
- loadAndPopulateAccountAndVideos,
- countByAccount,
- loadByUrl,
- loadByUUIDOrUrl
]
- const instanceMethods = [
- isOwned,
- toFormattedJSON,
- toActivityPubObject
- ]
- addMethodsToModel(VideoChannel, classMethods, instanceMethods)
+})
+export class VideoChannelModel extends Model<VideoChannelModel> {
- return VideoChannel
-}
+ @AllowNull(false)
+ @Default(DataType.UUIDV4)
+ @IsUUID(4)
+ @Column(DataType.UUID)
+ uuid: string
-// ------------------------------ METHODS ------------------------------
+ @AllowNull(false)
+ @Is('VideoChannelName', value => throwIfNotValid(value, isVideoChannelNameValid, 'name'))
+ @Column
+ name: string
-isOwned = function (this: VideoChannelInstance) {
- return this.remote === false
-}
-
-toFormattedJSON = function (this: VideoChannelInstance) {
- const json = {
- id: this.id,
- uuid: this.uuid,
- name: this.name,
- description: this.description,
- isLocal: this.isOwned(),
- createdAt: this.createdAt,
- updatedAt: this.updatedAt
- }
+ @AllowNull(true)
+ @Is('VideoChannelDescription', value => throwIfNotValid(value, isVideoChannelDescriptionValid, 'description'))
+ @Column
+ description: string
- if (this.Account !== undefined) {
- json['owner'] = {
- name: this.Account.name,
- uuid: this.Account.uuid
- }
- }
+ @AllowNull(false)
+ @Column
+ remote: boolean
- if (Array.isArray(this.Videos)) {
- json['videos'] = this.Videos.map(v => v.toFormattedJSON())
- }
+ @AllowNull(false)
+ @Is('VideoChannelUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url'))
+ @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_CHANNELS.URL.max))
+ url: string
- return json
-}
+ @CreatedAt
+ createdAt: Date
-toActivityPubObject = function (this: VideoChannelInstance) {
- let sharesObject
- if (Array.isArray(this.VideoChannelShares)) {
- const shares: string[] = []
+ @UpdatedAt
+ updatedAt: Date
- for (const videoChannelShare of this.VideoChannelShares) {
- const shareUrl = getAnnounceActivityPubUrl(this.url, videoChannelShare.Account)
- shares.push(shareUrl)
- }
+ @ForeignKey(() => AccountModel)
+ @Column
+ accountId: number
- sharesObject = activityPubCollection(shares)
- }
-
- const json = {
- type: 'VideoChannel' as 'VideoChannel',
- id: this.url,
- uuid: this.uuid,
- content: this.description,
- name: this.name,
- published: this.createdAt.toISOString(),
- updated: this.updatedAt.toISOString(),
- shares: sharesObject
- }
-
- return json
-}
-
-// ------------------------------ STATICS ------------------------------
+ @BelongsTo(() => AccountModel, {
+ foreignKey: {
+ allowNull: false
+ },
+ onDelete: 'CASCADE'
+ })
+ Account: AccountModel
-function associate (models) {
- VideoChannel.belongsTo(models.Account, {
+ @HasMany(() => VideoModel, {
foreignKey: {
- name: 'accountId',
+ name: 'channelId',
allowNull: false
},
onDelete: 'CASCADE'
})
+ Videos: VideoModel[]
- VideoChannel.hasMany(models.Video, {
+ @HasMany(() => VideoChannelShareModel, {
foreignKey: {
name: 'channelId',
allowNull: false
},
onDelete: 'CASCADE'
})
-}
+ VideoChannelShares: VideoChannelShareModel[]
-function afterDestroy (videoChannel: VideoChannelInstance) {
- if (videoChannel.isOwned()) {
- return sendDeleteVideoChannel(videoChannel, undefined)
- }
+ @AfterDestroy
+ static sendDeleteIfOwned (instance: VideoChannelModel) {
+ if (instance.isOwned()) {
+ return sendDeleteVideoChannel(instance, undefined)
+ }
- return undefined
-}
+ return undefined
+ }
-countByAccount = function (accountId: number) {
- const query = {
- where: {
- accountId
+ static countByAccount (accountId: number) {
+ const query = {
+ where: {
+ accountId
+ }
}
+
+ return VideoChannelModel.count(query)
}
- return VideoChannel.count(query)
-}
+ static listForApi (start: number, count: number, sort: string) {
+ const query = {
+ offset: start,
+ limit: count,
+ order: [ getSort(sort) ],
+ include: [
+ {
+ model: AccountModel,
+ required: true,
+ include: [ { model: ServerModel, required: false } ]
+ }
+ ]
+ }
-listForApi = function (start: number, count: number, sort: string) {
- const query = {
- offset: start,
- limit: count,
- order: [ getSort(sort) ],
- include: [
- {
- model: VideoChannel['sequelize'].models.Account,
- required: true,
- include: [ { model: VideoChannel['sequelize'].models.Server, required: false } ]
- }
- ]
+ return VideoChannelModel.findAndCountAll(query)
+ .then(({ rows, count }) => {
+ return { total: count, data: rows }
+ })
}
- return VideoChannel.findAndCountAll(query).then(({ rows, count }) => {
- return { total: count, data: rows }
- })
-}
+ static listByAccount (accountId: number) {
+ const query = {
+ order: [ getSort('createdAt') ],
+ include: [
+ {
+ model: AccountModel,
+ where: {
+ id: accountId
+ },
+ required: true,
+ include: [ { model: ServerModel, required: false } ]
+ }
+ ]
+ }
-listByAccount = function (accountId: number) {
- const query = {
- order: [ getSort('createdAt') ],
- include: [
- {
- model: VideoChannel['sequelize'].models.Account,
- where: {
- id: accountId
- },
- required: true,
- include: [ { model: VideoChannel['sequelize'].models.Server, required: false } ]
- }
- ]
+ return VideoChannelModel.findAndCountAll(query)
+ .then(({ rows, count }) => {
+ return { total: count, data: rows }
+ })
}
- return VideoChannel.findAndCountAll(query).then(({ rows, count }) => {
- return { total: count, data: rows }
- })
-}
+ static loadByUUID (uuid: string, t?: Sequelize.Transaction) {
+ const query: IFindOptions<VideoChannelModel> = {
+ where: {
+ uuid
+ }
+ }
+
+ if (t !== undefined) query.transaction = t
-loadByUUID = function (uuid: string, t?: Sequelize.Transaction) {
- const query: Sequelize.FindOptions<VideoChannelAttributes> = {
- where: {
- uuid
+ return VideoChannelModel.findOne(query)
+ }
+
+ static loadByUrl (url: string, t?: Sequelize.Transaction) {
+ const query: IFindOptions<VideoChannelModel> = {
+ where: {
+ url
+ },
+ include: [ AccountModel ]
}
+
+ if (t !== undefined) query.transaction = t
+
+ return VideoChannelModel.findOne(query)
}
- if (t !== undefined) query.transaction = t
+ static loadByUUIDOrUrl (uuid: string, url: string, t?: Sequelize.Transaction) {
+ const query: IFindOptions<VideoChannelModel> = {
+ where: {
+ [ Sequelize.Op.or ]: [
+ { uuid },
+ { url }
+ ]
+ }
+ }
- return VideoChannel.findOne(query)
-}
+ if (t !== undefined) query.transaction = t
-loadByUrl = function (url: string, t?: Sequelize.Transaction) {
- const query: Sequelize.FindOptions<VideoChannelAttributes> = {
- where: {
- url
- },
- include: [ VideoChannel['sequelize'].models.Account ]
+ return VideoChannelModel.findOne(query)
}
- if (t !== undefined) query.transaction = t
+ static loadByHostAndUUID (fromHost: string, uuid: string, t?: Sequelize.Transaction) {
+ const query: IFindOptions<VideoChannelModel> = {
+ where: {
+ uuid
+ },
+ include: [
+ {
+ model: AccountModel,
+ include: [
+ {
+ model: ServerModel,
+ required: true,
+ where: {
+ host: fromHost
+ }
+ }
+ ]
+ }
+ ]
+ }
- return VideoChannel.findOne(query)
-}
+ if (t !== undefined) query.transaction = t
+
+ return VideoChannelModel.findOne(query)
+ }
-loadByUUIDOrUrl = function (uuid: string, url: string, t?: Sequelize.Transaction) {
- const query: Sequelize.FindOptions<VideoChannelAttributes> = {
- where: {
- [Sequelize.Op.or]: [
- { uuid },
- { url }
+ static loadByIdAndAccount (id: number, accountId: number) {
+ const options = {
+ where: {
+ id,
+ accountId
+ },
+ include: [
+ {
+ model: AccountModel,
+ include: [ { model: ServerModel, required: false } ]
+ }
]
}
+
+ return VideoChannelModel.findOne(options)
}
- if (t !== undefined) query.transaction = t
+ static loadAndPopulateAccount (id: number) {
+ const options = {
+ include: [
+ {
+ model: AccountModel,
+ include: [ { model: ServerModel, required: false } ]
+ }
+ ]
+ }
- return VideoChannel.findOne(query)
-}
+ return VideoChannelModel.findById(id, options)
+ }
-loadByHostAndUUID = function (fromHost: string, uuid: string, t?: Sequelize.Transaction) {
- const query: Sequelize.FindOptions<VideoChannelAttributes> = {
- where: {
- uuid
- },
- include: [
- {
- model: VideoChannel['sequelize'].models.Account,
- include: [
- {
- model: VideoChannel['sequelize'].models.Server,
- required: true,
- where: {
- host: fromHost
- }
- }
- ]
- }
- ]
+ static loadByUUIDAndPopulateAccount (uuid: string) {
+ const options = {
+ where: {
+ uuid
+ },
+ include: [
+ {
+ model: AccountModel,
+ include: [ { model: ServerModel, required: false } ]
+ }
+ ]
+ }
+
+ return VideoChannelModel.findOne(options)
}
- if (t !== undefined) query.transaction = t
+ static loadAndPopulateAccountAndVideos (id: number) {
+ const options = {
+ include: [
+ {
+ model: AccountModel,
+ include: [ { model: ServerModel, required: false } ]
+ },
+ VideoModel
+ ]
+ }
- return VideoChannel.findOne(query)
-}
+ return VideoChannelModel.findById(id, options)
+ }
-loadByIdAndAccount = function (id: number, accountId: number) {
- const options = {
- where: {
- id,
- accountId
- },
- include: [
- {
- model: VideoChannel['sequelize'].models.Account,
- include: [ { model: VideoChannel['sequelize'].models.Server, required: false } ]
- }
- ]
+ isOwned () {
+ return this.remote === false
}
- return VideoChannel.findOne(options)
-}
+ toFormattedJSON () {
+ const json = {
+ id: this.id,
+ uuid: this.uuid,
+ name: this.name,
+ description: this.description,
+ isLocal: this.isOwned(),
+ createdAt: this.createdAt,
+ updatedAt: this.updatedAt
+ }
-loadAndPopulateAccount = function (id: number) {
- const options = {
- include: [
- {
- model: VideoChannel['sequelize'].models.Account,
- include: [ { model: VideoChannel['sequelize'].models.Server, required: false } ]
+ if (this.Account !== undefined) {
+ json[ 'owner' ] = {
+ name: this.Account.name,
+ uuid: this.Account.uuid
}
- ]
+ }
+
+ if (Array.isArray(this.Videos)) {
+ json[ 'videos' ] = this.Videos.map(v => v.toFormattedJSON())
+ }
+
+ return json
}
- return VideoChannel.findById(id, options)
-}
+ toActivityPubObject () {
+ let sharesObject
+ if (Array.isArray(this.VideoChannelShares)) {
+ const shares: string[] = []
-loadByUUIDAndPopulateAccount = function (uuid: string) {
- const options = {
- where: {
- uuid
- },
- include: [
- {
- model: VideoChannel['sequelize'].models.Account,
- include: [ { model: VideoChannel['sequelize'].models.Server, required: false } ]
+ for (const videoChannelShare of this.VideoChannelShares) {
+ const shareUrl = getAnnounceActivityPubUrl(this.url, videoChannelShare.Account)
+ shares.push(shareUrl)
}
- ]
- }
- return VideoChannel.findOne(options)
-}
+ sharesObject = activityPubCollection(shares)
+ }
-loadAndPopulateAccountAndVideos = function (id: number) {
- const options = {
- include: [
- {
- model: VideoChannel['sequelize'].models.Account,
- include: [ { model: VideoChannel['sequelize'].models.Server, required: false } ]
- },
- VideoChannel['sequelize'].models.Video
- ]
+ return {
+ type: 'VideoChannel' as 'VideoChannel',
+ id: this.url,
+ uuid: this.uuid,
+ content: this.description,
+ name: this.name,
+ published: this.createdAt.toISOString(),
+ updated: this.updatedAt.toISOString(),
+ shares: sharesObject
+ }
}
-
- return VideoChannel.findById(id, options)
}
+++ /dev/null
-import * as Sequelize from 'sequelize'
-
-export namespace VideoFileMethods {
-}
-
-export interface VideoFileClass {
-}
-
-export interface VideoFileAttributes {
- resolution: number
- size: number
- infoHash?: string
- extname: string
-
- videoId?: number
-}
-
-export interface VideoFileInstance extends VideoFileClass, VideoFileAttributes, Sequelize.Instance<VideoFileAttributes> {
- id: number
- createdAt: Date
- updatedAt: Date
-}
-
-export interface VideoFileModel extends VideoFileClass, Sequelize.Model<VideoFileInstance, VideoFileAttributes> {}
import { values } from 'lodash'
-import * as Sequelize from 'sequelize'
+import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { isVideoFileInfoHashValid, isVideoFileResolutionValid, isVideoFileSizeValid } from '../../helpers/custom-validators/videos'
-import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
+import { CONSTRAINTS_FIELDS } from '../../initializers'
+import { throwIfNotValid } from '../utils'
+import { VideoModel } from './video'
-import { addMethodsToModel } from '../utils'
-import { VideoFileAttributes, VideoFileInstance } from './video-file-interface'
-
-let VideoFile: Sequelize.Model<VideoFileInstance, VideoFileAttributes>
-
-export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
- VideoFile = sequelize.define<VideoFileInstance, VideoFileAttributes>('VideoFile',
+@Table({
+ tableName: 'videoFile',
+ indexes: [
{
- resolution: {
- type: DataTypes.INTEGER,
- allowNull: false,
- validate: {
- resolutionValid: value => {
- const res = isVideoFileResolutionValid(value)
- if (res === false) throw new Error('Video file resolution is not valid.')
- }
- }
- },
- size: {
- type: DataTypes.BIGINT,
- allowNull: false,
- validate: {
- sizeValid: value => {
- const res = isVideoFileSizeValid(value)
- if (res === false) throw new Error('Video file size is not valid.')
- }
- }
- },
- extname: {
- type: DataTypes.ENUM(values(CONSTRAINTS_FIELDS.VIDEOS.EXTNAME)),
- allowNull: false
- },
- infoHash: {
- type: DataTypes.STRING,
- allowNull: false,
- validate: {
- infoHashValid: value => {
- const res = isVideoFileInfoHashValid(value)
- if (res === false) throw new Error('Video file info hash is not valid.')
- }
- }
- }
+ fields: [ 'videoId' ]
},
{
- indexes: [
- {
- fields: [ 'videoId' ]
- },
- {
- fields: [ 'infoHash' ]
- }
- ]
+ fields: [ 'infoHash' ]
}
- )
-
- const classMethods = [
- associate
]
- addMethodsToModel(VideoFile, classMethods)
-
- return VideoFile
-}
-
-// ------------------------------ STATICS ------------------------------
-
-function associate (models) {
- VideoFile.belongsTo(models.Video, {
+})
+export class VideoFileModel extends Model<VideoFileModel> {
+ @CreatedAt
+ createdAt: Date
+
+ @UpdatedAt
+ updatedAt: Date
+
+ @AllowNull(false)
+ @Is('VideoFileResolution', value => throwIfNotValid(value, isVideoFileResolutionValid, 'resolution'))
+ @Column
+ resolution: number
+
+ @AllowNull(false)
+ @Is('VideoFileSize', value => throwIfNotValid(value, isVideoFileSizeValid, 'size'))
+ @Column(DataType.BIGINT)
+ size: number
+
+ @AllowNull(false)
+ @Column(DataType.ENUM(values(CONSTRAINTS_FIELDS.VIDEOS.EXTNAME)))
+ extname: string
+
+ @AllowNull(false)
+ @Is('VideoFileSize', value => throwIfNotValid(value, isVideoFileInfoHashValid, 'info hash'))
+ @Column
+ infoHash: string
+
+ @ForeignKey(() => VideoModel)
+ @Column
+ videoId: number
+
+ @BelongsTo(() => VideoModel, {
foreignKey: {
- name: 'videoId',
allowNull: false
},
onDelete: 'CASCADE'
})
+ Video: VideoModel
}
-
-// ------------------------------ METHODS ------------------------------
+++ /dev/null
-import * as Bluebird from 'bluebird'
-import * as Sequelize from 'sequelize'
-import { VideoTorrentObject } from '../../../shared/models/activitypub/objects/video-torrent-object'
-import { ResultList } from '../../../shared/models/result-list.model'
-import { Video as FormattedVideo, VideoDetails as FormattedDetailsVideo } from '../../../shared/models/videos/video.model'
-import { AccountVideoRateInstance } from '../account/account-video-rate-interface'
-
-import { TagAttributes, TagInstance } from './tag-interface'
-import { VideoChannelInstance } from './video-channel-interface'
-import { VideoFileAttributes, VideoFileInstance } from './video-file-interface'
-import { VideoShareInstance } from './video-share-interface'
-
-export namespace VideoMethods {
- export type GetThumbnailName = (this: VideoInstance) => string
- export type GetPreviewName = (this: VideoInstance) => string
- export type IsOwned = (this: VideoInstance) => boolean
- export type ToFormattedJSON = (this: VideoInstance) => FormattedVideo
- export type ToFormattedDetailsJSON = (this: VideoInstance) => FormattedDetailsVideo
-
- export type GetOriginalFile = (this: VideoInstance) => VideoFileInstance
- export type GetTorrentFileName = (this: VideoInstance, videoFile: VideoFileInstance) => string
- export type GetVideoFilename = (this: VideoInstance, videoFile: VideoFileInstance) => string
- export type CreatePreview = (this: VideoInstance, videoFile: VideoFileInstance) => Promise<string>
- export type CreateThumbnail = (this: VideoInstance, videoFile: VideoFileInstance) => Promise<string>
- export type GetVideoFilePath = (this: VideoInstance, videoFile: VideoFileInstance) => string
- export type CreateTorrentAndSetInfoHash = (this: VideoInstance, videoFile: VideoFileInstance) => Promise<void>
-
- export type ToActivityPubObject = (this: VideoInstance) => VideoTorrentObject
-
- export type OptimizeOriginalVideofile = (this: VideoInstance) => Promise<void>
- export type TranscodeOriginalVideofile = (this: VideoInstance, resolution: number) => Promise<void>
- export type GetOriginalFileHeight = (this: VideoInstance) => Promise<number>
- export type GetEmbedPath = (this: VideoInstance) => string
- export type GetThumbnailPath = (this: VideoInstance) => string
- export type GetPreviewPath = (this: VideoInstance) => string
- export type GetDescriptionPath = (this: VideoInstance) => string
- export type GetTruncatedDescription = (this: VideoInstance) => string
- export type GetCategoryLabel = (this: VideoInstance) => string
- export type GetLicenceLabel = (this: VideoInstance) => string
- export type GetLanguageLabel = (this: VideoInstance) => string
-
- export type List = () => Bluebird<VideoInstance[]>
-
- export type ListAllAndSharedByAccountForOutbox = (
- accountId: number,
- start: number,
- count: number
- ) => Bluebird< ResultList<VideoInstance> >
- export type ListForApi = (start: number, count: number, sort: string) => Bluebird< ResultList<VideoInstance> >
- export type ListUserVideosForApi = (userId: number, start: number, count: number, sort: string) => Bluebird< ResultList<VideoInstance> >
- export type SearchAndPopulateAccountAndServerAndTags = (
- value: string,
- start: number,
- count: number,
- sort: string
- ) => Bluebird< ResultList<VideoInstance> >
-
- export type Load = (id: number) => Bluebird<VideoInstance>
- export type LoadByUUID = (uuid: string, t?: Sequelize.Transaction) => Bluebird<VideoInstance>
- export type LoadByUrlAndPopulateAccount = (url: string, t?: Sequelize.Transaction) => Bluebird<VideoInstance>
- export type LoadAndPopulateAccountAndServerAndTags = (id: number) => Bluebird<VideoInstance>
- export type LoadByUUIDAndPopulateAccountAndServerAndTags = (uuid: string) => Bluebird<VideoInstance>
- export type LoadByUUIDOrURL = (uuid: string, url: string, t?: Sequelize.Transaction) => Bluebird<VideoInstance>
-
- export type RemoveThumbnail = (this: VideoInstance) => Promise<void>
- export type RemovePreview = (this: VideoInstance) => Promise<void>
- export type RemoveFile = (this: VideoInstance, videoFile: VideoFileInstance) => Promise<void>
- export type RemoveTorrent = (this: VideoInstance, videoFile: VideoFileInstance) => Promise<void>
-}
-
-export interface VideoClass {
- list: VideoMethods.List
- listAllAndSharedByAccountForOutbox: VideoMethods.ListAllAndSharedByAccountForOutbox
- listForApi: VideoMethods.ListForApi
- listUserVideosForApi: VideoMethods.ListUserVideosForApi
- load: VideoMethods.Load
- loadAndPopulateAccountAndServerAndTags: VideoMethods.LoadAndPopulateAccountAndServerAndTags
- loadByUUID: VideoMethods.LoadByUUID
- loadByUrlAndPopulateAccount: VideoMethods.LoadByUrlAndPopulateAccount
- loadByUUIDOrURL: VideoMethods.LoadByUUIDOrURL
- loadByUUIDAndPopulateAccountAndServerAndTags: VideoMethods.LoadByUUIDAndPopulateAccountAndServerAndTags
- searchAndPopulateAccountAndServerAndTags: VideoMethods.SearchAndPopulateAccountAndServerAndTags
-}
-
-export interface VideoAttributes {
- id?: number
- uuid?: string
- name: string
- category: number
- licence: number
- language: number
- nsfw: boolean
- description: string
- duration: number
- privacy: number
- views?: number
- likes?: number
- dislikes?: number
- remote: boolean
- url?: string
-
- createdAt?: Date
- updatedAt?: Date
-
- parentId?: number
- channelId?: number
-
- VideoChannel?: VideoChannelInstance
- Tags?: TagInstance[]
- VideoFiles?: VideoFileInstance[]
- VideoShares?: VideoShareInstance[]
- AccountVideoRates?: AccountVideoRateInstance[]
-}
-
-export interface VideoInstance extends VideoClass, VideoAttributes, Sequelize.Instance<VideoAttributes> {
- createPreview: VideoMethods.CreatePreview
- createThumbnail: VideoMethods.CreateThumbnail
- createTorrentAndSetInfoHash: VideoMethods.CreateTorrentAndSetInfoHash
- getOriginalFile: VideoMethods.GetOriginalFile
- getPreviewName: VideoMethods.GetPreviewName
- getPreviewPath: VideoMethods.GetPreviewPath
- getThumbnailName: VideoMethods.GetThumbnailName
- getThumbnailPath: VideoMethods.GetThumbnailPath
- getTorrentFileName: VideoMethods.GetTorrentFileName
- getVideoFilename: VideoMethods.GetVideoFilename
- getVideoFilePath: VideoMethods.GetVideoFilePath
- isOwned: VideoMethods.IsOwned
- removeFile: VideoMethods.RemoveFile
- removePreview: VideoMethods.RemovePreview
- removeThumbnail: VideoMethods.RemoveThumbnail
- removeTorrent: VideoMethods.RemoveTorrent
- toActivityPubObject: VideoMethods.ToActivityPubObject
- toFormattedJSON: VideoMethods.ToFormattedJSON
- toFormattedDetailsJSON: VideoMethods.ToFormattedDetailsJSON
- optimizeOriginalVideofile: VideoMethods.OptimizeOriginalVideofile
- transcodeOriginalVideofile: VideoMethods.TranscodeOriginalVideofile
- getOriginalFileHeight: VideoMethods.GetOriginalFileHeight
- getEmbedPath: VideoMethods.GetEmbedPath
- getDescriptionPath: VideoMethods.GetDescriptionPath
- getTruncatedDescription: VideoMethods.GetTruncatedDescription
- getCategoryLabel: VideoMethods.GetCategoryLabel
- getLicenceLabel: VideoMethods.GetLicenceLabel
- getLanguageLabel: VideoMethods.GetLanguageLabel
-
- setTags: Sequelize.HasManySetAssociationsMixin<TagAttributes, string>
- addVideoFile: Sequelize.HasManyAddAssociationMixin<VideoFileAttributes, string>
- setVideoFiles: Sequelize.HasManySetAssociationsMixin<VideoFileAttributes, string>
-}
-
-export interface VideoModel extends VideoClass, Sequelize.Model<VideoInstance, VideoAttributes> {}
+++ /dev/null
-import * as Bluebird from 'bluebird'
-import * as Sequelize from 'sequelize'
-import { AccountInstance } from '../account/account-interface'
-import { VideoInstance } from './video-interface'
-
-export namespace VideoShareMethods {
- export type LoadAccountsByShare = (videoId: number, t: Sequelize.Transaction) => Bluebird<AccountInstance[]>
- export type Load = (accountId: number, videoId: number, t: Sequelize.Transaction) => Bluebird<VideoShareInstance>
-}
-
-export interface VideoShareClass {
- loadAccountsByShare: VideoShareMethods.LoadAccountsByShare
- load: VideoShareMethods.Load
-}
-
-export interface VideoShareAttributes {
- accountId: number
- videoId: number
-}
-
-export interface VideoShareInstance extends VideoShareClass, VideoShareAttributes, Sequelize.Instance<VideoShareAttributes> {
- id: number
- createdAt: Date
- updatedAt: Date
-
- Account?: AccountInstance
- Video?: VideoInstance
-}
-
-export interface VideoShareModel extends VideoShareClass, Sequelize.Model<VideoShareInstance, VideoShareAttributes> {}
import * as Sequelize from 'sequelize'
+import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
+import { AccountModel } from '../account/account'
+import { VideoModel } from './video'
-import { addMethodsToModel } from '../utils'
-import { VideoShareAttributes, VideoShareInstance, VideoShareMethods } from './video-share-interface'
-
-let VideoShare: Sequelize.Model<VideoShareInstance, VideoShareAttributes>
-let loadAccountsByShare: VideoShareMethods.LoadAccountsByShare
-let load: VideoShareMethods.Load
-
-export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
- VideoShare = sequelize.define<VideoShareInstance, VideoShareAttributes>('VideoShare',
- { },
+@Table({
+ tableName: 'videoShare',
+ indexes: [
{
- indexes: [
- {
- fields: [ 'accountId' ]
- },
- {
- fields: [ 'videoId' ]
- }
- ]
+ fields: [ 'accountId' ]
+ },
+ {
+ fields: [ 'videoId' ]
}
- )
-
- const classMethods = [
- associate,
- loadAccountsByShare,
- load
]
- addMethodsToModel(VideoShare, classMethods)
+})
+export class VideoShareModel extends Model<VideoShareModel> {
+ @CreatedAt
+ createdAt: Date
- return VideoShare
-}
+ @UpdatedAt
+ updatedAt: Date
-// ------------------------------ METHODS ------------------------------
+ @ForeignKey(() => AccountModel)
+ @Column
+ accountId: number
-function associate (models) {
- VideoShare.belongsTo(models.Account, {
+ @BelongsTo(() => AccountModel, {
foreignKey: {
- name: 'accountId',
allowNull: false
},
onDelete: 'cascade'
})
+ Account: AccountModel
- VideoShare.belongsTo(models.Video, {
+ @ForeignKey(() => VideoModel)
+ @Column
+ videoId: number
+
+ @BelongsTo(() => VideoModel, {
foreignKey: {
- name: 'videoId',
- allowNull: true
+ allowNull: false
},
onDelete: 'cascade'
})
-}
-
-load = function (accountId: number, videoId: number, t: Sequelize.Transaction) {
- return VideoShare.findOne({
- where: {
- accountId,
- videoId
- },
- include: [
- VideoShare['sequelize'].models.Account
- ],
- transaction: t
- })
-}
+ Video: VideoModel
-loadAccountsByShare = function (videoId: number, t: Sequelize.Transaction) {
- const query = {
- where: {
- videoId
- },
- include: [
- {
- model: VideoShare['sequelize'].models.Account,
- required: true
- }
- ],
- transaction: t
+ static load (accountId: number, videoId: number, t: Sequelize.Transaction) {
+ return VideoShareModel.findOne({
+ where: {
+ accountId,
+ videoId
+ },
+ include: [
+ AccountModel
+ ],
+ transaction: t
+ })
}
- return VideoShare.findAll(query)
- .then(res => res.map(r => r.Account))
+ static loadAccountsByShare (videoId: number, t: Sequelize.Transaction) {
+ const query = {
+ where: {
+ videoId
+ },
+ include: [
+ {
+ model: AccountModel,
+ required: true
+ }
+ ],
+ transaction: t
+ }
+
+ return VideoShareModel.findAll(query)
+ .then(res => res.map(r => r.Account))
+ }
}
+++ /dev/null
-import * as Sequelize from 'sequelize'
-
-export namespace VideoTagMethods {
-}
-
-export interface VideoTagClass {
-}
-
-export interface VideoTagAttributes {
-}
-
-export interface VideoTagInstance extends VideoTagClass, VideoTagAttributes, Sequelize.Instance<VideoTagAttributes> {
- id: number
- createdAt: Date
- updatedAt: Date
-}
-
-export interface VideoTagModel extends VideoTagClass, Sequelize.Model<VideoTagInstance, VideoTagAttributes> {}
-import * as Sequelize from 'sequelize'
+import { Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
+import { TagModel } from './tag'
+import { VideoModel } from './video'
-import {
- VideoTagInstance,
- VideoTagAttributes
-} from './video-tag-interface'
+@Table({
+ tableName: 'videoTag',
+ indexes: [
+ {
+ fields: [ 'videoId' ]
+ },
+ {
+ fields: [ 'tagId' ]
+ }
+ ]
+})
+export class VideoTagModel extends Model<VideoTagModel> {
+ @CreatedAt
+ createdAt: Date
-let VideoTag: Sequelize.Model<VideoTagInstance, VideoTagAttributes>
+ @UpdatedAt
+ updatedAt: Date
-export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
- VideoTag = sequelize.define<VideoTagInstance, VideoTagAttributes>('VideoTag', {}, {
- indexes: [
- {
- fields: [ 'videoId' ]
- },
- {
- fields: [ 'tagId' ]
- }
- ]
- })
+ @ForeignKey(() => VideoModel)
+ @Column
+ videoId: number
- return VideoTag
+ @ForeignKey(() => TagModel)
+ @Column
+ tagId: number
}
import * as parseTorrent from 'parse-torrent'
import { join } from 'path'
import * as Sequelize from 'sequelize'
+import {
+ AfterDestroy,
+ AllowNull,
+ BelongsTo,
+ BelongsToMany,
+ Column,
+ CreatedAt,
+ DataType,
+ Default,
+ ForeignKey,
+ HasMany,
+ IFindOptions,
+ Is,
+ IsInt,
+ IsUUID,
+ Min,
+ Model,
+ Table,
+ UpdatedAt
+} from 'sequelize-typescript'
+import { IIncludeOptions } from 'sequelize-typescript/lib/interfaces/IIncludeOptions'
import { VideoPrivacy, VideoResolution } from '../../../shared'
-import { VideoTorrentObject } from '../../../shared/models/activitypub/objects/video-torrent-object'
-import { activityPubCollection } from '../../helpers/activitypub'
-import { createTorrentPromise, renamePromise, statPromise, unlinkPromise, writeFilePromise } from '../../helpers/core-utils'
-import { isVideoCategoryValid, isVideoLanguageValid, isVideoPrivacyValid } from '../../helpers/custom-validators/videos'
-import { generateImageFromVideoFile, getVideoFileHeight, transcode } from '../../helpers/ffmpeg-utils'
+import { VideoTorrentObject } from '../../../shared/models/activitypub/objects'
+import {
+ activityPubCollection,
+ createTorrentPromise,
+ generateImageFromVideoFile,
+ getVideoFileHeight,
+ logger,
+ renamePromise,
+ statPromise,
+ transcode,
+ unlinkPromise,
+ writeFilePromise
+} from '../../helpers'
+import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub'
import {
- isActivityPubUrlValid,
+ isVideoCategoryValid,
isVideoDescriptionValid,
isVideoDurationValid,
+ isVideoLanguageValid,
isVideoLicenceValid,
isVideoNameValid,
- isVideoNSFWValid
-} from '../../helpers/index'
-import { logger } from '../../helpers/logger'
+ isVideoNSFWValid,
+ isVideoPrivacyValid
+} from '../../helpers/custom-validators/videos'
import {
API_VERSION,
CONFIG,
VIDEO_LANGUAGES,
VIDEO_LICENCES,
VIDEO_PRIVACIES
-} from '../../initializers/constants'
-import { getAnnounceActivityPubUrl } from '../../lib/activitypub/url'
+} from '../../initializers'
+import { getAnnounceActivityPubUrl } from '../../lib/activitypub'
import { sendDeleteVideo } from '../../lib/index'
-import { addMethodsToModel, getSort } from '../utils'
-import { TagInstance } from './tag-interface'
-import { VideoFileInstance, VideoFileModel } from './video-file-interface'
-import { VideoAttributes, VideoInstance, VideoMethods } from './video-interface'
-
-let Video: Sequelize.Model<VideoInstance, VideoAttributes>
-let getOriginalFile: VideoMethods.GetOriginalFile
-let getVideoFilename: VideoMethods.GetVideoFilename
-let getThumbnailName: VideoMethods.GetThumbnailName
-let getThumbnailPath: VideoMethods.GetThumbnailPath
-let getPreviewName: VideoMethods.GetPreviewName
-let getPreviewPath: VideoMethods.GetPreviewPath
-let getTorrentFileName: VideoMethods.GetTorrentFileName
-let isOwned: VideoMethods.IsOwned
-let toFormattedJSON: VideoMethods.ToFormattedJSON
-let toFormattedDetailsJSON: VideoMethods.ToFormattedDetailsJSON
-let toActivityPubObject: VideoMethods.ToActivityPubObject
-let optimizeOriginalVideofile: VideoMethods.OptimizeOriginalVideofile
-let transcodeOriginalVideofile: VideoMethods.TranscodeOriginalVideofile
-let createPreview: VideoMethods.CreatePreview
-let createThumbnail: VideoMethods.CreateThumbnail
-let getVideoFilePath: VideoMethods.GetVideoFilePath
-let createTorrentAndSetInfoHash: VideoMethods.CreateTorrentAndSetInfoHash
-let getOriginalFileHeight: VideoMethods.GetOriginalFileHeight
-let getEmbedPath: VideoMethods.GetEmbedPath
-let getDescriptionPath: VideoMethods.GetDescriptionPath
-let getTruncatedDescription: VideoMethods.GetTruncatedDescription
-let getCategoryLabel: VideoMethods.GetCategoryLabel
-let getLicenceLabel: VideoMethods.GetLicenceLabel
-let getLanguageLabel: VideoMethods.GetLanguageLabel
-
-let list: VideoMethods.List
-let listForApi: VideoMethods.ListForApi
-let listAllAndSharedByAccountForOutbox: VideoMethods.ListAllAndSharedByAccountForOutbox
-let listUserVideosForApi: VideoMethods.ListUserVideosForApi
-let load: VideoMethods.Load
-let loadByUrlAndPopulateAccount: VideoMethods.LoadByUrlAndPopulateAccount
-let loadByUUID: VideoMethods.LoadByUUID
-let loadByUUIDOrURL: VideoMethods.LoadByUUIDOrURL
-let loadAndPopulateAccountAndServerAndTags: VideoMethods.LoadAndPopulateAccountAndServerAndTags
-let loadByUUIDAndPopulateAccountAndServerAndTags: VideoMethods.LoadByUUIDAndPopulateAccountAndServerAndTags
-let searchAndPopulateAccountAndServerAndTags: VideoMethods.SearchAndPopulateAccountAndServerAndTags
-let removeThumbnail: VideoMethods.RemoveThumbnail
-let removePreview: VideoMethods.RemovePreview
-let removeFile: VideoMethods.RemoveFile
-let removeTorrent: VideoMethods.RemoveTorrent
-
-export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
- Video = sequelize.define<VideoInstance, VideoAttributes>('Video',
+import { AccountModel } from '../account/account'
+import { AccountVideoRateModel } from '../account/account-video-rate'
+import { ServerModel } from '../server/server'
+import { getSort, throwIfNotValid } from '../utils'
+import { TagModel } from './tag'
+import { VideoAbuseModel } from './video-abuse'
+import { VideoChannelModel } from './video-channel'
+import { VideoFileModel } from './video-file'
+import { VideoShareModel } from './video-share'
+import { VideoTagModel } from './video-tag'
+
+@Table({
+ tableName: 'video',
+ indexes: [
{
- uuid: {
- type: DataTypes.UUID,
- defaultValue: DataTypes.UUIDV4,
- allowNull: false,
- validate: {
- isUUID: 4
- }
- },
- name: {
- type: DataTypes.STRING,
- allowNull: false,
- validate: {
- nameValid: value => {
- const res = isVideoNameValid(value)
- if (res === false) throw new Error('Video name is not valid.')
- }
- }
- },
- category: {
- type: DataTypes.INTEGER,
- allowNull: true,
- defaultValue: null,
- validate: {
- categoryValid: value => {
- const res = isVideoCategoryValid(value)
- if (res === false) throw new Error('Video category is not valid.')
- }
- }
- },
- licence: {
- type: DataTypes.INTEGER,
- allowNull: true,
- defaultValue: null,
- validate: {
- licenceValid: value => {
- const res = isVideoLicenceValid(value)
- if (res === false) throw new Error('Video licence is not valid.')
- }
- }
- },
- language: {
- type: DataTypes.INTEGER,
- allowNull: true,
- defaultValue: null,
- validate: {
- languageValid: value => {
- const res = isVideoLanguageValid(value)
- if (res === false) throw new Error('Video language is not valid.')
- }
- }
- },
- privacy: {
- type: DataTypes.INTEGER,
- allowNull: false,
- validate: {
- privacyValid: value => {
- const res = isVideoPrivacyValid(value)
- if (res === false) throw new Error('Video privacy is not valid.')
- }
- }
- },
- nsfw: {
- type: DataTypes.BOOLEAN,
- allowNull: false,
- validate: {
- nsfwValid: value => {
- const res = isVideoNSFWValid(value)
- if (res === false) throw new Error('Video nsfw attribute is not valid.')
- }
- }
- },
- description: {
- type: DataTypes.STRING(CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max),
- allowNull: true,
- defaultValue: null,
- validate: {
- descriptionValid: value => {
- const res = isVideoDescriptionValid(value)
- if (res === false) throw new Error('Video description is not valid.')
- }
- }
- },
- duration: {
- type: DataTypes.INTEGER,
- allowNull: false,
- validate: {
- durationValid: value => {
- const res = isVideoDurationValid(value)
- if (res === false) throw new Error('Video duration is not valid.')
- }
- }
- },
- views: {
- type: DataTypes.INTEGER,
- allowNull: false,
- defaultValue: 0,
- validate: {
- min: 0,
- isInt: true
- }
- },
- likes: {
- type: DataTypes.INTEGER,
- allowNull: false,
- defaultValue: 0,
- validate: {
- min: 0,
- isInt: true
- }
- },
- dislikes: {
- type: DataTypes.INTEGER,
- allowNull: false,
- defaultValue: 0,
- validate: {
- min: 0,
- isInt: true
- }
- },
- remote: {
- type: DataTypes.BOOLEAN,
- allowNull: false,
- defaultValue: false
- },
- url: {
- type: DataTypes.STRING(CONSTRAINTS_FIELDS.VIDEOS.URL.max),
- allowNull: false,
- validate: {
- urlValid: value => {
- const res = isActivityPubUrlValid(value)
- if (res === false) throw new Error('Video URL is not valid.')
- }
- }
- }
+ fields: [ 'name' ]
},
{
- indexes: [
- {
- fields: [ 'name' ]
- },
- {
- fields: [ 'createdAt' ]
- },
- {
- fields: [ 'duration' ]
- },
- {
- fields: [ 'views' ]
- },
- {
- fields: [ 'likes' ]
- },
- {
- fields: [ 'uuid' ]
- },
- {
- fields: [ 'channelId' ]
- }
- ],
- hooks: {
- afterDestroy
- }
+ fields: [ 'createdAt' ]
+ },
+ {
+ fields: [ 'duration' ]
+ },
+ {
+ fields: [ 'views' ]
+ },
+ {
+ fields: [ 'likes' ]
+ },
+ {
+ fields: [ 'uuid' ]
+ },
+ {
+ fields: [ 'channelId' ]
}
- )
-
- const classMethods = [
- associate,
-
- list,
- listAllAndSharedByAccountForOutbox,
- listForApi,
- listUserVideosForApi,
- load,
- loadByUrlAndPopulateAccount,
- loadAndPopulateAccountAndServerAndTags,
- loadByUUIDOrURL,
- loadByUUID,
- loadByUUIDAndPopulateAccountAndServerAndTags,
- searchAndPopulateAccountAndServerAndTags
- ]
- const instanceMethods = [
- createPreview,
- createThumbnail,
- createTorrentAndSetInfoHash,
- getPreviewName,
- getPreviewPath,
- getThumbnailName,
- getThumbnailPath,
- getTorrentFileName,
- getVideoFilename,
- getVideoFilePath,
- getOriginalFile,
- isOwned,
- removeFile,
- removePreview,
- removeThumbnail,
- removeTorrent,
- toActivityPubObject,
- toFormattedJSON,
- toFormattedDetailsJSON,
- optimizeOriginalVideofile,
- transcodeOriginalVideofile,
- getOriginalFileHeight,
- getEmbedPath,
- getTruncatedDescription,
- getDescriptionPath,
- getCategoryLabel,
- getLicenceLabel,
- getLanguageLabel
]
- addMethodsToModel(Video, classMethods, instanceMethods)
-
- return Video
-}
-
-// ------------------------------ METHODS ------------------------------
-
-function associate (models) {
- Video.belongsTo(models.VideoChannel, {
+})
+export class VideoModel extends Model<VideoModel> {
+
+ @AllowNull(false)
+ @Default(DataType.UUIDV4)
+ @IsUUID(4)
+ @Column(DataType.UUID)
+ uuid: string
+
+ @AllowNull(false)
+ @Is('VideoName', value => throwIfNotValid(value, isVideoNameValid, 'name'))
+ @Column
+ name: string
+
+ @AllowNull(true)
+ @Default(null)
+ @Is('VideoCategory', value => throwIfNotValid(value, isVideoCategoryValid, 'category'))
+ @Column
+ category: number
+
+ @AllowNull(true)
+ @Default(null)
+ @Is('VideoLicence', value => throwIfNotValid(value, isVideoLicenceValid, 'licence'))
+ @Column
+ licence: number
+
+ @AllowNull(true)
+ @Default(null)
+ @Is('VideoLanguage', value => throwIfNotValid(value, isVideoLanguageValid, 'language'))
+ @Column
+ language: number
+
+ @AllowNull(false)
+ @Is('VideoPrivacy', value => throwIfNotValid(value, isVideoPrivacyValid, 'privacy'))
+ @Column
+ privacy: number
+
+ @AllowNull(false)
+ @Is('VideoNSFW', value => throwIfNotValid(value, isVideoNSFWValid, 'NSFW boolean'))
+ @Column
+ nsfw: boolean
+
+ @AllowNull(true)
+ @Default(null)
+ @Is('VideoDescription', value => throwIfNotValid(value, isVideoDescriptionValid, 'description'))
+ @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max))
+ description: string
+
+ @AllowNull(false)
+ @Is('VideoDuration', value => throwIfNotValid(value, isVideoDurationValid, 'duration'))
+ @Column
+ duration: number
+
+ @AllowNull(false)
+ @Default(0)
+ @IsInt
+ @Min(0)
+ @Column
+ views: number
+
+ @AllowNull(false)
+ @Default(0)
+ @IsInt
+ @Min(0)
+ @Column
+ likes: number
+
+ @AllowNull(false)
+ @Default(0)
+ @IsInt
+ @Min(0)
+ @Column
+ dislikes: number
+
+ @AllowNull(false)
+ @Column
+ remote: boolean
+
+ @AllowNull(false)
+ @Is('VideoUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url'))
+ @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.URL.max))
+ url: string
+
+ @CreatedAt
+ createdAt: Date
+
+ @UpdatedAt
+ updatedAt: Date
+
+ @ForeignKey(() => VideoChannelModel)
+ @Column
+ channelId: number
+
+ @BelongsTo(() => VideoChannelModel, {
foreignKey: {
- name: 'channelId',
allowNull: false
},
onDelete: 'cascade'
})
+ VideoChannel: VideoChannelModel
- Video.belongsToMany(models.Tag, {
+ @BelongsToMany(() => TagModel, {
foreignKey: 'videoId',
- through: models.VideoTag,
- onDelete: 'cascade'
+ through: () => VideoTagModel,
+ onDelete: 'CASCADE'
})
+ Tags: TagModel[]
- Video.hasMany(models.VideoAbuse, {
+ @HasMany(() => VideoAbuseModel, {
foreignKey: {
name: 'videoId',
allowNull: false
},
onDelete: 'cascade'
})
+ VideoAbuses: VideoAbuseModel[]
- Video.hasMany(models.VideoFile, {
+ @HasMany(() => VideoFileModel, {
foreignKey: {
name: 'videoId',
allowNull: false
},
onDelete: 'cascade'
})
+ VideoFiles: VideoFileModel[]
- Video.hasMany(models.VideoShare, {
+ @HasMany(() => VideoShareModel, {
foreignKey: {
name: 'videoId',
allowNull: false
},
onDelete: 'cascade'
})
+ VideoShares: VideoShareModel[]
- Video.hasMany(models.AccountVideoRate, {
+ @HasMany(() => AccountVideoRateModel, {
foreignKey: {
name: 'videoId',
allowNull: false
},
onDelete: 'cascade'
})
-}
-
-function afterDestroy (video: VideoInstance) {
- const tasks = []
+ AccountVideoRates: AccountVideoRateModel[]
- tasks.push(
- video.removeThumbnail()
- )
+ @AfterDestroy
+ static removeFilesAndSendDelete (instance: VideoModel) {
+ const tasks = []
- if (video.isOwned()) {
tasks.push(
- video.removePreview(),
- sendDeleteVideo(video, undefined)
+ instance.removeThumbnail()
)
- // Remove physical files and torrents
- video.VideoFiles.forEach(file => {
- tasks.push(video.removeFile(file))
- tasks.push(video.removeTorrent(file))
- })
- }
-
- return Promise.all(tasks)
- .catch(err => {
- logger.error('Some errors when removing files of video %s in after destroy hook.', video.uuid, err)
- })
-}
-
-getOriginalFile = function (this: VideoInstance) {
- if (Array.isArray(this.VideoFiles) === false) return undefined
+ if (instance.isOwned()) {
+ tasks.push(
+ instance.removePreview(),
+ sendDeleteVideo(instance, undefined)
+ )
- // The original file is the file that have the higher resolution
- return maxBy(this.VideoFiles, file => file.resolution)
-}
+ // Remove physical files and torrents
+ instance.VideoFiles.forEach(file => {
+ tasks.push(instance.removeFile(file))
+ tasks.push(instance.removeTorrent(file))
+ })
+ }
-getVideoFilename = function (this: VideoInstance, videoFile: VideoFileInstance) {
- return this.uuid + '-' + videoFile.resolution + videoFile.extname
-}
+ return Promise.all(tasks)
+ .catch(err => {
+ logger.error('Some errors when removing files of video %s in after destroy hook.', instance.uuid, err)
+ })
+ }
-getThumbnailName = function (this: VideoInstance) {
- // We always have a copy of the thumbnail
- const extension = '.jpg'
- return this.uuid + extension
-}
+ static list () {
+ const query = {
+ include: [ VideoFileModel ]
+ }
-getPreviewName = function (this: VideoInstance) {
- const extension = '.jpg'
- return this.uuid + extension
-}
+ return VideoModel.findAll(query)
+ }
-getTorrentFileName = function (this: VideoInstance, videoFile: VideoFileInstance) {
- const extension = '.torrent'
- return this.uuid + '-' + videoFile.resolution + extension
-}
+ static listAllAndSharedByAccountForOutbox (accountId: number, start: number, count: number) {
+ function getRawQuery (select: string) {
+ const queryVideo = 'SELECT ' + select + ' FROM "video" AS "Video" ' +
+ 'INNER JOIN "videoChannel" AS "VideoChannel" ON "VideoChannel"."id" = "Video"."channelId" ' +
+ 'WHERE "VideoChannel"."accountId" = ' + accountId
+ const queryVideoShare = 'SELECT ' + select + ' FROM "videoShare" AS "VideoShare" ' +
+ 'INNER JOIN "video" AS "Video" ON "Video"."id" = "VideoShare"."videoId" ' +
+ 'WHERE "VideoShare"."accountId" = ' + accountId
-isOwned = function (this: VideoInstance) {
- return this.remote === false
-}
+ return `(${queryVideo}) UNION (${queryVideoShare})`
+ }
-createPreview = function (this: VideoInstance, videoFile: VideoFileInstance) {
- const imageSize = PREVIEWS_SIZE.width + 'x' + PREVIEWS_SIZE.height
+ const rawQuery = getRawQuery('"Video"."id"')
+ const rawCountQuery = getRawQuery('COUNT("Video"."id") as "total"')
+
+ const query = {
+ distinct: true,
+ offset: start,
+ limit: count,
+ order: [ getSort('createdAt'), [ 'Tags', 'name', 'ASC' ] ],
+ where: {
+ id: {
+ [Sequelize.Op.in]: Sequelize.literal('(' + rawQuery + ')')
+ }
+ },
+ include: [
+ {
+ model: VideoShareModel,
+ required: false,
+ where: {
+ [Sequelize.Op.and]: [
+ {
+ id: {
+ [Sequelize.Op.not]: null
+ }
+ },
+ {
+ accountId
+ }
+ ]
+ },
+ include: [ AccountModel ]
+ },
+ {
+ model: VideoChannelModel,
+ required: true,
+ include: [
+ {
+ model: AccountModel,
+ required: true
+ }
+ ]
+ },
+ {
+ model: AccountVideoRateModel,
+ include: [ AccountModel ]
+ },
+ VideoFileModel,
+ TagModel
+ ]
+ }
- return generateImageFromVideoFile(
- this.getVideoFilePath(videoFile),
- CONFIG.STORAGE.PREVIEWS_DIR,
- this.getPreviewName(),
- imageSize
- )
-}
+ return Bluebird.all([
+ // FIXME: typing issue
+ VideoModel.findAll(query as any),
+ VideoModel.sequelize.query(rawCountQuery, { type: Sequelize.QueryTypes.SELECT })
+ ]).then(([ rows, totals ]) => {
+ // totals: totalVideos + totalVideoShares
+ let totalVideos = 0
+ let totalVideoShares = 0
+ if (totals[0]) totalVideos = parseInt(totals[0].total, 10)
+ if (totals[1]) totalVideoShares = parseInt(totals[1].total, 10)
+
+ const total = totalVideos + totalVideoShares
+ return {
+ data: rows,
+ total: total
+ }
+ })
+ }
-createThumbnail = function (this: VideoInstance, videoFile: VideoFileInstance) {
- const imageSize = THUMBNAILS_SIZE.width + 'x' + THUMBNAILS_SIZE.height
+ static listUserVideosForApi (userId: number, start: number, count: number, sort: string) {
+ const query = {
+ distinct: true,
+ offset: start,
+ limit: count,
+ order: [ getSort(sort), [ 'Tags', 'name', 'ASC' ] ],
+ include: [
+ {
+ model: VideoChannelModel,
+ required: true,
+ include: [
+ {
+ model: AccountModel,
+ where: {
+ userId
+ },
+ required: true
+ }
+ ]
+ },
+ TagModel
+ ]
+ }
- return generateImageFromVideoFile(
- this.getVideoFilePath(videoFile),
- CONFIG.STORAGE.THUMBNAILS_DIR,
- this.getThumbnailName(),
- imageSize
- )
-}
+ return VideoModel.findAndCountAll(query).then(({ rows, count }) => {
+ return {
+ data: rows,
+ total: count
+ }
+ })
+ }
-getVideoFilePath = function (this: VideoInstance, videoFile: VideoFileInstance) {
- return join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile))
-}
+ static listForApi (start: number, count: number, sort: string) {
+ const query = {
+ distinct: true,
+ offset: start,
+ limit: count,
+ order: [ getSort(sort), [ 'Tags', 'name', 'ASC' ] ],
+ include: [
+ {
+ model: VideoChannelModel,
+ required: true,
+ include: [
+ {
+ model: AccountModel,
+ required: true,
+ include: [
+ {
+ model: ServerModel,
+ required: false
+ }
+ ]
+ }
+ ]
+ },
+ TagModel
+ ],
+ where: this.createBaseVideosWhere()
+ }
-createTorrentAndSetInfoHash = async function (this: VideoInstance, videoFile: VideoFileInstance) {
- const options = {
- announceList: [
- [ CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT + '/tracker/socket' ]
- ],
- urlList: [
- CONFIG.WEBSERVER.URL + STATIC_PATHS.WEBSEED + this.getVideoFilename(videoFile)
- ]
+ return VideoModel.findAndCountAll(query).then(({ rows, count }) => {
+ return {
+ data: rows,
+ total: count
+ }
+ })
}
- const torrent = await createTorrentPromise(this.getVideoFilePath(videoFile), options)
+ static load (id: number) {
+ return VideoModel.findById(id)
+ }
- const filePath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile))
- logger.info('Creating torrent %s.', filePath)
+ static loadByUUID (uuid: string, t?: Sequelize.Transaction) {
+ const query: IFindOptions<VideoModel> = {
+ where: {
+ uuid
+ },
+ include: [ VideoFileModel ]
+ }
- await writeFilePromise(filePath, torrent)
+ if (t !== undefined) query.transaction = t
- const parsedTorrent = parseTorrent(torrent)
- videoFile.infoHash = parsedTorrent.infoHash
-}
+ return VideoModel.findOne(query)
+ }
-getEmbedPath = function (this: VideoInstance) {
- return '/videos/embed/' + this.uuid
-}
+ static loadByUrlAndPopulateAccount (url: string, t?: Sequelize.Transaction) {
+ const query: IFindOptions<VideoModel> = {
+ where: {
+ url
+ },
+ include: [
+ VideoFileModel,
+ {
+ model: VideoChannelModel,
+ include: [ AccountModel ]
+ }
+ ]
+ }
-getThumbnailPath = function (this: VideoInstance) {
- return join(STATIC_PATHS.THUMBNAILS, this.getThumbnailName())
-}
+ if (t !== undefined) query.transaction = t
-getPreviewPath = function (this: VideoInstance) {
- return join(STATIC_PATHS.PREVIEWS, this.getPreviewName())
-}
+ return VideoModel.findOne(query)
+ }
-toFormattedJSON = function (this: VideoInstance) {
- let serverHost
+ static loadByUUIDOrURL (uuid: string, url: string, t?: Sequelize.Transaction) {
+ const query: IFindOptions<VideoModel> = {
+ where: {
+ [Sequelize.Op.or]: [
+ { uuid },
+ { url }
+ ]
+ },
+ include: [ VideoFileModel ]
+ }
- if (this.VideoChannel.Account.Server) {
- serverHost = this.VideoChannel.Account.Server.host
- } else {
- // It means it's our video
- serverHost = CONFIG.WEBSERVER.HOST
- }
+ if (t !== undefined) query.transaction = t
- const json = {
- id: this.id,
- uuid: this.uuid,
- name: this.name,
- category: this.category,
- categoryLabel: this.getCategoryLabel(),
- licence: this.licence,
- licenceLabel: this.getLicenceLabel(),
- language: this.language,
- languageLabel: this.getLanguageLabel(),
- nsfw: this.nsfw,
- description: this.getTruncatedDescription(),
- serverHost,
- isLocal: this.isOwned(),
- accountName: this.VideoChannel.Account.name,
- duration: this.duration,
- views: this.views,
- likes: this.likes,
- dislikes: this.dislikes,
- tags: map<TagInstance, string>(this.Tags, 'name'),
- thumbnailPath: this.getThumbnailPath(),
- previewPath: this.getPreviewPath(),
- embedPath: this.getEmbedPath(),
- createdAt: this.createdAt,
- updatedAt: this.updatedAt
+ return VideoModel.findOne(query)
}
- return json
-}
+ static loadAndPopulateAccountAndServerAndTags (id: number) {
+ const options = {
+ order: [ [ 'Tags', 'name', 'ASC' ] ],
+ include: [
+ {
+ model: VideoChannelModel,
+ include: [
+ {
+ model: AccountModel,
+ include: [ { model: ServerModel, required: false } ]
+ }
+ ]
+ },
+ {
+ model: AccountVideoRateModel,
+ include: [ AccountModel ]
+ },
+ {
+ model: VideoShareModel,
+ include: [ AccountModel ]
+ },
+ TagModel,
+ VideoFileModel
+ ]
+ }
-toFormattedDetailsJSON = function (this: VideoInstance) {
- const formattedJson = this.toFormattedJSON()
+ return VideoModel.findById(id, options)
+ }
- // Maybe our server is not up to date and there are new privacy settings since our version
- let privacyLabel = VIDEO_PRIVACIES[this.privacy]
- if (!privacyLabel) privacyLabel = 'Unknown'
+ static loadByUUIDAndPopulateAccountAndServerAndTags (uuid: string) {
+ const options = {
+ order: [ [ 'Tags', 'name', 'ASC' ] ],
+ where: {
+ uuid
+ },
+ include: [
+ {
+ model: VideoChannelModel,
+ include: [
+ {
+ model: AccountModel,
+ include: [ { model: ServerModel, required: false } ]
+ }
+ ]
+ },
+ {
+ model: AccountVideoRateModel,
+ include: [ AccountModel ]
+ },
+ {
+ model: VideoShareModel,
+ include: [ AccountModel ]
+ },
+ TagModel,
+ VideoFileModel
+ ]
+ }
- const detailsJson = {
- privacyLabel,
- privacy: this.privacy,
- descriptionPath: this.getDescriptionPath(),
- channel: this.VideoChannel.toFormattedJSON(),
- account: this.VideoChannel.Account.toFormattedJSON(),
- files: []
+ return VideoModel.findOne(options)
}
- // Format and sort video files
- const { baseUrlHttp, baseUrlWs } = getBaseUrls(this)
- detailsJson.files = this.VideoFiles
- .map(videoFile => {
- let resolutionLabel = videoFile.resolution + 'p'
-
- const videoFileJson = {
- resolution: videoFile.resolution,
- resolutionLabel,
- magnetUri: generateMagnetUri(this, videoFile, baseUrlHttp, baseUrlWs),
- size: videoFile.size,
- torrentUrl: getTorrentUrl(this, videoFile, baseUrlHttp),
- fileUrl: getVideoFileUrl(this, videoFile, baseUrlHttp)
- }
-
- return videoFileJson
- })
- .sort((a, b) => {
- if (a.resolution < b.resolution) return 1
- if (a.resolution === b.resolution) return 0
- return -1
- })
-
- return Object.assign(formattedJson, detailsJson)
-}
-
-toActivityPubObject = function (this: VideoInstance) {
- const { baseUrlHttp, baseUrlWs } = getBaseUrls(this)
- if (!this.Tags) this.Tags = []
+ static searchAndPopulateAccountAndServerAndTags (value: string, start: number, count: number, sort: string) {
+ const serverInclude: IIncludeOptions = {
+ model: ServerModel,
+ required: false
+ }
- const tag = this.Tags.map(t => ({
- type: 'Hashtag' as 'Hashtag',
- name: t.name
- }))
+ const accountInclude: IIncludeOptions = {
+ model: AccountModel,
+ include: [ serverInclude ]
+ }
- let language
- if (this.language) {
- language = {
- identifier: this.language + '',
- name: this.getLanguageLabel()
+ const videoChannelInclude: IIncludeOptions = {
+ model: VideoChannelModel,
+ include: [ accountInclude ],
+ required: true
}
- }
- let category
- if (this.category) {
- category = {
- identifier: this.category + '',
- name: this.getCategoryLabel()
+ const tagInclude: IIncludeOptions = {
+ model: TagModel
}
- }
- let licence
- if (this.licence) {
- licence = {
- identifier: this.licence + '',
- name: this.getLicenceLabel()
+ const query: IFindOptions<VideoModel> = {
+ distinct: true,
+ where: this.createBaseVideosWhere(),
+ offset: start,
+ limit: count,
+ order: [ getSort(sort), [ 'Tags', 'name', 'ASC' ] ]
}
- }
- let likesObject
- let dislikesObject
+ // TODO: search on tags too
+ // const escapedValue = Video['sequelize'].escape('%' + value + '%')
+ // query.where['id'][Sequelize.Op.in] = Video['sequelize'].literal(
+ // `(SELECT "VideoTags"."videoId"
+ // FROM "Tags"
+ // INNER JOIN "VideoTags" ON "Tags"."id" = "VideoTags"."tagId"
+ // WHERE name ILIKE ${escapedValue}
+ // )`
+ // )
+
+ // TODO: search on account too
+ // accountInclude.where = {
+ // name: {
+ // [Sequelize.Op.iLike]: '%' + value + '%'
+ // }
+ // }
+ query.where['name'] = {
+ [Sequelize.Op.iLike]: '%' + value + '%'
+ }
- if (Array.isArray(this.AccountVideoRates)) {
- const likes: string[] = []
- const dislikes: string[] = []
+ query.include = [
+ videoChannelInclude, tagInclude
+ ]
- for (const rate of this.AccountVideoRates) {
- if (rate.type === 'like') {
- likes.push(rate.Account.url)
- } else if (rate.type === 'dislike') {
- dislikes.push(rate.Account.url)
+ return VideoModel.findAndCountAll(query).then(({ rows, count }) => {
+ return {
+ data: rows,
+ total: count
}
- }
-
- likesObject = activityPubCollection(likes)
- dislikesObject = activityPubCollection(dislikes)
+ })
}
- let sharesObject
- if (Array.isArray(this.VideoShares)) {
- const shares: string[] = []
-
- for (const videoShare of this.VideoShares) {
- const shareUrl = getAnnounceActivityPubUrl(this.url, videoShare.Account)
- shares.push(shareUrl)
+ private static createBaseVideosWhere () {
+ return {
+ id: {
+ [Sequelize.Op.notIn]: VideoModel.sequelize.literal(
+ '(SELECT "videoBlacklist"."videoId" FROM "videoBlacklist")'
+ )
+ },
+ privacy: VideoPrivacy.PUBLIC
}
-
- sharesObject = activityPubCollection(shares)
}
- const url = []
- for (const file of this.VideoFiles) {
- url.push({
- type: 'Link',
- mimeType: 'video/' + file.extname.replace('.', ''),
- url: getVideoFileUrl(this, file, baseUrlHttp),
- width: file.resolution,
- size: file.size
- })
+ getOriginalFile () {
+ if (Array.isArray(this.VideoFiles) === false) return undefined
- url.push({
- type: 'Link',
- mimeType: 'application/x-bittorrent',
- url: getTorrentUrl(this, file, baseUrlHttp),
- width: file.resolution
- })
-
- url.push({
- type: 'Link',
- mimeType: 'application/x-bittorrent;x-scheme-handler/magnet',
- url: generateMagnetUri(this, file, baseUrlHttp, baseUrlWs),
- width: file.resolution
- })
+ // The original file is the file that have the higher resolution
+ return maxBy(this.VideoFiles, file => file.resolution)
}
- // Add video url too
- url.push({
- type: 'Link',
- mimeType: 'text/html',
- url: CONFIG.WEBSERVER.URL + '/videos/watch/' + this.uuid
- })
+ getVideoFilename (videoFile: VideoFileModel) {
+ return this.uuid + '-' + videoFile.resolution + videoFile.extname
+ }
- const videoObject: VideoTorrentObject = {
- type: 'Video' as 'Video',
- id: this.url,
- name: this.name,
- // https://www.w3.org/TR/activitystreams-vocabulary/#dfn-duration
- duration: 'PT' + this.duration + 'S',
- uuid: this.uuid,
- tag,
- category,
- licence,
- language,
- views: this.views,
- nsfw: this.nsfw,
- published: this.createdAt.toISOString(),
- updated: this.updatedAt.toISOString(),
- mediaType: 'text/markdown',
- content: this.getTruncatedDescription(),
- icon: {
- type: 'Image',
- url: getThumbnailUrl(this, baseUrlHttp),
- mediaType: 'image/jpeg',
- width: THUMBNAILS_SIZE.width,
- height: THUMBNAILS_SIZE.height
- },
- url,
- likes: likesObject,
- dislikes: dislikesObject,
- shares: sharesObject
+ getThumbnailName () {
+ // We always have a copy of the thumbnail
+ const extension = '.jpg'
+ return this.uuid + extension
}
- return videoObject
-}
+ getPreviewName () {
+ const extension = '.jpg'
+ return this.uuid + extension
+ }
-getTruncatedDescription = function (this: VideoInstance) {
- if (!this.description) return null
+ getTorrentFileName (videoFile: VideoFileModel) {
+ const extension = '.torrent'
+ return this.uuid + '-' + videoFile.resolution + extension
+ }
- const options = {
- length: CONSTRAINTS_FIELDS.VIDEOS.TRUNCATED_DESCRIPTION.max
+ isOwned () {
+ return this.remote === false
}
- return truncate(this.description, options)
-}
+ createPreview (videoFile: VideoFileModel) {
+ const imageSize = PREVIEWS_SIZE.width + 'x' + PREVIEWS_SIZE.height
+
+ return generateImageFromVideoFile(
+ this.getVideoFilePath(videoFile),
+ CONFIG.STORAGE.PREVIEWS_DIR,
+ this.getPreviewName(),
+ imageSize
+ )
+ }
-optimizeOriginalVideofile = async function (this: VideoInstance) {
- const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
- const newExtname = '.mp4'
- const inputVideoFile = this.getOriginalFile()
- const videoInputPath = join(videosDirectory, this.getVideoFilename(inputVideoFile))
- const videoOutputPath = join(videosDirectory, this.id + '-transcoded' + newExtname)
+ createThumbnail (videoFile: VideoFileModel) {
+ const imageSize = THUMBNAILS_SIZE.width + 'x' + THUMBNAILS_SIZE.height
- const transcodeOptions = {
- inputPath: videoInputPath,
- outputPath: videoOutputPath
+ return generateImageFromVideoFile(
+ this.getVideoFilePath(videoFile),
+ CONFIG.STORAGE.THUMBNAILS_DIR,
+ this.getThumbnailName(),
+ imageSize
+ )
}
- try {
- // Could be very long!
- await transcode(transcodeOptions)
+ getVideoFilePath (videoFile: VideoFileModel) {
+ return join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile))
+ }
- await unlinkPromise(videoInputPath)
+ createTorrentAndSetInfoHash = async function (videoFile: VideoFileModel) {
+ const options = {
+ announceList: [
+ [ CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT + '/tracker/socket' ]
+ ],
+ urlList: [
+ CONFIG.WEBSERVER.URL + STATIC_PATHS.WEBSEED + this.getVideoFilename(videoFile)
+ ]
+ }
- // Important to do this before getVideoFilename() to take in account the new file extension
- inputVideoFile.set('extname', newExtname)
+ const torrent = await createTorrentPromise(this.getVideoFilePath(videoFile), options)
- await renamePromise(videoOutputPath, this.getVideoFilePath(inputVideoFile))
- const stats = await statPromise(this.getVideoFilePath(inputVideoFile))
+ const filePath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile))
+ logger.info('Creating torrent %s.', filePath)
- inputVideoFile.set('size', stats.size)
+ await writeFilePromise(filePath, torrent)
- await this.createTorrentAndSetInfoHash(inputVideoFile)
- await inputVideoFile.save()
+ const parsedTorrent = parseTorrent(torrent)
+ videoFile.infoHash = parsedTorrent.infoHash
+ }
- } catch (err) {
- // Auto destruction...
- this.destroy().catch(err => logger.error('Cannot destruct video after transcoding failure.', err))
+ getEmbedPath () {
+ return '/videos/embed/' + this.uuid
+ }
- throw err
+ getThumbnailPath () {
+ return join(STATIC_PATHS.THUMBNAILS, this.getThumbnailName())
}
-}
-transcodeOriginalVideofile = async function (this: VideoInstance, resolution: VideoResolution) {
- const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
- const extname = '.mp4'
+ getPreviewPath () {
+ return join(STATIC_PATHS.PREVIEWS, this.getPreviewName())
+ }
- // We are sure it's x264 in mp4 because optimizeOriginalVideofile was already executed
- const videoInputPath = join(videosDirectory, this.getVideoFilename(this.getOriginalFile()))
+ toFormattedJSON () {
+ let serverHost
- const newVideoFile = (Video['sequelize'].models.VideoFile as VideoFileModel).build({
- resolution,
- extname,
- size: 0,
- videoId: this.id
- })
- const videoOutputPath = join(videosDirectory, this.getVideoFilename(newVideoFile))
+ if (this.VideoChannel.Account.Server) {
+ serverHost = this.VideoChannel.Account.Server.host
+ } else {
+ // It means it's our video
+ serverHost = CONFIG.WEBSERVER.HOST
+ }
- const transcodeOptions = {
- inputPath: videoInputPath,
- outputPath: videoOutputPath,
- resolution
+ return {
+ id: this.id,
+ uuid: this.uuid,
+ name: this.name,
+ category: this.category,
+ categoryLabel: this.getCategoryLabel(),
+ licence: this.licence,
+ licenceLabel: this.getLicenceLabel(),
+ language: this.language,
+ languageLabel: this.getLanguageLabel(),
+ nsfw: this.nsfw,
+ description: this.getTruncatedDescription(),
+ serverHost,
+ isLocal: this.isOwned(),
+ accountName: this.VideoChannel.Account.name,
+ duration: this.duration,
+ views: this.views,
+ likes: this.likes,
+ dislikes: this.dislikes,
+ tags: map<TagModel, string>(this.Tags, 'name'),
+ thumbnailPath: this.getThumbnailPath(),
+ previewPath: this.getPreviewPath(),
+ embedPath: this.getEmbedPath(),
+ createdAt: this.createdAt,
+ updatedAt: this.updatedAt
+ }
}
- await transcode(transcodeOptions)
+ toFormattedDetailsJSON () {
+ const formattedJson = this.toFormattedJSON()
- const stats = await statPromise(videoOutputPath)
+ // Maybe our server is not up to date and there are new privacy settings since our version
+ let privacyLabel = VIDEO_PRIVACIES[this.privacy]
+ if (!privacyLabel) privacyLabel = 'Unknown'
- newVideoFile.set('size', stats.size)
+ const detailsJson = {
+ privacyLabel,
+ privacy: this.privacy,
+ descriptionPath: this.getDescriptionPath(),
+ channel: this.VideoChannel.toFormattedJSON(),
+ account: this.VideoChannel.Account.toFormattedJSON(),
+ files: []
+ }
- await this.createTorrentAndSetInfoHash(newVideoFile)
+ // Format and sort video files
+ const { baseUrlHttp, baseUrlWs } = this.getBaseUrls()
+ detailsJson.files = this.VideoFiles
+ .map(videoFile => {
+ let resolutionLabel = videoFile.resolution + 'p'
+
+ return {
+ resolution: videoFile.resolution,
+ resolutionLabel,
+ magnetUri: this.generateMagnetUri(videoFile, baseUrlHttp, baseUrlWs),
+ size: videoFile.size,
+ torrentUrl: this.getTorrentUrl(videoFile, baseUrlHttp),
+ fileUrl: this.getVideoFileUrl(videoFile, baseUrlHttp)
+ }
+ })
+ .sort((a, b) => {
+ if (a.resolution < b.resolution) return 1
+ if (a.resolution === b.resolution) return 0
+ return -1
+ })
+
+ return Object.assign(formattedJson, detailsJson)
+ }
- await newVideoFile.save()
+ toActivityPubObject (): VideoTorrentObject {
+ const { baseUrlHttp, baseUrlWs } = this.getBaseUrls()
+ if (!this.Tags) this.Tags = []
- this.VideoFiles.push(newVideoFile)
-}
+ const tag = this.Tags.map(t => ({
+ type: 'Hashtag' as 'Hashtag',
+ name: t.name
+ }))
-getOriginalFileHeight = function (this: VideoInstance) {
- const originalFilePath = this.getVideoFilePath(this.getOriginalFile())
+ let language
+ if (this.language) {
+ language = {
+ identifier: this.language + '',
+ name: this.getLanguageLabel()
+ }
+ }
- return getVideoFileHeight(originalFilePath)
-}
+ let category
+ if (this.category) {
+ category = {
+ identifier: this.category + '',
+ name: this.getCategoryLabel()
+ }
+ }
-getDescriptionPath = function (this: VideoInstance) {
- return `/api/${API_VERSION}/videos/${this.uuid}/description`
-}
+ let licence
+ if (this.licence) {
+ licence = {
+ identifier: this.licence + '',
+ name: this.getLicenceLabel()
+ }
+ }
-getCategoryLabel = function (this: VideoInstance) {
- let categoryLabel = VIDEO_CATEGORIES[this.category]
- if (!categoryLabel) categoryLabel = 'Misc'
+ let likesObject
+ let dislikesObject
- return categoryLabel
-}
+ if (Array.isArray(this.AccountVideoRates)) {
+ const likes: string[] = []
+ const dislikes: string[] = []
-getLicenceLabel = function (this: VideoInstance) {
- let licenceLabel = VIDEO_LICENCES[this.licence]
- if (!licenceLabel) licenceLabel = 'Unknown'
+ for (const rate of this.AccountVideoRates) {
+ if (rate.type === 'like') {
+ likes.push(rate.Account.url)
+ } else if (rate.type === 'dislike') {
+ dislikes.push(rate.Account.url)
+ }
+ }
- return licenceLabel
-}
+ likesObject = activityPubCollection(likes)
+ dislikesObject = activityPubCollection(dislikes)
+ }
-getLanguageLabel = function (this: VideoInstance) {
- let languageLabel = VIDEO_LANGUAGES[this.language]
- if (!languageLabel) languageLabel = 'Unknown'
+ let sharesObject
+ if (Array.isArray(this.VideoShares)) {
+ const shares: string[] = []
- return languageLabel
-}
+ for (const videoShare of this.VideoShares) {
+ const shareUrl = getAnnounceActivityPubUrl(this.url, videoShare.Account)
+ shares.push(shareUrl)
+ }
-removeThumbnail = function (this: VideoInstance) {
- const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName())
- return unlinkPromise(thumbnailPath)
-}
+ sharesObject = activityPubCollection(shares)
+ }
-removePreview = function (this: VideoInstance) {
- // Same name than video thumbnail
- return unlinkPromise(CONFIG.STORAGE.PREVIEWS_DIR + this.getPreviewName())
-}
+ const url = []
+ for (const file of this.VideoFiles) {
+ url.push({
+ type: 'Link',
+ mimeType: 'video/' + file.extname.replace('.', ''),
+ url: this.getVideoFileUrl(file, baseUrlHttp),
+ width: file.resolution,
+ size: file.size
+ })
+
+ url.push({
+ type: 'Link',
+ mimeType: 'application/x-bittorrent',
+ url: this.getTorrentUrl(file, baseUrlHttp),
+ width: file.resolution
+ })
+
+ url.push({
+ type: 'Link',
+ mimeType: 'application/x-bittorrent;x-scheme-handler/magnet',
+ url: this.generateMagnetUri(file, baseUrlHttp, baseUrlWs),
+ width: file.resolution
+ })
+ }
-removeFile = function (this: VideoInstance, videoFile: VideoFileInstance) {
- const filePath = join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile))
- return unlinkPromise(filePath)
-}
+ // Add video url too
+ url.push({
+ type: 'Link',
+ mimeType: 'text/html',
+ url: CONFIG.WEBSERVER.URL + '/videos/watch/' + this.uuid
+ })
-removeTorrent = function (this: VideoInstance, videoFile: VideoFileInstance) {
- const torrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile))
- return unlinkPromise(torrentPath)
-}
+ return {
+ type: 'Video' as 'Video',
+ id: this.url,
+ name: this.name,
+ // https://www.w3.org/TR/activitystreams-vocabulary/#dfn-duration
+ duration: 'PT' + this.duration + 'S',
+ uuid: this.uuid,
+ tag,
+ category,
+ licence,
+ language,
+ views: this.views,
+ nsfw: this.nsfw,
+ published: this.createdAt.toISOString(),
+ updated: this.updatedAt.toISOString(),
+ mediaType: 'text/markdown',
+ content: this.getTruncatedDescription(),
+ icon: {
+ type: 'Image',
+ url: this.getThumbnailUrl(baseUrlHttp),
+ mediaType: 'image/jpeg',
+ width: THUMBNAILS_SIZE.width,
+ height: THUMBNAILS_SIZE.height
+ },
+ url,
+ likes: likesObject,
+ dislikes: dislikesObject,
+ shares: sharesObject
+ }
+ }
+
+ getTruncatedDescription () {
+ if (!this.description) return null
-// ------------------------------ STATICS ------------------------------
+ const options = {
+ length: CONSTRAINTS_FIELDS.VIDEOS.TRUNCATED_DESCRIPTION.max
+ }
-list = function () {
- const query = {
- include: [ Video['sequelize'].models.VideoFile ]
+ return truncate(this.description, options)
}
- return Video.findAll(query)
-}
+ optimizeOriginalVideofile = async function () {
+ const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
+ const newExtname = '.mp4'
+ const inputVideoFile = this.getOriginalFile()
+ const videoInputPath = join(videosDirectory, this.getVideoFilename(inputVideoFile))
+ const videoOutputPath = join(videosDirectory, this.id + '-transcoded' + newExtname)
-listAllAndSharedByAccountForOutbox = function (accountId: number, start: number, count: number) {
- function getRawQuery (select: string) {
- const queryVideo = 'SELECT ' + select + ' FROM "Videos" AS "Video" ' +
- 'INNER JOIN "VideoChannels" AS "VideoChannel" ON "VideoChannel"."id" = "Video"."channelId" ' +
- 'WHERE "VideoChannel"."accountId" = ' + accountId
- const queryVideoShare = 'SELECT ' + select + ' FROM "VideoShares" AS "VideoShare" ' +
- 'INNER JOIN "Videos" AS "Video" ON "Video"."id" = "VideoShare"."videoId" ' +
- 'WHERE "VideoShare"."accountId" = ' + accountId
+ const transcodeOptions = {
+ inputPath: videoInputPath,
+ outputPath: videoOutputPath
+ }
- let rawQuery = `(${queryVideo}) UNION (${queryVideoShare})`
+ try {
+ // Could be very long!
+ await transcode(transcodeOptions)
- return rawQuery
- }
+ await unlinkPromise(videoInputPath)
- const rawQuery = getRawQuery('"Video"."id"')
- const rawCountQuery = getRawQuery('COUNT("Video"."id") as "total"')
+ // Important to do this before getVideoFilename() to take in account the new file extension
+ inputVideoFile.set('extname', newExtname)
- const query = {
- distinct: true,
- offset: start,
- limit: count,
- order: [ getSort('createdAt'), [ Video['sequelize'].models.Tag, 'name', 'ASC' ] ],
- where: {
- id: {
- [Sequelize.Op.in]: Sequelize.literal('(' + rawQuery + ')')
- }
- },
- include: [
- {
- model: Video['sequelize'].models.VideoShare,
- required: false,
- where: {
- [Sequelize.Op.and]: [
- {
- id: {
- [Sequelize.Op.not]: null
- }
- },
- {
- accountId
- }
- ]
- },
- include: [ Video['sequelize'].models.Account ]
- },
- {
- model: Video['sequelize'].models.VideoChannel,
- required: true,
- include: [
- {
- model: Video['sequelize'].models.Account,
- required: true
- }
- ]
- },
- {
- model: Video['sequelize'].models.AccountVideoRate,
- include: [ Video['sequelize'].models.Account ]
- },
- Video['sequelize'].models.VideoFile,
- Video['sequelize'].models.Tag
- ]
- }
+ await renamePromise(videoOutputPath, this.getVideoFilePath(inputVideoFile))
+ const stats = await statPromise(this.getVideoFilePath(inputVideoFile))
- return Bluebird.all([
- Video.findAll(query),
- Video['sequelize'].query(rawCountQuery, { type: Sequelize.QueryTypes.SELECT })
- ]).then(([ rows, totals ]) => {
- // totals: totalVideos + totalVideoShares
- let totalVideos = 0
- let totalVideoShares = 0
- if (totals[0]) totalVideos = parseInt(totals[0].total, 10)
- if (totals[1]) totalVideoShares = parseInt(totals[1].total, 10)
-
- const total = totalVideos + totalVideoShares
- return {
- data: rows,
- total: total
- }
- })
-}
+ inputVideoFile.set('size', stats.size)
-listUserVideosForApi = function (userId: number, start: number, count: number, sort: string) {
- const query = {
- distinct: true,
- offset: start,
- limit: count,
- order: [ getSort(sort), [ Video['sequelize'].models.Tag, 'name', 'ASC' ] ],
- include: [
- {
- model: Video['sequelize'].models.VideoChannel,
- required: true,
- include: [
- {
- model: Video['sequelize'].models.Account,
- where: {
- userId
- },
- required: true
- }
- ]
- },
- Video['sequelize'].models.Tag
- ]
- }
+ await this.createTorrentAndSetInfoHash(inputVideoFile)
+ await inputVideoFile.save()
- return Video.findAndCountAll(query).then(({ rows, count }) => {
- return {
- data: rows,
- total: count
- }
- })
-}
+ } catch (err) {
+ // Auto destruction...
+ this.destroy().catch(err => logger.error('Cannot destruct video after transcoding failure.', err))
-listForApi = function (start: number, count: number, sort: string) {
- const query = {
- distinct: true,
- offset: start,
- limit: count,
- order: [ getSort(sort), [ Video['sequelize'].models.Tag, 'name', 'ASC' ] ],
- include: [
- {
- model: Video['sequelize'].models.VideoChannel,
- required: true,
- include: [
- {
- model: Video['sequelize'].models.Account,
- required: true,
- include: [
- {
- model: Video['sequelize'].models.Server,
- required: false
- }
- ]
- }
- ]
- },
- Video['sequelize'].models.Tag
- ],
- where: createBaseVideosWhere()
+ throw err
+ }
}
- return Video.findAndCountAll(query).then(({ rows, count }) => {
- return {
- data: rows,
- total: count
- }
- })
-}
+ transcodeOriginalVideofile = async function (resolution: VideoResolution) {
+ const videosDirectory = CONFIG.STORAGE.VIDEOS_DIR
+ const extname = '.mp4'
-load = function (id: number) {
- return Video.findById(id)
-}
+ // We are sure it's x264 in mp4 because optimizeOriginalVideofile was already executed
+ const videoInputPath = join(videosDirectory, this.getVideoFilename(this.getOriginalFile()))
-loadByUUID = function (uuid: string, t?: Sequelize.Transaction) {
- const query: Sequelize.FindOptions<VideoAttributes> = {
- where: {
- uuid
- },
- include: [ Video['sequelize'].models.VideoFile ]
- }
+ const newVideoFile = new VideoFileModel({
+ resolution,
+ extname,
+ size: 0,
+ videoId: this.id
+ })
+ const videoOutputPath = join(videosDirectory, this.getVideoFilename(newVideoFile))
- if (t !== undefined) query.transaction = t
+ const transcodeOptions = {
+ inputPath: videoInputPath,
+ outputPath: videoOutputPath,
+ resolution
+ }
- return Video.findOne(query)
-}
+ await transcode(transcodeOptions)
-loadByUrlAndPopulateAccount = function (url: string, t?: Sequelize.Transaction) {
- const query: Sequelize.FindOptions<VideoAttributes> = {
- where: {
- url
- },
- include: [
- Video['sequelize'].models.VideoFile,
- {
- model: Video['sequelize'].models.VideoChannel,
- include: [ Video['sequelize'].models.Account ]
- }
- ]
- }
+ const stats = await statPromise(videoOutputPath)
- if (t !== undefined) query.transaction = t
+ newVideoFile.set('size', stats.size)
- return Video.findOne(query)
-}
+ await this.createTorrentAndSetInfoHash(newVideoFile)
-loadByUUIDOrURL = function (uuid: string, url: string, t?: Sequelize.Transaction) {
- const query: Sequelize.FindOptions<VideoAttributes> = {
- where: {
- [Sequelize.Op.or]: [
- { uuid },
- { url }
- ]
- },
- include: [ Video['sequelize'].models.VideoFile ]
+ await newVideoFile.save()
+
+ this.VideoFiles.push(newVideoFile)
}
- if (t !== undefined) query.transaction = t
+ getOriginalFileHeight () {
+ const originalFilePath = this.getVideoFilePath(this.getOriginalFile())
- return Video.findOne(query)
-}
+ return getVideoFileHeight(originalFilePath)
+ }
-loadAndPopulateAccountAndServerAndTags = function (id: number) {
- const options = {
- order: [ [ Video['sequelize'].models.Tag, 'name', 'ASC' ] ],
- include: [
- {
- model: Video['sequelize'].models.VideoChannel,
- include: [
- {
- model: Video['sequelize'].models.Account,
- include: [ { model: Video['sequelize'].models.Server, required: false } ]
- }
- ]
- },
- {
- model: Video['sequelize'].models.AccountVideoRate,
- include: [ Video['sequelize'].models.Account ]
- },
- {
- model: Video['sequelize'].models.VideoShare,
- include: [ Video['sequelize'].models.Account ]
- },
- Video['sequelize'].models.Tag,
- Video['sequelize'].models.VideoFile
- ]
+ getDescriptionPath () {
+ return `/api/${API_VERSION}/videos/${this.uuid}/description`
}
- return Video.findById(id, options)
-}
+ getCategoryLabel () {
+ let categoryLabel = VIDEO_CATEGORIES[this.category]
+ if (!categoryLabel) categoryLabel = 'Misc'
-loadByUUIDAndPopulateAccountAndServerAndTags = function (uuid: string) {
- const options = {
- order: [ [ Video['sequelize'].models.Tag, 'name', 'ASC' ] ],
- where: {
- uuid
- },
- include: [
- {
- model: Video['sequelize'].models.VideoChannel,
- include: [
- {
- model: Video['sequelize'].models.Account,
- include: [ { model: Video['sequelize'].models.Server, required: false } ]
- }
- ]
- },
- {
- model: Video['sequelize'].models.AccountVideoRate,
- include: [ Video['sequelize'].models.Account ]
- },
- {
- model: Video['sequelize'].models.VideoShare,
- include: [ Video['sequelize'].models.Account ]
- },
- Video['sequelize'].models.Tag,
- Video['sequelize'].models.VideoFile
- ]
+ return categoryLabel
}
- return Video.findOne(options)
-}
+ getLicenceLabel () {
+ let licenceLabel = VIDEO_LICENCES[this.licence]
+ if (!licenceLabel) licenceLabel = 'Unknown'
-searchAndPopulateAccountAndServerAndTags = function (value: string, start: number, count: number, sort: string) {
- const serverInclude: Sequelize.IncludeOptions = {
- model: Video['sequelize'].models.Server,
- required: false
+ return licenceLabel
}
- const accountInclude: Sequelize.IncludeOptions = {
- model: Video['sequelize'].models.Account,
- include: [ serverInclude ]
+ getLanguageLabel () {
+ let languageLabel = VIDEO_LANGUAGES[this.language]
+ if (!languageLabel) languageLabel = 'Unknown'
+
+ return languageLabel
}
- const videoChannelInclude: Sequelize.IncludeOptions = {
- model: Video['sequelize'].models.VideoChannel,
- include: [ accountInclude ],
- required: true
+ removeThumbnail () {
+ const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, this.getThumbnailName())
+ return unlinkPromise(thumbnailPath)
}
- const tagInclude: Sequelize.IncludeOptions = {
- model: Video['sequelize'].models.Tag
+ removePreview () {
+ // Same name than video thumbnail
+ return unlinkPromise(CONFIG.STORAGE.PREVIEWS_DIR + this.getPreviewName())
}
- const query: Sequelize.FindOptions<VideoAttributes> = {
- distinct: true,
- where: createBaseVideosWhere(),
- offset: start,
- limit: count,
- order: [ getSort(sort), [ Video['sequelize'].models.Tag, 'name', 'ASC' ] ]
+ removeFile (videoFile: VideoFileModel) {
+ const filePath = join(CONFIG.STORAGE.VIDEOS_DIR, this.getVideoFilename(videoFile))
+ return unlinkPromise(filePath)
}
- // TODO: search on tags too
- // const escapedValue = Video['sequelize'].escape('%' + value + '%')
- // query.where['id'][Sequelize.Op.in] = Video['sequelize'].literal(
- // `(SELECT "VideoTags"."videoId"
- // FROM "Tags"
- // INNER JOIN "VideoTags" ON "Tags"."id" = "VideoTags"."tagId"
- // WHERE name ILIKE ${escapedValue}
- // )`
- // )
-
- // TODO: search on account too
- // accountInclude.where = {
- // name: {
- // [Sequelize.Op.iLike]: '%' + value + '%'
- // }
- // }
- query.where['name'] = {
- [Sequelize.Op.iLike]: '%' + value + '%'
+ removeTorrent (videoFile: VideoFileModel) {
+ const torrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile))
+ return unlinkPromise(torrentPath)
}
- query.include = [
- videoChannelInclude, tagInclude
- ]
+ private getBaseUrls () {
+ let baseUrlHttp
+ let baseUrlWs
- return Video.findAndCountAll(query).then(({ rows, count }) => {
- return {
- data: rows,
- total: count
+ if (this.isOwned()) {
+ baseUrlHttp = CONFIG.WEBSERVER.URL
+ baseUrlWs = CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT
+ } else {
+ baseUrlHttp = REMOTE_SCHEME.HTTP + '://' + this.VideoChannel.Account.Server.host
+ baseUrlWs = REMOTE_SCHEME.WS + '://' + this.VideoChannel.Account.Server.host
}
- })
-}
-
-// ---------------------------------------------------------------------------
-function createBaseVideosWhere () {
- return {
- id: {
- [Sequelize.Op.notIn]: Video['sequelize'].literal(
- '(SELECT "BlacklistedVideos"."videoId" FROM "BlacklistedVideos")'
- )
- },
- privacy: VideoPrivacy.PUBLIC
+ return { baseUrlHttp, baseUrlWs }
}
-}
-function getBaseUrls (video: VideoInstance) {
- let baseUrlHttp
- let baseUrlWs
-
- if (video.isOwned()) {
- baseUrlHttp = CONFIG.WEBSERVER.URL
- baseUrlWs = CONFIG.WEBSERVER.WS + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT
- } else {
- baseUrlHttp = REMOTE_SCHEME.HTTP + '://' + video.VideoChannel.Account.Server.host
- baseUrlWs = REMOTE_SCHEME.WS + '://' + video.VideoChannel.Account.Server.host
+ private getThumbnailUrl (baseUrlHttp: string) {
+ return baseUrlHttp + STATIC_PATHS.THUMBNAILS + this.getThumbnailName()
}
- return { baseUrlHttp, baseUrlWs }
-}
-
-function getThumbnailUrl (video: VideoInstance, baseUrlHttp: string) {
- return baseUrlHttp + STATIC_PATHS.THUMBNAILS + video.getThumbnailName()
-}
+ private getTorrentUrl (videoFile: VideoFileModel, baseUrlHttp: string) {
+ return baseUrlHttp + STATIC_PATHS.TORRENTS + this.getTorrentFileName(videoFile)
+ }
-function getTorrentUrl (video: VideoInstance, videoFile: VideoFileInstance, baseUrlHttp: string) {
- return baseUrlHttp + STATIC_PATHS.TORRENTS + video.getTorrentFileName(videoFile)
-}
+ private getVideoFileUrl (videoFile: VideoFileModel, baseUrlHttp: string) {
+ return baseUrlHttp + STATIC_PATHS.WEBSEED + this.getVideoFilename(videoFile)
+ }
-function getVideoFileUrl (video: VideoInstance, videoFile: VideoFileInstance, baseUrlHttp: string) {
- return baseUrlHttp + STATIC_PATHS.WEBSEED + video.getVideoFilename(videoFile)
-}
+ private generateMagnetUri (videoFile: VideoFileModel, baseUrlHttp: string, baseUrlWs: string) {
+ const xs = this.getTorrentUrl(videoFile, baseUrlHttp)
+ const announce = [ baseUrlWs + '/tracker/socket', baseUrlHttp + '/tracker/announce' ]
+ const urlList = [ this.getVideoFileUrl(videoFile, baseUrlHttp) ]
+
+ const magnetHash = {
+ xs,
+ announce,
+ urlList,
+ infoHash: videoFile.infoHash,
+ name: this.name
+ }
-function generateMagnetUri (video: VideoInstance, videoFile: VideoFileInstance, baseUrlHttp: string, baseUrlWs: string) {
- const xs = getTorrentUrl(video, videoFile, baseUrlHttp)
- const announce = [ baseUrlWs + '/tracker/socket', baseUrlHttp + '/tracker/announce' ]
- const urlList = [ getVideoFileUrl(video, videoFile, baseUrlHttp) ]
-
- const magnetHash = {
- xs,
- announce,
- urlList,
- infoHash: videoFile.infoHash,
- name: video.name
+ return magnetUtil.encode(magnetHash)
}
-
- return magnetUtil.encode(magnetHash)
}
import { UserRight } from './user-right.enum'
-import user from '../../../server/models/account/user'
// Keep the order
export enum UserRole {
{
"compilerOptions": {
"module": "commonjs",
- "target": "es5",
+ "target": "es2015",
"noImplicitAny": false,
"sourceMap": false,
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
"outDir": "./dist",
"lib": [
"dom",
- "es2015"
+ "es2015",
+ "es2016",
+ "es2017"
],
"types": [
"node"
dependencies:
"@types/node" "*"
-"@types/bluebird@*":
+"@types/bluebird@*", "@types/bluebird@3.5.18":
version "3.5.18"
resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.18.tgz#6a60435d4663e290f3709898a4f75014f279c4d6"
version "8.0.53"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.53.tgz#396b35af826fa66aad472c8cb7b8d5e277f4e6d8"
+"@types/node@6.0.41":
+ version "6.0.41"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.41.tgz#578cf53aaec65887bcaf16792f8722932e8ff8ea"
+
"@types/parse-torrent-file@*":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@types/parse-torrent-file/-/parse-torrent-file-4.0.1.tgz#056a6c18f3fac0cd7c6c74540f00496a3225976b"
version "1.9.3"
resolved "https://registry.yarnpkg.com/@types/pem/-/pem-1.9.3.tgz#0c864c8b79e43fef6367db895f60fd1edd10e86c"
+"@types/reflect-metadata@0.0.4":
+ version "0.0.4"
+ resolved "https://registry.yarnpkg.com/@types/reflect-metadata/-/reflect-metadata-0.0.4.tgz#b6477ca9a97e5265f2ac67f9ea704eae5e0eaf4d"
+
"@types/request@^2.0.3":
version "2.0.8"
resolved "https://registry.yarnpkg.com/@types/request/-/request-2.0.8.tgz#424d3de255868107ed4dd6695c65c5f1766aba80"
"@types/form-data" "*"
"@types/node" "*"
-"@types/sequelize@^4.0.55":
+"@types/sequelize@4.0.79", "@types/sequelize@^4.0.55":
version "4.0.79"
resolved "https://registry.yarnpkg.com/@types/sequelize/-/sequelize-4.0.79.tgz#74c366407a978e493e70d7cea3d80c681aed15c0"
dependencies:
es6-symbol "3.1.1"
event-emitter "~0.3.5"
+es6-shim@0.35.3:
+ version "0.35.3"
+ resolved "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.35.3.tgz#9bfb7363feffff87a6cdb6cd93e405ec3c4b6f26"
+
es6-symbol@3.1.1, es6-symbol@^3.1.1, es6-symbol@~3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77"
dependencies:
resolve "^1.1.6"
+reflect-metadata@^0.1.10:
+ version "0.1.10"
+ resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.10.tgz#b4f83704416acad89988c9b15635d47e03b9344a"
+
regex-cache@^0.4.2:
version "0.4.4"
resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd"
range-parser "~1.2.0"
statuses "~1.3.1"
+sequelize-typescript@^0.6.1:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/sequelize-typescript/-/sequelize-typescript-0.6.1.tgz#603c948183aebb534dbea182258ee6b0d0571bcb"
+ dependencies:
+ "@types/bluebird" "3.5.18"
+ "@types/node" "6.0.41"
+ "@types/reflect-metadata" "0.0.4"
+ "@types/sequelize" "4.0.79"
+ es6-shim "0.35.3"
+ glob "7.1.2"
+
sequelize@^4.7.5:
version "4.23.1"
resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-4.23.1.tgz#2ec517bbb2ccddece45f934ef3c770cfbe818a8b"