aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/helpers/custom-validators/activitypub/account.ts7
-rw-r--r--server/helpers/custom-validators/activitypub/misc.ts10
-rw-r--r--server/helpers/custom-validators/activitypub/videos.ts7
-rw-r--r--server/helpers/custom-validators/video-channels.ts8
-rw-r--r--server/helpers/custom-validators/videos.ts6
-rw-r--r--server/initializers/constants.ts11
-rw-r--r--server/initializers/installer.ts20
-rw-r--r--server/lib/user.ts3
-rw-r--r--server/lib/video-channel.ts3
-rw-r--r--server/models/account/account.ts36
-rw-r--r--server/models/account/user.ts21
-rw-r--r--server/models/pod/pod.ts10
-rw-r--r--server/models/video/video-channel.ts9
-rw-r--r--server/models/video/video.ts8
14 files changed, 95 insertions, 64 deletions
diff --git a/server/helpers/custom-validators/activitypub/account.ts b/server/helpers/custom-validators/activitypub/account.ts
index 8a7d1b7fe..acd2b8058 100644
--- a/server/helpers/custom-validators/activitypub/account.ts
+++ b/server/helpers/custom-validators/activitypub/account.ts
@@ -3,6 +3,7 @@ import * as validator from 'validator'
3import { exists, isUUIDValid } from '../misc' 3import { exists, isUUIDValid } from '../misc'
4import { isActivityPubUrlValid } from './misc' 4import { isActivityPubUrlValid } from './misc'
5import { isUserUsernameValid } from '../users' 5import { isUserUsernameValid } from '../users'
6import { CONSTRAINTS_FIELDS } from '../../../initializers/constants'
6 7
7function isAccountEndpointsObjectValid (endpointObject: any) { 8function isAccountEndpointsObjectValid (endpointObject: any) {
8 return isAccountSharedInboxValid(endpointObject.sharedInbox) 9 return isAccountSharedInboxValid(endpointObject.sharedInbox)
@@ -34,7 +35,8 @@ function isAccountPublicKeyValid (publicKey: string) {
34 return exists(publicKey) && 35 return exists(publicKey) &&
35 typeof publicKey === 'string' && 36 typeof publicKey === 'string' &&
36 publicKey.startsWith('-----BEGIN PUBLIC KEY-----') && 37 publicKey.startsWith('-----BEGIN PUBLIC KEY-----') &&
37 publicKey.endsWith('-----END PUBLIC KEY-----') 38 publicKey.endsWith('-----END PUBLIC KEY-----') &&
39 validator.isLength(publicKey, CONSTRAINTS_FIELDS.ACCOUNTS.PUBLIC_KEY)
38} 40}
39 41
40function isAccountIdValid (id: string) { 42function isAccountIdValid (id: string) {
@@ -73,7 +75,8 @@ function isAccountPrivateKeyValid (privateKey: string) {
73 return exists(privateKey) && 75 return exists(privateKey) &&
74 typeof privateKey === 'string' && 76 typeof privateKey === 'string' &&
75 privateKey.startsWith('-----BEGIN RSA PRIVATE KEY-----') && 77 privateKey.startsWith('-----BEGIN RSA PRIVATE KEY-----') &&
76 privateKey.endsWith('-----END RSA PRIVATE KEY-----') 78 privateKey.endsWith('-----END RSA PRIVATE KEY-----') &&
79 validator.isLength(privateKey, CONSTRAINTS_FIELDS.ACCOUNTS.PRIVATE_KEY)
77} 80}
78 81
79function isRemoteAccountValid (remoteAccount: any) { 82function isRemoteAccountValid (remoteAccount: any) {
diff --git a/server/helpers/custom-validators/activitypub/misc.ts b/server/helpers/custom-validators/activitypub/misc.ts
index f049f5a8c..a94c36b51 100644
--- a/server/helpers/custom-validators/activitypub/misc.ts
+++ b/server/helpers/custom-validators/activitypub/misc.ts
@@ -1,4 +1,7 @@
1import * as validator from 'validator'
1import { exists } from '../misc' 2import { exists } from '../misc'
3import { isTestInstance } from '../../core-utils'
4import { CONSTRAINTS_FIELDS } from '../../../initializers/constants'
2 5
3function isActivityPubUrlValid (url: string) { 6function isActivityPubUrlValid (url: string) {
4 const isURLOptions = { 7 const isURLOptions = {
@@ -9,7 +12,12 @@ function isActivityPubUrlValid (url: string) {
9 protocols: [ 'http', 'https' ] 12 protocols: [ 'http', 'https' ]
10 } 13 }
11 14
12 return exists(url) && validator.isURL(url, isURLOptions) 15 // We validate 'localhost', so we don't have the top level domain
16 if (isTestInstance()) {
17 isURLOptions.require_tld = false
18 }
19
20 return exists(url) && validator.isURL(url, isURLOptions) && validator.isLength(url, CONSTRAINTS_FIELDS.ACCOUNTS.URL)
13} 21}
14 22
15function isBaseActivityValid (activity: any, type: string) { 23function isBaseActivityValid (activity: any, type: string) {
diff --git a/server/helpers/custom-validators/activitypub/videos.ts b/server/helpers/custom-validators/activitypub/videos.ts
index 9233a1359..8f6d50f50 100644
--- a/server/helpers/custom-validators/activitypub/videos.ts
+++ b/server/helpers/custom-validators/activitypub/videos.ts
@@ -10,7 +10,8 @@ import {
10 isVideoTruncatedDescriptionValid, 10 isVideoTruncatedDescriptionValid,
11 isVideoDurationValid, 11 isVideoDurationValid,
12 isVideoNameValid, 12 isVideoNameValid,
13 isVideoTagValid 13 isVideoTagValid,
14 isVideoUrlValid
14} from '../videos' 15} from '../videos'
15import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../video-channels' 16import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../video-channels'
16import { isBaseActivityValid } from './misc' 17import { isBaseActivityValid } from './misc'
@@ -93,7 +94,7 @@ function isRemoteVideoContentValid (mediaType: string, content: string) {
93 94
94function isRemoteVideoIconValid (icon: any) { 95function isRemoteVideoIconValid (icon: any) {
95 return icon.type === 'Image' && 96 return icon.type === 'Image' &&
96 validator.isURL(icon.url) && 97 isVideoUrlValid(icon.url) &&
97 icon.mediaType === 'image/jpeg' && 98 icon.mediaType === 'image/jpeg' &&
98 validator.isInt(icon.width, { min: 0 }) && 99 validator.isInt(icon.width, { min: 0 }) &&
99 validator.isInt(icon.height, { min: 0 }) 100 validator.isInt(icon.height, { min: 0 })
@@ -111,7 +112,7 @@ function setValidRemoteVideoUrls (video: any) {
111function isRemoteVideoUrlValid (url: any) { 112function isRemoteVideoUrlValid (url: any) {
112 return url.type === 'Link' && 113 return url.type === 'Link' &&
113 ACTIVITY_PUB.VIDEO_URL_MIME_TYPES.indexOf(url.mimeType) !== -1 && 114 ACTIVITY_PUB.VIDEO_URL_MIME_TYPES.indexOf(url.mimeType) !== -1 &&
114 validator.isURL(url.url) && 115 isVideoUrlValid(url.url) &&
115 validator.isInt(url.width, { min: 0 }) && 116 validator.isInt(url.width, { min: 0 }) &&
116 validator.isInt(url.size, { min: 0 }) 117 validator.isInt(url.size, { min: 0 })
117} 118}
diff --git a/server/helpers/custom-validators/video-channels.ts b/server/helpers/custom-validators/video-channels.ts
index acc42f4a4..5787c3850 100644
--- a/server/helpers/custom-validators/video-channels.ts
+++ b/server/helpers/custom-validators/video-channels.ts
@@ -8,9 +8,14 @@ import { database as db, CONSTRAINTS_FIELDS } from '../../initializers'
8import { VideoChannelInstance } from '../../models' 8import { VideoChannelInstance } from '../../models'
9import { logger } from '../logger' 9import { logger } from '../logger'
10import { exists } from './misc' 10import { exists } from './misc'
11import { isActivityPubUrlValid } from './index'
11 12
12const VIDEO_CHANNELS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_CHANNELS 13const VIDEO_CHANNELS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_CHANNELS
13 14
15function isVideoChannelUrlValid (value: string) {
16 return isActivityPubUrlValid(value)
17}
18
14function isVideoChannelDescriptionValid (value: string) { 19function isVideoChannelDescriptionValid (value: string) {
15 return value === null || validator.isLength(value, VIDEO_CHANNELS_CONSTRAINTS_FIELDS.DESCRIPTION) 20 return value === null || validator.isLength(value, VIDEO_CHANNELS_CONSTRAINTS_FIELDS.DESCRIPTION)
16} 21}
@@ -53,5 +58,6 @@ export {
53 isVideoChannelDescriptionValid, 58 isVideoChannelDescriptionValid,
54 isVideoChannelNameValid, 59 isVideoChannelNameValid,
55 isVideoChannelUUIDValid, 60 isVideoChannelUUIDValid,
56 checkVideoChannelExists 61 checkVideoChannelExists,
62 isVideoChannelUrlValid
57} 63}
diff --git a/server/helpers/custom-validators/videos.ts b/server/helpers/custom-validators/videos.ts
index 487b3d646..715119cf6 100644
--- a/server/helpers/custom-validators/videos.ts
+++ b/server/helpers/custom-validators/videos.ts
@@ -19,6 +19,7 @@ import { isArray, exists } from './misc'
19import { VideoInstance } from '../../models' 19import { VideoInstance } from '../../models'
20import { logger } from '../../helpers' 20import { logger } from '../../helpers'
21import { VideoRateType } from '../../../shared' 21import { VideoRateType } from '../../../shared'
22import { isActivityPubUrlValid } from './activitypub/misc'
22 23
23const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS 24const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS
24const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES 25const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES
@@ -33,6 +34,10 @@ function isRemoteVideoCategoryValid (value: string) {
33 return validator.isInt('' + value) 34 return validator.isInt('' + value)
34} 35}
35 36
37function isVideoUrlValid (value: string) {
38 return isActivityPubUrlValid(value)
39}
40
36function isVideoLicenceValid (value: number) { 41function isVideoLicenceValid (value: number) {
37 return VIDEO_LICENCES[value] !== undefined 42 return VIDEO_LICENCES[value] !== undefined
38} 43}
@@ -219,5 +224,6 @@ export {
219 isVideoTagValid, 224 isVideoTagValid,
220 isRemoteVideoCategoryValid, 225 isRemoteVideoCategoryValid,
221 isRemoteVideoLicenceValid, 226 isRemoteVideoLicenceValid,
227 isVideoUrlValid,
222 isRemoteVideoLanguageValid 228 isRemoteVideoLanguageValid
223} 229}
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index 5d0d39395..e27d011fa 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -121,7 +121,8 @@ const CONSTRAINTS_FIELDS = {
121 }, 121 },
122 VIDEO_CHANNELS: { 122 VIDEO_CHANNELS: {
123 NAME: { min: 3, max: 120 }, // Length 123 NAME: { min: 3, max: 120 }, // Length
124 DESCRIPTION: { min: 3, max: 250 } // Length 124 DESCRIPTION: { min: 3, max: 250 }, // Length
125 URL: { min: 3, max: 2000 } // Length
125 }, 126 },
126 VIDEOS: { 127 VIDEOS: {
127 NAME: { min: 3, max: 120 }, // Length 128 NAME: { min: 3, max: 120 }, // Length
@@ -137,7 +138,13 @@ const CONSTRAINTS_FIELDS = {
137 VIEWS: { min: 0 }, 138 VIEWS: { min: 0 },
138 LIKES: { min: 0 }, 139 LIKES: { min: 0 },
139 DISLIKES: { min: 0 }, 140 DISLIKES: { min: 0 },
140 FILE_SIZE: { min: 10, max: 1024 * 1024 * 1024 * 3 /* 3Go */ } 141 FILE_SIZE: { min: 10, max: 1024 * 1024 * 1024 * 3 /* 3Go */ },
142 URL: { min: 3, max: 2000 } // Length
143 },
144 ACCOUNTS: {
145 PUBLIC_KEY: { min: 10, max: 5000 }, // Length
146 PRIVATE_KEY: { min: 10, max: 5000 }, // Length
147 URL: { min: 3, max: 2000 } // Length
141 }, 148 },
142 VIDEO_EVENTS: { 149 VIDEO_EVENTS: {
143 COUNT: { min: 0 } 150 COUNT: { min: 0 }
diff --git a/server/initializers/installer.ts b/server/initializers/installer.ts
index c617b16c9..5221b81a5 100644
--- a/server/initializers/installer.ts
+++ b/server/initializers/installer.ts
@@ -1,21 +1,25 @@
1import * as passwordGenerator from 'password-generator' 1import * as passwordGenerator from 'password-generator'
2import { UserRole } from '../../shared' 2import { UserRole } from '../../shared'
3import { logger, mkdirpPromise, rimrafPromise } from '../helpers' 3import { logger, mkdirpPromise, rimrafPromise } from '../helpers'
4import { createPrivateAndPublicKeys } from '../helpers/peertube-crypto'
5import { createUserAccountAndChannel } from '../lib' 4import { createUserAccountAndChannel } from '../lib'
5import { createLocalAccount } from '../lib/user'
6import { clientsExist, usersExist } from './checker' 6import { clientsExist, usersExist } from './checker'
7import { CACHE, CONFIG, LAST_MIGRATION_VERSION } from './constants' 7import { CACHE, CONFIG, LAST_MIGRATION_VERSION } from './constants'
8 8
9import { database as db } from './database' 9import { database as db } from './database'
10import { createLocalAccount } from '../lib/user'
11 10
12async function installApplication () { 11async function installApplication () {
13 await db.sequelize.sync() 12 try {
14 await removeCacheDirectories() 13 await db.sequelize.sync()
15 await createDirectoriesIfNotExist() 14 await removeCacheDirectories()
16 await createOAuthClientIfNotExist() 15 await createDirectoriesIfNotExist()
17 await createOAuthAdminIfNotExist() 16 await createOAuthClientIfNotExist()
18 await createApplicationIfNotExist() 17 await createOAuthAdminIfNotExist()
18 await createApplicationIfNotExist()
19 } catch (err) {
20 logger.error('Cannot install application.', err)
21 throw err
22 }
19} 23}
20 24
21// --------------------------------------------------------------------------- 25// ---------------------------------------------------------------------------
diff --git a/server/lib/user.ts b/server/lib/user.ts
index 1094c2401..d2d599dfd 100644
--- a/server/lib/user.ts
+++ b/server/lib/user.ts
@@ -16,8 +16,9 @@ async function createUserAccountAndChannel (user: UserInstance, validateUser = t
16 const userCreated = await user.save(userOptions) 16 const userCreated = await user.save(userOptions)
17 const accountCreated = await createLocalAccount(user.username, user.id, null, t) 17 const accountCreated = await createLocalAccount(user.username, user.id, null, t)
18 18
19 const videoChannelName = `Default ${userCreated.username} channel`
19 const videoChannelInfo = { 20 const videoChannelInfo = {
20 name: `Default ${userCreated.username} channel` 21 name: videoChannelName
21 } 22 }
22 const videoChannel = await createVideoChannel(videoChannelInfo, accountCreated, t) 23 const videoChannel = await createVideoChannel(videoChannelInfo, accountCreated, t)
23 24
diff --git a/server/lib/video-channel.ts b/server/lib/video-channel.ts
index 459d9d4a8..13841ea33 100644
--- a/server/lib/video-channel.ts
+++ b/server/lib/video-channel.ts
@@ -5,6 +5,7 @@ import { logger } from '../helpers'
5import { AccountInstance } from '../models' 5import { AccountInstance } from '../models'
6import { VideoChannelCreate } from '../../shared/models' 6import { VideoChannelCreate } from '../../shared/models'
7import { sendCreateVideoChannel } from './activitypub/send-request' 7import { sendCreateVideoChannel } from './activitypub/send-request'
8import { getActivityPubUrl } from '../helpers/activitypub'
8 9
9async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountInstance, t: Sequelize.Transaction) { 10async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountInstance, t: Sequelize.Transaction) {
10 const videoChannelData = { 11 const videoChannelData = {
@@ -15,6 +16,8 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account
15 } 16 }
16 17
17 const videoChannel = db.VideoChannel.build(videoChannelData) 18 const videoChannel = db.VideoChannel.build(videoChannelData)
19 videoChannel.set('url', getActivityPubUrl('videoChannel', videoChannel.uuid))
20
18 const options = { transaction: t } 21 const options = { transaction: t }
19 22
20 const videoChannelCreated = await videoChannel.save(options) 23 const videoChannelCreated = await videoChannel.save(options)
diff --git a/server/models/account/account.ts b/server/models/account/account.ts
index 6ef29c8b7..cd6c822f1 100644
--- a/server/models/account/account.ts
+++ b/server/models/account/account.ts
@@ -22,8 +22,8 @@ import {
22 22
23 AccountMethods 23 AccountMethods
24} from './account-interface' 24} from './account-interface'
25import LoadApplication = AccountMethods.LoadApplication
26import { sendDeleteAccount } from '../../lib/activitypub/send-request' 25import { sendDeleteAccount } from '../../lib/activitypub/send-request'
26import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
27 27
28let Account: Sequelize.Model<AccountInstance, AccountAttributes> 28let Account: Sequelize.Model<AccountInstance, AccountAttributes>
29let loadAccountByPodAndUUID: AccountMethods.LoadAccountByPodAndUUID 29let loadAccountByPodAndUUID: AccountMethods.LoadAccountByPodAndUUID
@@ -60,14 +60,14 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
60 type: DataTypes.STRING, 60 type: DataTypes.STRING,
61 allowNull: false, 61 allowNull: false,
62 validate: { 62 validate: {
63 usernameValid: value => { 63 nameValid: value => {
64 const res = isUserUsernameValid(value) 64 const res = isUserUsernameValid(value)
65 if (res === false) throw new Error('Username is not valid.') 65 if (res === false) throw new Error('Name is not valid.')
66 } 66 }
67 } 67 }
68 }, 68 },
69 url: { 69 url: {
70 type: DataTypes.STRING, 70 type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max),
71 allowNull: false, 71 allowNull: false,
72 validate: { 72 validate: {
73 urlValid: value => { 73 urlValid: value => {
@@ -77,7 +77,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
77 } 77 }
78 }, 78 },
79 publicKey: { 79 publicKey: {
80 type: DataTypes.STRING, 80 type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.PUBLIC_KEY.max),
81 allowNull: false, 81 allowNull: false,
82 validate: { 82 validate: {
83 publicKeyValid: value => { 83 publicKeyValid: value => {
@@ -87,7 +87,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
87 } 87 }
88 }, 88 },
89 privateKey: { 89 privateKey: {
90 type: DataTypes.STRING, 90 type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.PRIVATE_KEY.max),
91 allowNull: false, 91 allowNull: false,
92 validate: { 92 validate: {
93 privateKeyValid: value => { 93 privateKeyValid: value => {
@@ -110,14 +110,14 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
110 type: DataTypes.INTEGER, 110 type: DataTypes.INTEGER,
111 allowNull: false, 111 allowNull: false,
112 validate: { 112 validate: {
113 followersCountValid: value => { 113 followingCountValid: value => {
114 const res = isAccountFollowingCountValid(value) 114 const res = isAccountFollowingCountValid(value)
115 if (res === false) throw new Error('Following count is not valid.') 115 if (res === false) throw new Error('Following count is not valid.')
116 } 116 }
117 } 117 }
118 }, 118 },
119 inboxUrl: { 119 inboxUrl: {
120 type: DataTypes.STRING, 120 type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max),
121 allowNull: false, 121 allowNull: false,
122 validate: { 122 validate: {
123 inboxUrlValid: value => { 123 inboxUrlValid: value => {
@@ -127,7 +127,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
127 } 127 }
128 }, 128 },
129 outboxUrl: { 129 outboxUrl: {
130 type: DataTypes.STRING, 130 type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max),
131 allowNull: false, 131 allowNull: false,
132 validate: { 132 validate: {
133 outboxUrlValid: value => { 133 outboxUrlValid: value => {
@@ -137,7 +137,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
137 } 137 }
138 }, 138 },
139 sharedInboxUrl: { 139 sharedInboxUrl: {
140 type: DataTypes.STRING, 140 type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max),
141 allowNull: false, 141 allowNull: false,
142 validate: { 142 validate: {
143 sharedInboxUrlValid: value => { 143 sharedInboxUrlValid: value => {
@@ -147,7 +147,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
147 } 147 }
148 }, 148 },
149 followersUrl: { 149 followersUrl: {
150 type: DataTypes.STRING, 150 type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max),
151 allowNull: false, 151 allowNull: false,
152 validate: { 152 validate: {
153 followersUrlValid: value => { 153 followersUrlValid: value => {
@@ -157,7 +157,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
157 } 157 }
158 }, 158 },
159 followingUrl: { 159 followingUrl: {
160 type: DataTypes.STRING, 160 type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max),
161 allowNull: false, 161 allowNull: false,
162 validate: { 162 validate: {
163 followingUrlValid: value => { 163 followingUrlValid: value => {
@@ -241,7 +241,7 @@ function associate (models) {
241 241
242 Account.belongsTo(models.Application, { 242 Account.belongsTo(models.Application, {
243 foreignKey: { 243 foreignKey: {
244 name: 'userId', 244 name: 'applicationId',
245 allowNull: true 245 allowNull: true
246 }, 246 },
247 onDelete: 'cascade' 247 onDelete: 'cascade'
@@ -256,7 +256,7 @@ function associate (models) {
256 hooks: true 256 hooks: true
257 }) 257 })
258 258
259 Account.hasMany(models.AccountFollower, { 259 Account.hasMany(models.AccountFollow, {
260 foreignKey: { 260 foreignKey: {
261 name: 'accountId', 261 name: 'accountId',
262 allowNull: false 262 allowNull: false
@@ -265,7 +265,7 @@ function associate (models) {
265 onDelete: 'cascade' 265 onDelete: 'cascade'
266 }) 266 })
267 267
268 Account.hasMany(models.AccountFollower, { 268 Account.hasMany(models.AccountFollow, {
269 foreignKey: { 269 foreignKey: {
270 name: 'targetAccountId', 270 name: 'targetAccountId',
271 allowNull: false 271 allowNull: false
@@ -329,7 +329,7 @@ getFollowerSharedInboxUrls = function (this: AccountInstance) {
329 attributes: [ 'sharedInboxUrl' ], 329 attributes: [ 'sharedInboxUrl' ],
330 include: [ 330 include: [
331 { 331 {
332 model: Account['sequelize'].models.AccountFollower, 332 model: Account['sequelize'].models.AccountFollow,
333 where: { 333 where: {
334 targetAccountId: this.id 334 targetAccountId: this.id
335 } 335 }
@@ -523,9 +523,9 @@ async function createListAcceptedFollowForApiQuery (type: 'followers' | 'followi
523 523
524 for (const selection of selections) { 524 for (const selection of selections) {
525 let query = 'SELECT ' + selection + ' FROM "Account" ' + 525 let query = 'SELECT ' + selection + ' FROM "Account" ' +
526 'INNER JOIN "AccountFollower" ON "AccountFollower"."' + firstJoin + '" = "Account"."id" ' + 526 'INNER JOIN "AccountFollow" ON "AccountFollow"."' + firstJoin + '" = "Account"."id" ' +
527 'INNER JOIN "Account" AS "Follows" ON "Followers"."id" = "Follows"."' + secondJoin + '" ' + 527 'INNER JOIN "Account" AS "Follows" ON "Followers"."id" = "Follows"."' + secondJoin + '" ' +
528 'WHERE "Account"."id" = $id AND "AccountFollower"."state" = \'accepted\' ' + 528 'WHERE "Account"."id" = $id AND "AccountFollow"."state" = \'accepted\' ' +
529 'LIMIT ' + start 529 'LIMIT ' + start
530 530
531 if (count !== undefined) query += ', ' + count 531 if (count !== undefined) query += ', ' + count
diff --git a/server/models/account/user.ts b/server/models/account/user.ts
index 7390baf91..8f7c9b013 100644
--- a/server/models/account/user.ts
+++ b/server/models/account/user.ts
@@ -1,23 +1,16 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2 2import { hasUserRight, USER_ROLE_LABELS, UserRight } from '../../../shared'
3import { getSort, addMethodsToModel } from '../utils'
4import { 3import {
5 cryptPassword,
6 comparePassword, 4 comparePassword,
5 cryptPassword,
6 isUserDisplayNSFWValid,
7 isUserPasswordValid, 7 isUserPasswordValid,
8 isUserRoleValid,
8 isUserUsernameValid, 9 isUserUsernameValid,
9 isUserDisplayNSFWValid, 10 isUserVideoQuotaValid
10 isUserVideoQuotaValid,
11 isUserRoleValid
12} from '../../helpers' 11} from '../../helpers'
13import { UserRight, USER_ROLE_LABELS, hasUserRight } from '../../../shared' 12import { addMethodsToModel, getSort } from '../utils'
14 13import { UserAttributes, UserInstance, UserMethods } from './user-interface'
15import {
16 UserInstance,
17 UserAttributes,
18
19 UserMethods
20} from './user-interface'
21 14
22let User: Sequelize.Model<UserInstance, UserAttributes> 15let User: Sequelize.Model<UserInstance, UserAttributes>
23let isPasswordMatch: UserMethods.IsPasswordMatch 16let isPasswordMatch: UserMethods.IsPasswordMatch
diff --git a/server/models/pod/pod.ts b/server/models/pod/pod.ts
index 7c8b49bf8..6d270ad7f 100644
--- a/server/models/pod/pod.ts
+++ b/server/models/pod/pod.ts
@@ -63,8 +63,6 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
63 ) 63 )
64 64
65 const classMethods = [ 65 const classMethods = [
66 associate,
67
68 countAll, 66 countAll,
69 incrementScores, 67 incrementScores,
70 list, 68 list,
@@ -98,14 +96,6 @@ toFormattedJSON = function (this: PodInstance) {
98 96
99// ------------------------------ Statics ------------------------------ 97// ------------------------------ Statics ------------------------------
100 98
101function associate (models) {
102 Pod.belongsToMany(models.Request, {
103 foreignKey: 'podId',
104 through: models.RequestToPod,
105 onDelete: 'cascade'
106 })
107}
108
109countAll = function () { 99countAll = function () {
110 return Pod.count() 100 return Pod.count()
111} 101}
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts
index 919ec916d..6d70f2aa2 100644
--- a/server/models/video/video-channel.ts
+++ b/server/models/video/video-channel.ts
@@ -10,6 +10,8 @@ import {
10 VideoChannelMethods 10 VideoChannelMethods
11} from './video-channel-interface' 11} from './video-channel-interface'
12import { sendDeleteVideoChannel } from '../../lib/activitypub/send-request' 12import { sendDeleteVideoChannel } from '../../lib/activitypub/send-request'
13import { isVideoChannelUrlValid } from '../../helpers/custom-validators/video-channels'
14import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
13 15
14let VideoChannel: Sequelize.Model<VideoChannelInstance, VideoChannelAttributes> 16let VideoChannel: Sequelize.Model<VideoChannelInstance, VideoChannelAttributes>
15let toFormattedJSON: VideoChannelMethods.ToFormattedJSON 17let toFormattedJSON: VideoChannelMethods.ToFormattedJSON
@@ -65,10 +67,13 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
65 defaultValue: false 67 defaultValue: false
66 }, 68 },
67 url: { 69 url: {
68 type: DataTypes.STRING, 70 type: DataTypes.STRING(CONSTRAINTS_FIELDS.VIDEO_CHANNELS.URL.max),
69 allowNull: false, 71 allowNull: false,
70 validate: { 72 validate: {
71 isUrl: true 73 urlValid: value => {
74 const res = isVideoChannelUrlValid(value)
75 if (res === false) throw new Error('Video channel URL is not valid.')
76 }
72 } 77 }
73 } 78 }
74 }, 79 },
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index ca71da375..dd73dd7ca 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -46,6 +46,7 @@ import { TagInstance } from './tag-interface'
46import { VideoFileInstance, VideoFileModel } from './video-file-interface' 46import { VideoFileInstance, VideoFileModel } from './video-file-interface'
47import { VideoAttributes, VideoInstance, VideoMethods } from './video-interface' 47import { VideoAttributes, VideoInstance, VideoMethods } from './video-interface'
48import { sendDeleteVideo } from '../../lib/activitypub/send-request' 48import { sendDeleteVideo } from '../../lib/activitypub/send-request'
49import { isVideoUrlValid } from '../../helpers/custom-validators/videos'
49 50
50const Buffer = safeBuffer.Buffer 51const Buffer = safeBuffer.Buffer
51 52
@@ -220,10 +221,13 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
220 defaultValue: false 221 defaultValue: false
221 }, 222 },
222 url: { 223 url: {
223 type: DataTypes.STRING, 224 type: DataTypes.STRING(CONSTRAINTS_FIELDS.VIDEOS.URL.max),
224 allowNull: false, 225 allowNull: false,
225 validate: { 226 validate: {
226 isUrl: true 227 urlValid: value => {
228 const res = isVideoUrlValid(value)
229 if (res === false) throw new Error('Video URL is not valid.')
230 }
227 } 231 }
228 } 232 }
229 }, 233 },