1
0
Fork 0

Fix issues on server start

This commit is contained in:
Chocobozzz 2017-11-14 10:57:56 +01:00
parent 1e1265b36c
commit e34c85e527
No known key found for this signature in database
GPG key ID: 583A612D890159BE
14 changed files with 96 additions and 65 deletions

View file

@ -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) {

View file

@ -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) {

View file

@ -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 })
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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 }

View file

@ -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
}
}
// ---------------------------------------------------------------------------

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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()
}

View file

@ -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.')
}
}
}
},

View file

@ -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.')
}
}
}
},