1
0
Fork 0

expliciting type checks and predicates (server only)

This commit is contained in:
Rigel Kent 2018-07-25 22:01:25 +02:00
parent 5f7021c33d
commit c1e791bad0
No known key found for this signature in database
GPG Key ID: EA12971B0E438F36
34 changed files with 127 additions and 84 deletions

View File

@ -67,8 +67,8 @@ async function activityPubCollectionPagination (url: string, handler: ActivityPu
const { start, count } = pageToStartAndCount(page, ACTIVITY_PUB.COLLECTION_ITEMS_PER_PAGE)
const result = await handler(start, count)
let next: string
let prev: string
let next: string | undefined
let prev: string | undefined
// Assert page is a number
page = parseInt(page, 10)

View File

@ -42,7 +42,7 @@ function root () {
const paths = [ __dirname, '..', '..' ]
// We are under /dist directory
if (process.mainModule.filename.endsWith('.ts') === false) {
if (process.mainModule && process.mainModule.filename.endsWith('.ts') === false) {
paths.push('..')
}
@ -143,6 +143,7 @@ const renamePromise = promisify2WithVoid<string, string>(rename)
const writeFilePromise = promisify2WithVoid<string, any>(writeFile)
const readdirPromise = promisify1<string, string[]>(readdir)
const mkdirpPromise = promisify1<string, string>(mkdirp)
// we cannot modify the Promise types, so we should make the promisify instance check mkdirp
const pseudoRandomBytesPromise = promisify1<number, Buffer>(pseudoRandomBytes)
const createPrivateKey = promisify1<number, { key: string }>(pem.createPrivateKey)
const getPublicKey = promisify1<string, { publicKey: string }>(pem.getPublicKey)

View File

@ -51,7 +51,7 @@ function isFileValid (
files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[],
mimeTypeRegex: string,
field: string,
maxSize: number,
maxSize: number | null,
optional = false
) {
// Should have files
@ -69,7 +69,7 @@ function isFileValid (
if (!file || !file.originalname) return false
// Check size
if (maxSize && file.size > maxSize) return false
if ((maxSize !== null) && file.size > maxSize) return false
return new RegExp(`^${mimeTypeRegex}$`, 'i').test(file.mimetype)
}

View File

@ -150,7 +150,7 @@ function checkUserCanManageVideo (user: UserModel, video: VideoModel, right: Use
}
async function isVideoExist (id: string, res: Response) {
let video: VideoModel
let video: VideoModel | null
if (validator.isInt(id)) {
video = await VideoModel.loadAndPopulateAccountAndServerAndTags(+id)
@ -158,7 +158,7 @@ async function isVideoExist (id: string, res: Response) {
video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(id)
}
if (!video) {
if (video && video !== null) {
res.status(404)
.json({ error: 'Video not found' })
.end()
@ -173,7 +173,7 @@ async function isVideoExist (id: string, res: Response) {
async function isVideoChannelOfAccountExist (channelId: number, user: UserModel, res: Response) {
if (user.hasRight(UserRight.UPDATE_ANY_VIDEO) === true) {
const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId)
if (!videoChannel) {
if (videoChannel && videoChannel !== null) {
res.status(400)
.json({ error: 'Unknown video video channel on this instance.' })
.end()
@ -186,7 +186,7 @@ async function isVideoChannelOfAccountExist (channelId: number, user: UserModel,
}
const videoChannel = await VideoChannelModel.loadByIdAndAccount(channelId, user.Account.id)
if (!videoChannel) {
if (videoChannel && videoChannel !== null) {
res.status(400)
.json({ error: 'Unknown video video channel for this account.' })
.end()

View File

@ -64,7 +64,7 @@ function createReqFiles (
}
})
const fields = []
let fields: { name: string, maxCount: number }[] = []
for (const fieldName of fieldNames) {
fields.push({
name: fieldName,

View File

@ -80,7 +80,8 @@ const logger = winston.createLogger({
function bunyanLogFactory (level: string) {
return function () {
let meta = null
let args = [].concat(arguments)
let args: any[] = []
args.concat(arguments)
if (arguments[ 0 ] instanceof Error) {
meta = arguments[ 0 ].toString()

View File

@ -52,7 +52,7 @@ async function isSignupAllowed () {
function isSignupAllowedForCurrentIP (ip: string) {
const addr = ipaddr.parse(ip)
let excludeList = [ 'blacklist' ]
let matched: string
let matched = ''
// if there is a valid, non-empty whitelist, we exclude all unknown adresses too
if (CONFIG.SIGNUP.FILTERS.CIDR.WHITELIST.filter(cidr => isCidr(cidr)).length > 0) {
@ -144,6 +144,7 @@ let serverActor: ActorModel
async function getServerActor () {
if (serverActor === undefined) {
const application = await ApplicationModel.load()
if (!application) throw Error('Could not application.')
serverActor = application.Account.Actor
}

View File

@ -52,7 +52,7 @@ function createDirectoriesIfNotExist () {
const cacheDirectories = Object.keys(CACHE)
.map(k => CACHE[k].DIRECTORY)
const tasks = []
const tasks: Promise<string>[] = []
for (const key of Object.keys(storage)) {
const dir = storage[key]
tasks.push(mkdirpPromise(dir))

View File

@ -1,5 +1,6 @@
import * as Sequelize from 'sequelize'
import * as Promise from 'bluebird'
import { Migration } from '../../models/migrations'
function up (utils: {
transaction: Sequelize.Transaction,
@ -12,7 +13,7 @@ function up (utils: {
type: Sequelize.STRING(400),
allowNull: false,
defaultValue: ''
}
} as Migration.String
return q.addColumn('Pods', 'email', data)
.then(() => {

View File

@ -1,5 +1,6 @@
import * as Sequelize from 'sequelize'
import * as Promise from 'bluebird'
import { Migration } from '../../models/migrations'
function up (utils: {
transaction: Sequelize.Transaction,
@ -12,7 +13,7 @@ function up (utils: {
type: Sequelize.STRING(400),
allowNull: false,
defaultValue: ''
}
} as Migration.String
return q.addColumn('Users', 'email', data)
.then(() => {
const query = 'UPDATE "Users" SET "email" = CONCAT("username", \'@example.com\')'

View File

@ -1,5 +1,6 @@
import * as Sequelize from 'sequelize'
import * as Promise from 'bluebird'
import { Migration } from '../../models/migrations'
function up (utils: {
transaction: Sequelize.Transaction,
@ -12,7 +13,7 @@ function up (utils: {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 0
}
} as Migration.Integer
return q.addColumn('Videos', 'category', data)
.then(() => {

View File

@ -1,5 +1,6 @@
import * as Sequelize from 'sequelize'
import * as Promise from 'bluebird'
import { Migration } from '../../models/migrations'
function up (utils: {
transaction: Sequelize.Transaction,
@ -12,7 +13,7 @@ function up (utils: {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 0
}
} as Migration.Integer
return q.addColumn('Videos', 'licence', data)
.then(() => {

View File

@ -1,5 +1,6 @@
import * as Sequelize from 'sequelize'
import * as Promise from 'bluebird'
import { Migration } from '../../models/migrations'
function up (utils: {
transaction: Sequelize.Transaction,
@ -12,7 +13,7 @@ function up (utils: {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: false
}
} as Migration.Boolean
return q.addColumn('Videos', 'nsfw', data)
.then(() => {

View File

@ -24,7 +24,7 @@ function up (utils: {
return utils.sequelize.query(query)
})
.then(() => {
dataUUID.defaultValue = null
dataUUID.defaultValue = null // FIXME:default value cannot be null if string
return q.changeColumn('Videos', 'uuid', dataUUID)
})

View File

@ -1,5 +1,6 @@
import * as Sequelize from 'sequelize'
import * as Promise from 'bluebird'
import { Migration } from '../../models/migrations'
function up (utils: {
transaction: Sequelize.Transaction,
@ -13,7 +14,7 @@ function up (utils: {
type: Sequelize.BIGINT,
allowNull: false,
defaultValue: -1
}
} as Migration.BigInteger
return q.addColumn('Users', 'videoQuota', data)
.then(() => {

View File

@ -1,4 +1,5 @@
import * as Sequelize from 'sequelize'
import { Migration } from '../../models/migrations'
async function up (utils: {
transaction: Sequelize.Transaction,
@ -9,7 +10,7 @@ async function up (utils: {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: true
}
} as Migration.Boolean
await utils.queryInterface.addColumn('video', 'commentsEnabled', data)
data.defaultValue = null

View File

@ -2,6 +2,7 @@ import * as Sequelize from 'sequelize'
import { createClient } from 'redis'
import { CONFIG } from '../constants'
import { JobQueue } from '../../lib/job-queue'
import { Redis } from '../../lib/redis'
import { initDatabaseModels } from '../database'
async function up (utils: {
@ -12,11 +13,7 @@ async function up (utils: {
await initDatabaseModels(false)
return new Promise((res, rej) => {
const client = createClient({
host: CONFIG.REDIS.HOSTNAME,
port: CONFIG.REDIS.PORT,
db: CONFIG.REDIS.DB
})
const client = createClient(Redis.getRedisClient())
const jobsPrefix = 'q-' + CONFIG.WEBSERVER.HOST
@ -36,7 +33,7 @@ async function up (utils: {
return res({ type: job.type, payload: parsedData })
} catch (err) {
console.error('Cannot parse data %s.', job.data)
return res(null)
return res(undefined)
}
})
})

View File

@ -11,7 +11,7 @@ async function migrate () {
// The installer will do that
if (tables.length === 0) return
let actualVersion: number = null
let actualVersion: number | null = null
const [ rows ] = await sequelizeTypescript.query('SELECT "migrationVersion" FROM "application"')
if (rows && rows[0] && rows[0].migrationVersion) {

View File

@ -20,7 +20,7 @@ function getVideoCommentAudience (
isOrigin = false
) {
const to = [ ACTIVITY_PUB.PUBLIC ]
const cc = []
const cc: string[] = []
// Owner of the video we comment
if (isOrigin === false) {
@ -60,8 +60,8 @@ function getAudience (actorSender: ActorModel, isPublic = true) {
}
function buildAudience (followerUrls: string[], isPublic = true) {
let to = []
let cc = []
let to: string[] = []
let cc: string[] = []
if (isPublic) {
to = [ ACTIVITY_PUB.PUBLIC ]

View File

@ -88,17 +88,17 @@ async function videoActivityObjectToDBAttributes (
const privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPrivacy.PUBLIC : VideoPrivacy.UNLISTED
const duration = videoObject.duration.replace(/[^\d]+/, '')
let language: string = null
let language: string | undefined
if (videoObject.language) {
language = videoObject.language.identifier
}
let category: number = null
let category: number | undefined
if (videoObject.category) {
category = parseInt(videoObject.category.identifier, 10)
}
let licence: number = null
let licence: number | undefined
if (videoObject.licence) {
licence = parseInt(videoObject.licence.identifier, 10)
}
@ -143,7 +143,7 @@ function videoFileActivityUrlToDBAttributes (videoCreated: VideoModel, videoObje
throw new Error('Cannot find video files for ' + videoCreated.url)
}
const attributes = []
const attributes: VideoFileModel[] = []
for (const fileUrl of fileUrls) {
// Fetch associated magnet uri
const magnet = videoObject.url.find(u => {
@ -153,7 +153,11 @@ function videoFileActivityUrlToDBAttributes (videoCreated: VideoModel, videoObje
if (!magnet) throw new Error('Cannot find associated magnet uri for file ' + fileUrl.href)
const parsed = magnetUtil.decode(magnet.href)
if (!parsed || isVideoFileInfoHashValid(parsed.infoHash) === false) throw new Error('Cannot parse magnet URI ' + magnet.href)
if (!parsed ||
(parsed.infoHash &&
(isVideoFileInfoHashValid(parsed.infoHash) === false))) {
throw new Error('Cannot parse magnet URI ' + magnet.href)
}
const attribute = {
extname: VIDEO_MIMETYPE_EXT[ fileUrl.mimeType ],
@ -161,7 +165,7 @@ function videoFileActivityUrlToDBAttributes (videoCreated: VideoModel, videoObje
resolution: fileUrl.width,
size: fileUrl.size,
videoId: videoCreated.id
}
} as VideoFileModel
attributes.push(attribute)
}

View File

@ -91,9 +91,10 @@ class Emailer {
async addVideoAbuseReport (videoId: number) {
const video = await VideoModel.load(videoId)
if (!video) throw new Error('Unknown Video id during Abuse report.')
const text = `Hi,\n\n` +
`Your instance received an abuse for video the following video ${video.url}\n\n` +
`Your instance received an abuse for the following video ${video.url}\n\n` +
`Cheers,\n` +
`PeerTube.`

View File

@ -15,7 +15,7 @@ async function computeBody (payload: { body: any, signatureActorId?: number }) {
}
async function buildSignedRequestOptions (payload: { signatureActorId?: number }) {
let actor: ActorModel
let actor: ActorModel | null
if (payload.signatureActorId) {
actor = await ActorModel.load(payload.signatureActorId)
if (!actor) throw new Error('Unknown signature actor id.')

View File

@ -28,7 +28,7 @@ async function processVideoFileImport (job: Bull.Job) {
const video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(payload.videoUUID)
// No video, maybe deleted?
if (!video) {
logger.info('Do not process job %d, video does not exist.', job.id, { videoUUID: video.uuid })
logger.info('Do not process job %d, video does not exist.', job.id)
return undefined
}
@ -45,13 +45,13 @@ async function processVideoFile (job: Bull.Job) {
const video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(payload.videoUUID)
// No video, maybe deleted?
if (!video) {
logger.info('Do not process job %d, video does not exist.', job.id, { videoUUID: video.uuid })
logger.info('Do not process job %d, video does not exist.', job.id)
return undefined
}
// Transcoding in other resolution
if (payload.resolution) {
await video.transcodeOriginalVideofile(payload.resolution, payload.isPortraitMode)
await video.transcodeOriginalVideofile(payload.resolution, payload.isPortraitMode || false)
await retryTransactionWrapper(onVideoFileTranscoderOrImportSuccess, video)
} else {

View File

@ -87,7 +87,7 @@ class JobQueue {
const queue = this.queues[obj.type]
if (queue === undefined) {
logger.error('Unknown queue %s: cannot create job.', obj.type)
return
throw Error('Unknown queue, cannot create job')
}
const jobArgs: Bull.JobOptions = {

View File

@ -6,8 +6,8 @@ import { CONFIG, USER_PASSWORD_RESET_LIFETIME, VIDEO_VIEW_LIFETIME } from '../in
type CachedRoute = {
body: string,
contentType?: string
statusCode?: string
contentType: string
statusCode: string
}
class Redis {
@ -75,11 +75,12 @@ class Redis {
}
setCachedRoute (req: express.Request, body: any, lifetime: number, contentType?: string, statusCode?: number) {
const cached: CachedRoute = {
body: body.toString(),
contentType,
statusCode: statusCode.toString()
}
const cached: CachedRoute = Object.assign({}, {
body: body.toString()
},
(contentType) ? { contentType } : null,
(statusCode) ? { statusCode: statusCode.toString() } : null
)
return this.setObject(this.buildCachedRouteKey(req), cached, lifetime)
}

View File

@ -6,6 +6,7 @@ import { UserModel } from '../models/account/user'
import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub'
import { createVideoChannel } from './video-channel'
import { VideoChannelModel } from '../models/video/video-channel'
import { FilteredModelAttributes } from 'sequelize-typescript/lib/models/Model'
async function createUserAccountAndChannel (userToCreate: UserModel, validateUser = true) {
const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => {
@ -34,9 +35,9 @@ async function createUserAccountAndChannel (userToCreate: UserModel, validateUse
async function createLocalAccountWithoutKeys (
name: string,
userId: number,
applicationId: number,
t: Sequelize.Transaction,
userId: number | null,
applicationId: number | null,
t: Sequelize.Transaction | undefined,
type: ActivityPubActorType= 'Person'
) {
const url = getAccountActivityPubUrl(name)
@ -49,7 +50,7 @@ async function createLocalAccountWithoutKeys (
userId,
applicationId,
actorId: actorInstanceCreated.id
})
} as FilteredModelAttributes<AccountModel>)
const accountInstanceCreated = await accountInstance.save({ transaction: t })
accountInstanceCreated.Actor = actorInstanceCreated

View File

@ -9,14 +9,14 @@ import { sendCreateVideoComment } from './activitypub/send'
async function createVideoComment (obj: {
text: string,
inReplyToComment: VideoCommentModel,
inReplyToComment: VideoCommentModel | null,
video: VideoModel
account: AccountModel
}, t: Sequelize.Transaction) {
let originCommentId: number = null
let inReplyToCommentId: number = null
let originCommentId: number | null = null
let inReplyToCommentId: number | null = null
if (obj.inReplyToComment) {
if (obj.inReplyToComment && obj.inReplyToComment !== null) {
originCommentId = obj.inReplyToComment.originCommentId || obj.inReplyToComment.id
inReplyToCommentId = obj.inReplyToComment.id
}

View File

@ -15,7 +15,7 @@ function setDefaultSearchSort (req: express.Request, res: express.Response, next
}
function setBlacklistSort (req: express.Request, res: express.Response, next: express.NextFunction) {
let newSort: SortType = { sortModel: undefined, sortValue: undefined }
let newSort: SortType = { sortModel: undefined, sortValue: '' }
if (!req.query.sort) req.query.sort = '-createdAt'

View File

@ -111,7 +111,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
if (numberOfActorFollowsRemoved) logger.info('Removed bad %d actor follows.', numberOfActorFollowsRemoved)
}
static updateActorFollowsScore (goodInboxes: string[], badInboxes: string[], t: Sequelize.Transaction) {
static updateActorFollowsScore (goodInboxes: string[], badInboxes: string[], t: Sequelize.Transaction | undefined) {
if (goodInboxes.length === 0 && badInboxes.length === 0) return
logger.info('Updating %d good actor follows and %d bad actor follows scores.', goodInboxes.length, badInboxes.length)
@ -344,7 +344,7 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
}
}
private static incrementScores (inboxUrls: string[], value: number, t: Sequelize.Transaction) {
private static incrementScores (inboxUrls: string[], value: number, t: Sequelize.Transaction | undefined) {
const inboxUrlsString = inboxUrls.map(url => `'${url}'`).join(',')
const query = `UPDATE "actorFollow" SET "score" = LEAST("score" + ${value}, ${ACTOR_FOLLOW_SCORE.MAX}) ` +
@ -354,10 +354,10 @@ export class ActorFollowModel extends Model<ActorFollowModel> {
'WHERE "actor"."inboxUrl" IN (' + inboxUrlsString + ') OR "actor"."sharedInboxUrl" IN (' + inboxUrlsString + ')' +
')'
const options = {
const options = t ? {
type: Sequelize.QueryTypes.BULKUPDATE,
transaction: t
}
} : undefined
return ActorFollowModel.sequelize.query(query, options)
}

View File

@ -0,0 +1,23 @@
import * as Sequelize from 'sequelize'
declare namespace Migration {
interface Boolean extends Sequelize.DefineAttributeColumnOptions {
defaultValue: boolean | null
}
interface String extends Sequelize.DefineAttributeColumnOptions {
defaultValue: string | null
}
interface Integer extends Sequelize.DefineAttributeColumnOptions {
defaultValue: number | null
}
interface BigInteger extends Sequelize.DefineAttributeColumnOptions {
defaultValue: Sequelize.DataTypeBigInt | number | null
}
}
export {
Migration
}

View File

@ -154,9 +154,12 @@ export class OAuthTokenModel extends Model<OAuthTokenModel> {
return OAuthTokenModel.scope(ScopeNames.WITH_ACCOUNT)
.findOne(query)
.then(token => {
token['user'] = token.User
return token
if (token) {
token['user'] = token.User
return token
} else {
return new OAuthTokenModel()
}
})
}

View File

@ -156,7 +156,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
as: 'InReplyToVideoComment',
onDelete: 'CASCADE'
})
InReplyToVideoComment: VideoCommentModel
InReplyToVideoComment: VideoCommentModel | null
@ForeignKey(() => VideoModel)
@Column
@ -417,7 +417,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
toActivityPubObject (threadParentComments: VideoCommentModel[]): VideoCommentObject {
let inReplyTo: string
// New thread, so in AS we reply to the video
if (this.inReplyToCommentId === null) {
if ((this.inReplyToCommentId !== null) || (this.InReplyToVideoComment !== null)) {
inReplyTo = this.Video.url
} else {
inReplyTo = this.InReplyToVideoComment.url

View File

@ -55,5 +55,8 @@ export {
login,
serverLogin,
userLogin,
setAccessTokensToServers
setAccessTokensToServers,
Server,
Client,
User
}

View File

@ -2,7 +2,10 @@ import * as program from 'commander'
import {
getClient,
serverLogin
serverLogin,
Server,
Client,
User
} from '../tests/utils/index'
program
@ -19,22 +22,19 @@ if (
throw new Error('All arguments are required.')
}
const server = {
url: program['url'],
user: {
username: program['username'],
password: program['password']
},
client: {
id: null,
secret: null
}
}
getClient(program.url)
.then(res => {
server.client.id = res.body.client_id
server.client.secret = res.body.client_secret
const server = {
url: program['url'],
user: {
username: program['username'],
password: program['password']
} as User,
client: {
id: res.body.client_id as string,
secret: res.body.client_secret as string
} as Client
} as Server
return serverLogin(server)
})