Fix issues on server start
This commit is contained in:
parent
1e1265b36c
commit
e34c85e527
14 changed files with 96 additions and 65 deletions
|
@ -3,6 +3,7 @@ import * as validator from 'validator'
|
|||
import { exists, isUUIDValid } from '../misc'
|
||||
import { isActivityPubUrlValid } from './misc'
|
||||
import { isUserUsernameValid } from '../users'
|
||||
import { CONSTRAINTS_FIELDS } from '../../../initializers/constants'
|
||||
|
||||
function isAccountEndpointsObjectValid (endpointObject: any) {
|
||||
return isAccountSharedInboxValid(endpointObject.sharedInbox)
|
||||
|
@ -34,7 +35,8 @@ function isAccountPublicKeyValid (publicKey: string) {
|
|||
return exists(publicKey) &&
|
||||
typeof publicKey === 'string' &&
|
||||
publicKey.startsWith('-----BEGIN PUBLIC KEY-----') &&
|
||||
publicKey.endsWith('-----END PUBLIC KEY-----')
|
||||
publicKey.endsWith('-----END PUBLIC KEY-----') &&
|
||||
validator.isLength(publicKey, CONSTRAINTS_FIELDS.ACCOUNTS.PUBLIC_KEY)
|
||||
}
|
||||
|
||||
function isAccountIdValid (id: string) {
|
||||
|
@ -73,7 +75,8 @@ function isAccountPrivateKeyValid (privateKey: string) {
|
|||
return exists(privateKey) &&
|
||||
typeof privateKey === 'string' &&
|
||||
privateKey.startsWith('-----BEGIN RSA PRIVATE KEY-----') &&
|
||||
privateKey.endsWith('-----END RSA PRIVATE KEY-----')
|
||||
privateKey.endsWith('-----END RSA PRIVATE KEY-----') &&
|
||||
validator.isLength(privateKey, CONSTRAINTS_FIELDS.ACCOUNTS.PRIVATE_KEY)
|
||||
}
|
||||
|
||||
function isRemoteAccountValid (remoteAccount: any) {
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import * as validator from 'validator'
|
||||
import { exists } from '../misc'
|
||||
import { isTestInstance } from '../../core-utils'
|
||||
import { CONSTRAINTS_FIELDS } from '../../../initializers/constants'
|
||||
|
||||
function isActivityPubUrlValid (url: string) {
|
||||
const isURLOptions = {
|
||||
|
@ -9,7 +12,12 @@ function isActivityPubUrlValid (url: string) {
|
|||
protocols: [ 'http', 'https' ]
|
||||
}
|
||||
|
||||
return exists(url) && validator.isURL(url, isURLOptions)
|
||||
// We validate 'localhost', so we don't have the top level domain
|
||||
if (isTestInstance()) {
|
||||
isURLOptions.require_tld = false
|
||||
}
|
||||
|
||||
return exists(url) && validator.isURL(url, isURLOptions) && validator.isLength(url, CONSTRAINTS_FIELDS.ACCOUNTS.URL)
|
||||
}
|
||||
|
||||
function isBaseActivityValid (activity: any, type: string) {
|
||||
|
|
|
@ -10,7 +10,8 @@ import {
|
|||
isVideoTruncatedDescriptionValid,
|
||||
isVideoDurationValid,
|
||||
isVideoNameValid,
|
||||
isVideoTagValid
|
||||
isVideoTagValid,
|
||||
isVideoUrlValid
|
||||
} from '../videos'
|
||||
import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../video-channels'
|
||||
import { isBaseActivityValid } from './misc'
|
||||
|
@ -93,7 +94,7 @@ function isRemoteVideoContentValid (mediaType: string, content: string) {
|
|||
|
||||
function isRemoteVideoIconValid (icon: any) {
|
||||
return icon.type === 'Image' &&
|
||||
validator.isURL(icon.url) &&
|
||||
isVideoUrlValid(icon.url) &&
|
||||
icon.mediaType === 'image/jpeg' &&
|
||||
validator.isInt(icon.width, { min: 0 }) &&
|
||||
validator.isInt(icon.height, { min: 0 })
|
||||
|
@ -111,7 +112,7 @@ function setValidRemoteVideoUrls (video: any) {
|
|||
function isRemoteVideoUrlValid (url: any) {
|
||||
return url.type === 'Link' &&
|
||||
ACTIVITY_PUB.VIDEO_URL_MIME_TYPES.indexOf(url.mimeType) !== -1 &&
|
||||
validator.isURL(url.url) &&
|
||||
isVideoUrlValid(url.url) &&
|
||||
validator.isInt(url.width, { min: 0 }) &&
|
||||
validator.isInt(url.size, { min: 0 })
|
||||
}
|
||||
|
|
|
@ -8,9 +8,14 @@ import { database as db, CONSTRAINTS_FIELDS } from '../../initializers'
|
|||
import { VideoChannelInstance } from '../../models'
|
||||
import { logger } from '../logger'
|
||||
import { exists } from './misc'
|
||||
import { isActivityPubUrlValid } from './index'
|
||||
|
||||
const VIDEO_CHANNELS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_CHANNELS
|
||||
|
||||
function isVideoChannelUrlValid (value: string) {
|
||||
return isActivityPubUrlValid(value)
|
||||
}
|
||||
|
||||
function isVideoChannelDescriptionValid (value: string) {
|
||||
return value === null || validator.isLength(value, VIDEO_CHANNELS_CONSTRAINTS_FIELDS.DESCRIPTION)
|
||||
}
|
||||
|
@ -53,5 +58,6 @@ export {
|
|||
isVideoChannelDescriptionValid,
|
||||
isVideoChannelNameValid,
|
||||
isVideoChannelUUIDValid,
|
||||
checkVideoChannelExists
|
||||
checkVideoChannelExists,
|
||||
isVideoChannelUrlValid
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import { isArray, exists } from './misc'
|
|||
import { VideoInstance } from '../../models'
|
||||
import { logger } from '../../helpers'
|
||||
import { VideoRateType } from '../../../shared'
|
||||
import { isActivityPubUrlValid } from './activitypub/misc'
|
||||
|
||||
const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS
|
||||
const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES
|
||||
|
@ -33,6 +34,10 @@ function isRemoteVideoCategoryValid (value: string) {
|
|||
return validator.isInt('' + value)
|
||||
}
|
||||
|
||||
function isVideoUrlValid (value: string) {
|
||||
return isActivityPubUrlValid(value)
|
||||
}
|
||||
|
||||
function isVideoLicenceValid (value: number) {
|
||||
return VIDEO_LICENCES[value] !== undefined
|
||||
}
|
||||
|
@ -219,5 +224,6 @@ export {
|
|||
isVideoTagValid,
|
||||
isRemoteVideoCategoryValid,
|
||||
isRemoteVideoLicenceValid,
|
||||
isVideoUrlValid,
|
||||
isRemoteVideoLanguageValid
|
||||
}
|
||||
|
|
|
@ -121,7 +121,8 @@ const CONSTRAINTS_FIELDS = {
|
|||
},
|
||||
VIDEO_CHANNELS: {
|
||||
NAME: { min: 3, max: 120 }, // Length
|
||||
DESCRIPTION: { min: 3, max: 250 } // Length
|
||||
DESCRIPTION: { min: 3, max: 250 }, // Length
|
||||
URL: { min: 3, max: 2000 } // Length
|
||||
},
|
||||
VIDEOS: {
|
||||
NAME: { min: 3, max: 120 }, // Length
|
||||
|
@ -137,7 +138,13 @@ const CONSTRAINTS_FIELDS = {
|
|||
VIEWS: { min: 0 },
|
||||
LIKES: { min: 0 },
|
||||
DISLIKES: { min: 0 },
|
||||
FILE_SIZE: { min: 10, max: 1024 * 1024 * 1024 * 3 /* 3Go */ }
|
||||
FILE_SIZE: { min: 10, max: 1024 * 1024 * 1024 * 3 /* 3Go */ },
|
||||
URL: { min: 3, max: 2000 } // Length
|
||||
},
|
||||
ACCOUNTS: {
|
||||
PUBLIC_KEY: { min: 10, max: 5000 }, // Length
|
||||
PRIVATE_KEY: { min: 10, max: 5000 }, // Length
|
||||
URL: { min: 3, max: 2000 } // Length
|
||||
},
|
||||
VIDEO_EVENTS: {
|
||||
COUNT: { min: 0 }
|
||||
|
|
|
@ -1,21 +1,25 @@
|
|||
import * as passwordGenerator from 'password-generator'
|
||||
import { UserRole } from '../../shared'
|
||||
import { logger, mkdirpPromise, rimrafPromise } from '../helpers'
|
||||
import { createPrivateAndPublicKeys } from '../helpers/peertube-crypto'
|
||||
import { createUserAccountAndChannel } from '../lib'
|
||||
import { createLocalAccount } from '../lib/user'
|
||||
import { clientsExist, usersExist } from './checker'
|
||||
import { CACHE, CONFIG, LAST_MIGRATION_VERSION } from './constants'
|
||||
|
||||
import { database as db } from './database'
|
||||
import { createLocalAccount } from '../lib/user'
|
||||
|
||||
async function installApplication () {
|
||||
try {
|
||||
await db.sequelize.sync()
|
||||
await removeCacheDirectories()
|
||||
await createDirectoriesIfNotExist()
|
||||
await createOAuthClientIfNotExist()
|
||||
await createOAuthAdminIfNotExist()
|
||||
await createApplicationIfNotExist()
|
||||
} catch (err) {
|
||||
logger.error('Cannot install application.', err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
@ -16,8 +16,9 @@ async function createUserAccountAndChannel (user: UserInstance, validateUser = t
|
|||
const userCreated = await user.save(userOptions)
|
||||
const accountCreated = await createLocalAccount(user.username, user.id, null, t)
|
||||
|
||||
const videoChannelName = `Default ${userCreated.username} channel`
|
||||
const videoChannelInfo = {
|
||||
name: `Default ${userCreated.username} channel`
|
||||
name: videoChannelName
|
||||
}
|
||||
const videoChannel = await createVideoChannel(videoChannelInfo, accountCreated, t)
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import { logger } from '../helpers'
|
|||
import { AccountInstance } from '../models'
|
||||
import { VideoChannelCreate } from '../../shared/models'
|
||||
import { sendCreateVideoChannel } from './activitypub/send-request'
|
||||
import { getActivityPubUrl } from '../helpers/activitypub'
|
||||
|
||||
async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountInstance, t: Sequelize.Transaction) {
|
||||
const videoChannelData = {
|
||||
|
@ -15,6 +16,8 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account
|
|||
}
|
||||
|
||||
const videoChannel = db.VideoChannel.build(videoChannelData)
|
||||
videoChannel.set('url', getActivityPubUrl('videoChannel', videoChannel.uuid))
|
||||
|
||||
const options = { transaction: t }
|
||||
|
||||
const videoChannelCreated = await videoChannel.save(options)
|
||||
|
|
|
@ -22,8 +22,8 @@ import {
|
|||
|
||||
AccountMethods
|
||||
} from './account-interface'
|
||||
import LoadApplication = AccountMethods.LoadApplication
|
||||
import { sendDeleteAccount } from '../../lib/activitypub/send-request'
|
||||
import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
|
||||
|
||||
let Account: Sequelize.Model<AccountInstance, AccountAttributes>
|
||||
let loadAccountByPodAndUUID: AccountMethods.LoadAccountByPodAndUUID
|
||||
|
@ -60,14 +60,14 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
|
|||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
validate: {
|
||||
usernameValid: value => {
|
||||
nameValid: value => {
|
||||
const res = isUserUsernameValid(value)
|
||||
if (res === false) throw new Error('Username is not valid.')
|
||||
if (res === false) throw new Error('Name is not valid.')
|
||||
}
|
||||
}
|
||||
},
|
||||
url: {
|
||||
type: DataTypes.STRING,
|
||||
type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max),
|
||||
allowNull: false,
|
||||
validate: {
|
||||
urlValid: value => {
|
||||
|
@ -77,7 +77,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
|
|||
}
|
||||
},
|
||||
publicKey: {
|
||||
type: DataTypes.STRING,
|
||||
type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.PUBLIC_KEY.max),
|
||||
allowNull: false,
|
||||
validate: {
|
||||
publicKeyValid: value => {
|
||||
|
@ -87,7 +87,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
|
|||
}
|
||||
},
|
||||
privateKey: {
|
||||
type: DataTypes.STRING,
|
||||
type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.PRIVATE_KEY.max),
|
||||
allowNull: false,
|
||||
validate: {
|
||||
privateKeyValid: value => {
|
||||
|
@ -110,14 +110,14 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
|
|||
type: DataTypes.INTEGER,
|
||||
allowNull: false,
|
||||
validate: {
|
||||
followersCountValid: value => {
|
||||
followingCountValid: value => {
|
||||
const res = isAccountFollowingCountValid(value)
|
||||
if (res === false) throw new Error('Following count is not valid.')
|
||||
}
|
||||
}
|
||||
},
|
||||
inboxUrl: {
|
||||
type: DataTypes.STRING,
|
||||
type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max),
|
||||
allowNull: false,
|
||||
validate: {
|
||||
inboxUrlValid: value => {
|
||||
|
@ -127,7 +127,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
|
|||
}
|
||||
},
|
||||
outboxUrl: {
|
||||
type: DataTypes.STRING,
|
||||
type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max),
|
||||
allowNull: false,
|
||||
validate: {
|
||||
outboxUrlValid: value => {
|
||||
|
@ -137,7 +137,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
|
|||
}
|
||||
},
|
||||
sharedInboxUrl: {
|
||||
type: DataTypes.STRING,
|
||||
type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max),
|
||||
allowNull: false,
|
||||
validate: {
|
||||
sharedInboxUrlValid: value => {
|
||||
|
@ -147,7 +147,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
|
|||
}
|
||||
},
|
||||
followersUrl: {
|
||||
type: DataTypes.STRING,
|
||||
type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max),
|
||||
allowNull: false,
|
||||
validate: {
|
||||
followersUrlValid: value => {
|
||||
|
@ -157,7 +157,7 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
|
|||
}
|
||||
},
|
||||
followingUrl: {
|
||||
type: DataTypes.STRING,
|
||||
type: DataTypes.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max),
|
||||
allowNull: false,
|
||||
validate: {
|
||||
followingUrlValid: value => {
|
||||
|
@ -241,7 +241,7 @@ function associate (models) {
|
|||
|
||||
Account.belongsTo(models.Application, {
|
||||
foreignKey: {
|
||||
name: 'userId',
|
||||
name: 'applicationId',
|
||||
allowNull: true
|
||||
},
|
||||
onDelete: 'cascade'
|
||||
|
@ -256,7 +256,7 @@ function associate (models) {
|
|||
hooks: true
|
||||
})
|
||||
|
||||
Account.hasMany(models.AccountFollower, {
|
||||
Account.hasMany(models.AccountFollow, {
|
||||
foreignKey: {
|
||||
name: 'accountId',
|
||||
allowNull: false
|
||||
|
@ -265,7 +265,7 @@ function associate (models) {
|
|||
onDelete: 'cascade'
|
||||
})
|
||||
|
||||
Account.hasMany(models.AccountFollower, {
|
||||
Account.hasMany(models.AccountFollow, {
|
||||
foreignKey: {
|
||||
name: 'targetAccountId',
|
||||
allowNull: false
|
||||
|
@ -329,7 +329,7 @@ getFollowerSharedInboxUrls = function (this: AccountInstance) {
|
|||
attributes: [ 'sharedInboxUrl' ],
|
||||
include: [
|
||||
{
|
||||
model: Account['sequelize'].models.AccountFollower,
|
||||
model: Account['sequelize'].models.AccountFollow,
|
||||
where: {
|
||||
targetAccountId: this.id
|
||||
}
|
||||
|
@ -523,9 +523,9 @@ async function createListAcceptedFollowForApiQuery (type: 'followers' | 'followi
|
|||
|
||||
for (const selection of selections) {
|
||||
let query = 'SELECT ' + selection + ' FROM "Account" ' +
|
||||
'INNER JOIN "AccountFollower" ON "AccountFollower"."' + firstJoin + '" = "Account"."id" ' +
|
||||
'INNER JOIN "AccountFollow" ON "AccountFollow"."' + firstJoin + '" = "Account"."id" ' +
|
||||
'INNER JOIN "Account" AS "Follows" ON "Followers"."id" = "Follows"."' + secondJoin + '" ' +
|
||||
'WHERE "Account"."id" = $id AND "AccountFollower"."state" = \'accepted\' ' +
|
||||
'WHERE "Account"."id" = $id AND "AccountFollow"."state" = \'accepted\' ' +
|
||||
'LIMIT ' + start
|
||||
|
||||
if (count !== undefined) query += ', ' + count
|
||||
|
|
|
@ -1,23 +1,16 @@
|
|||
import * as Sequelize from 'sequelize'
|
||||
|
||||
import { getSort, addMethodsToModel } from '../utils'
|
||||
import { hasUserRight, USER_ROLE_LABELS, UserRight } from '../../../shared'
|
||||
import {
|
||||
cryptPassword,
|
||||
comparePassword,
|
||||
isUserPasswordValid,
|
||||
isUserUsernameValid,
|
||||
cryptPassword,
|
||||
isUserDisplayNSFWValid,
|
||||
isUserVideoQuotaValid,
|
||||
isUserRoleValid
|
||||
isUserPasswordValid,
|
||||
isUserRoleValid,
|
||||
isUserUsernameValid,
|
||||
isUserVideoQuotaValid
|
||||
} from '../../helpers'
|
||||
import { UserRight, USER_ROLE_LABELS, hasUserRight } from '../../../shared'
|
||||
|
||||
import {
|
||||
UserInstance,
|
||||
UserAttributes,
|
||||
|
||||
UserMethods
|
||||
} from './user-interface'
|
||||
import { addMethodsToModel, getSort } from '../utils'
|
||||
import { UserAttributes, UserInstance, UserMethods } from './user-interface'
|
||||
|
||||
let User: Sequelize.Model<UserInstance, UserAttributes>
|
||||
let isPasswordMatch: UserMethods.IsPasswordMatch
|
||||
|
|
|
@ -63,8 +63,6 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
|
|||
)
|
||||
|
||||
const classMethods = [
|
||||
associate,
|
||||
|
||||
countAll,
|
||||
incrementScores,
|
||||
list,
|
||||
|
@ -98,14 +96,6 @@ toFormattedJSON = function (this: PodInstance) {
|
|||
|
||||
// ------------------------------ Statics ------------------------------
|
||||
|
||||
function associate (models) {
|
||||
Pod.belongsToMany(models.Request, {
|
||||
foreignKey: 'podId',
|
||||
through: models.RequestToPod,
|
||||
onDelete: 'cascade'
|
||||
})
|
||||
}
|
||||
|
||||
countAll = function () {
|
||||
return Pod.count()
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ import {
|
|||
VideoChannelMethods
|
||||
} from './video-channel-interface'
|
||||
import { sendDeleteVideoChannel } from '../../lib/activitypub/send-request'
|
||||
import { isVideoChannelUrlValid } from '../../helpers/custom-validators/video-channels'
|
||||
import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
|
||||
|
||||
let VideoChannel: Sequelize.Model<VideoChannelInstance, VideoChannelAttributes>
|
||||
let toFormattedJSON: VideoChannelMethods.ToFormattedJSON
|
||||
|
@ -65,10 +67,13 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
|
|||
defaultValue: false
|
||||
},
|
||||
url: {
|
||||
type: DataTypes.STRING,
|
||||
type: DataTypes.STRING(CONSTRAINTS_FIELDS.VIDEO_CHANNELS.URL.max),
|
||||
allowNull: false,
|
||||
validate: {
|
||||
isUrl: true
|
||||
urlValid: value => {
|
||||
const res = isVideoChannelUrlValid(value)
|
||||
if (res === false) throw new Error('Video channel URL is not valid.')
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -46,6 +46,7 @@ import { TagInstance } from './tag-interface'
|
|||
import { VideoFileInstance, VideoFileModel } from './video-file-interface'
|
||||
import { VideoAttributes, VideoInstance, VideoMethods } from './video-interface'
|
||||
import { sendDeleteVideo } from '../../lib/activitypub/send-request'
|
||||
import { isVideoUrlValid } from '../../helpers/custom-validators/videos'
|
||||
|
||||
const Buffer = safeBuffer.Buffer
|
||||
|
||||
|
@ -220,10 +221,13 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
|
|||
defaultValue: false
|
||||
},
|
||||
url: {
|
||||
type: DataTypes.STRING,
|
||||
type: DataTypes.STRING(CONSTRAINTS_FIELDS.VIDEOS.URL.max),
|
||||
allowNull: false,
|
||||
validate: {
|
||||
isUrl: true
|
||||
urlValid: value => {
|
||||
const res = isVideoUrlValid(value)
|
||||
if (res === false) throw new Error('Video URL is not valid.')
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue