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 { start, count } = pageToStartAndCount(page, ACTIVITY_PUB.COLLECTION_ITEMS_PER_PAGE)
const result = await handler(start, count) const result = await handler(start, count)
let next: string let next: string | undefined
let prev: string let prev: string | undefined
// Assert page is a number // Assert page is a number
page = parseInt(page, 10) page = parseInt(page, 10)

View File

@ -42,7 +42,7 @@ function root () {
const paths = [ __dirname, '..', '..' ] const paths = [ __dirname, '..', '..' ]
// We are under /dist directory // We are under /dist directory
if (process.mainModule.filename.endsWith('.ts') === false) { if (process.mainModule && process.mainModule.filename.endsWith('.ts') === false) {
paths.push('..') paths.push('..')
} }
@ -143,6 +143,7 @@ const renamePromise = promisify2WithVoid<string, string>(rename)
const writeFilePromise = promisify2WithVoid<string, any>(writeFile) const writeFilePromise = promisify2WithVoid<string, any>(writeFile)
const readdirPromise = promisify1<string, string[]>(readdir) const readdirPromise = promisify1<string, string[]>(readdir)
const mkdirpPromise = promisify1<string, string>(mkdirp) 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 pseudoRandomBytesPromise = promisify1<number, Buffer>(pseudoRandomBytes)
const createPrivateKey = promisify1<number, { key: string }>(pem.createPrivateKey) const createPrivateKey = promisify1<number, { key: string }>(pem.createPrivateKey)
const getPublicKey = promisify1<string, { publicKey: string }>(pem.getPublicKey) 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[], files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[],
mimeTypeRegex: string, mimeTypeRegex: string,
field: string, field: string,
maxSize: number, maxSize: number | null,
optional = false optional = false
) { ) {
// Should have files // Should have files
@ -69,7 +69,7 @@ function isFileValid (
if (!file || !file.originalname) return false if (!file || !file.originalname) return false
// Check size // 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) 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) { async function isVideoExist (id: string, res: Response) {
let video: VideoModel let video: VideoModel | null
if (validator.isInt(id)) { if (validator.isInt(id)) {
video = await VideoModel.loadAndPopulateAccountAndServerAndTags(+id) video = await VideoModel.loadAndPopulateAccountAndServerAndTags(+id)
@ -158,7 +158,7 @@ async function isVideoExist (id: string, res: Response) {
video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(id) video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(id)
} }
if (!video) { if (video && video !== null) {
res.status(404) res.status(404)
.json({ error: 'Video not found' }) .json({ error: 'Video not found' })
.end() .end()
@ -173,7 +173,7 @@ async function isVideoExist (id: string, res: Response) {
async function isVideoChannelOfAccountExist (channelId: number, user: UserModel, res: Response) { async function isVideoChannelOfAccountExist (channelId: number, user: UserModel, res: Response) {
if (user.hasRight(UserRight.UPDATE_ANY_VIDEO) === true) { if (user.hasRight(UserRight.UPDATE_ANY_VIDEO) === true) {
const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId) const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId)
if (!videoChannel) { if (videoChannel && videoChannel !== null) {
res.status(400) res.status(400)
.json({ error: 'Unknown video video channel on this instance.' }) .json({ error: 'Unknown video video channel on this instance.' })
.end() .end()
@ -186,7 +186,7 @@ async function isVideoChannelOfAccountExist (channelId: number, user: UserModel,
} }
const videoChannel = await VideoChannelModel.loadByIdAndAccount(channelId, user.Account.id) const videoChannel = await VideoChannelModel.loadByIdAndAccount(channelId, user.Account.id)
if (!videoChannel) { if (videoChannel && videoChannel !== null) {
res.status(400) res.status(400)
.json({ error: 'Unknown video video channel for this account.' }) .json({ error: 'Unknown video video channel for this account.' })
.end() .end()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,6 +2,7 @@ import * as Sequelize from 'sequelize'
import { createClient } from 'redis' import { createClient } from 'redis'
import { CONFIG } from '../constants' import { CONFIG } from '../constants'
import { JobQueue } from '../../lib/job-queue' import { JobQueue } from '../../lib/job-queue'
import { Redis } from '../../lib/redis'
import { initDatabaseModels } from '../database' import { initDatabaseModels } from '../database'
async function up (utils: { async function up (utils: {
@ -12,11 +13,7 @@ async function up (utils: {
await initDatabaseModels(false) await initDatabaseModels(false)
return new Promise((res, rej) => { return new Promise((res, rej) => {
const client = createClient({ const client = createClient(Redis.getRedisClient())
host: CONFIG.REDIS.HOSTNAME,
port: CONFIG.REDIS.PORT,
db: CONFIG.REDIS.DB
})
const jobsPrefix = 'q-' + CONFIG.WEBSERVER.HOST const jobsPrefix = 'q-' + CONFIG.WEBSERVER.HOST
@ -36,7 +33,7 @@ async function up (utils: {
return res({ type: job.type, payload: parsedData }) return res({ type: job.type, payload: parsedData })
} catch (err) { } catch (err) {
console.error('Cannot parse data %s.', job.data) 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 // The installer will do that
if (tables.length === 0) return if (tables.length === 0) return
let actualVersion: number = null let actualVersion: number | null = null
const [ rows ] = await sequelizeTypescript.query('SELECT "migrationVersion" FROM "application"') const [ rows ] = await sequelizeTypescript.query('SELECT "migrationVersion" FROM "application"')
if (rows && rows[0] && rows[0].migrationVersion) { if (rows && rows[0] && rows[0].migrationVersion) {

View File

@ -20,7 +20,7 @@ function getVideoCommentAudience (
isOrigin = false isOrigin = false
) { ) {
const to = [ ACTIVITY_PUB.PUBLIC ] const to = [ ACTIVITY_PUB.PUBLIC ]
const cc = [] const cc: string[] = []
// Owner of the video we comment // Owner of the video we comment
if (isOrigin === false) { if (isOrigin === false) {
@ -60,8 +60,8 @@ function getAudience (actorSender: ActorModel, isPublic = true) {
} }
function buildAudience (followerUrls: string[], isPublic = true) { function buildAudience (followerUrls: string[], isPublic = true) {
let to = [] let to: string[] = []
let cc = [] let cc: string[] = []
if (isPublic) { if (isPublic) {
to = [ ACTIVITY_PUB.PUBLIC ] 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 privacy = to.indexOf(ACTIVITY_PUB.PUBLIC) !== -1 ? VideoPrivacy.PUBLIC : VideoPrivacy.UNLISTED
const duration = videoObject.duration.replace(/[^\d]+/, '') const duration = videoObject.duration.replace(/[^\d]+/, '')
let language: string = null let language: string | undefined
if (videoObject.language) { if (videoObject.language) {
language = videoObject.language.identifier language = videoObject.language.identifier
} }
let category: number = null let category: number | undefined
if (videoObject.category) { if (videoObject.category) {
category = parseInt(videoObject.category.identifier, 10) category = parseInt(videoObject.category.identifier, 10)
} }
let licence: number = null let licence: number | undefined
if (videoObject.licence) { if (videoObject.licence) {
licence = parseInt(videoObject.licence.identifier, 10) 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) throw new Error('Cannot find video files for ' + videoCreated.url)
} }
const attributes = [] const attributes: VideoFileModel[] = []
for (const fileUrl of fileUrls) { for (const fileUrl of fileUrls) {
// Fetch associated magnet uri // Fetch associated magnet uri
const magnet = videoObject.url.find(u => { 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) if (!magnet) throw new Error('Cannot find associated magnet uri for file ' + fileUrl.href)
const parsed = magnetUtil.decode(magnet.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 = { const attribute = {
extname: VIDEO_MIMETYPE_EXT[ fileUrl.mimeType ], extname: VIDEO_MIMETYPE_EXT[ fileUrl.mimeType ],
@ -161,7 +165,7 @@ function videoFileActivityUrlToDBAttributes (videoCreated: VideoModel, videoObje
resolution: fileUrl.width, resolution: fileUrl.width,
size: fileUrl.size, size: fileUrl.size,
videoId: videoCreated.id videoId: videoCreated.id
} } as VideoFileModel
attributes.push(attribute) attributes.push(attribute)
} }

View File

@ -91,9 +91,10 @@ class Emailer {
async addVideoAbuseReport (videoId: number) { async addVideoAbuseReport (videoId: number) {
const video = await VideoModel.load(videoId) const video = await VideoModel.load(videoId)
if (!video) throw new Error('Unknown Video id during Abuse report.')
const text = `Hi,\n\n` + 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` + `Cheers,\n` +
`PeerTube.` `PeerTube.`

View File

@ -15,7 +15,7 @@ async function computeBody (payload: { body: any, signatureActorId?: number }) {
} }
async function buildSignedRequestOptions (payload: { signatureActorId?: number }) { async function buildSignedRequestOptions (payload: { signatureActorId?: number }) {
let actor: ActorModel let actor: ActorModel | null
if (payload.signatureActorId) { if (payload.signatureActorId) {
actor = await ActorModel.load(payload.signatureActorId) actor = await ActorModel.load(payload.signatureActorId)
if (!actor) throw new Error('Unknown signature actor id.') 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) const video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(payload.videoUUID)
// No video, maybe deleted? // No video, maybe deleted?
if (!video) { 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 return undefined
} }
@ -45,13 +45,13 @@ async function processVideoFile (job: Bull.Job) {
const video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(payload.videoUUID) const video = await VideoModel.loadByUUIDAndPopulateAccountAndServerAndTags(payload.videoUUID)
// No video, maybe deleted? // No video, maybe deleted?
if (!video) { 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 return undefined
} }
// Transcoding in other resolution // Transcoding in other resolution
if (payload.resolution) { if (payload.resolution) {
await video.transcodeOriginalVideofile(payload.resolution, payload.isPortraitMode) await video.transcodeOriginalVideofile(payload.resolution, payload.isPortraitMode || false)
await retryTransactionWrapper(onVideoFileTranscoderOrImportSuccess, video) await retryTransactionWrapper(onVideoFileTranscoderOrImportSuccess, video)
} else { } else {

View File

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

View File

@ -6,8 +6,8 @@ import { CONFIG, USER_PASSWORD_RESET_LIFETIME, VIDEO_VIEW_LIFETIME } from '../in
type CachedRoute = { type CachedRoute = {
body: string, body: string,
contentType?: string contentType: string
statusCode?: string statusCode: string
} }
class Redis { class Redis {
@ -75,11 +75,12 @@ class Redis {
} }
setCachedRoute (req: express.Request, body: any, lifetime: number, contentType?: string, statusCode?: number) { setCachedRoute (req: express.Request, body: any, lifetime: number, contentType?: string, statusCode?: number) {
const cached: CachedRoute = { const cached: CachedRoute = Object.assign({}, {
body: body.toString(), body: body.toString()
contentType, },
statusCode: statusCode.toString() (contentType) ? { contentType } : null,
} (statusCode) ? { statusCode: statusCode.toString() } : null
)
return this.setObject(this.buildCachedRouteKey(req), cached, lifetime) 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 { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub'
import { createVideoChannel } from './video-channel' import { createVideoChannel } from './video-channel'
import { VideoChannelModel } from '../models/video/video-channel' import { VideoChannelModel } from '../models/video/video-channel'
import { FilteredModelAttributes } from 'sequelize-typescript/lib/models/Model'
async function createUserAccountAndChannel (userToCreate: UserModel, validateUser = true) { async function createUserAccountAndChannel (userToCreate: UserModel, validateUser = true) {
const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => { const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => {
@ -34,9 +35,9 @@ async function createUserAccountAndChannel (userToCreate: UserModel, validateUse
async function createLocalAccountWithoutKeys ( async function createLocalAccountWithoutKeys (
name: string, name: string,
userId: number, userId: number | null,
applicationId: number, applicationId: number | null,
t: Sequelize.Transaction, t: Sequelize.Transaction | undefined,
type: ActivityPubActorType= 'Person' type: ActivityPubActorType= 'Person'
) { ) {
const url = getAccountActivityPubUrl(name) const url = getAccountActivityPubUrl(name)
@ -49,7 +50,7 @@ async function createLocalAccountWithoutKeys (
userId, userId,
applicationId, applicationId,
actorId: actorInstanceCreated.id actorId: actorInstanceCreated.id
}) } as FilteredModelAttributes<AccountModel>)
const accountInstanceCreated = await accountInstance.save({ transaction: t }) const accountInstanceCreated = await accountInstance.save({ transaction: t })
accountInstanceCreated.Actor = actorInstanceCreated accountInstanceCreated.Actor = actorInstanceCreated

View File

@ -9,14 +9,14 @@ import { sendCreateVideoComment } from './activitypub/send'
async function createVideoComment (obj: { async function createVideoComment (obj: {
text: string, text: string,
inReplyToComment: VideoCommentModel, inReplyToComment: VideoCommentModel | null,
video: VideoModel video: VideoModel
account: AccountModel account: AccountModel
}, t: Sequelize.Transaction) { }, t: Sequelize.Transaction) {
let originCommentId: number = null let originCommentId: number | null = null
let inReplyToCommentId: number = null let inReplyToCommentId: number | null = null
if (obj.inReplyToComment) { if (obj.inReplyToComment && obj.inReplyToComment !== null) {
originCommentId = obj.inReplyToComment.originCommentId || obj.inReplyToComment.id originCommentId = obj.inReplyToComment.originCommentId || obj.inReplyToComment.id
inReplyToCommentId = 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) { 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' 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) 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 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) 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 inboxUrlsString = inboxUrls.map(url => `'${url}'`).join(',')
const query = `UPDATE "actorFollow" SET "score" = LEAST("score" + ${value}, ${ACTOR_FOLLOW_SCORE.MAX}) ` + 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 + ')' + 'WHERE "actor"."inboxUrl" IN (' + inboxUrlsString + ') OR "actor"."sharedInboxUrl" IN (' + inboxUrlsString + ')' +
')' ')'
const options = { const options = t ? {
type: Sequelize.QueryTypes.BULKUPDATE, type: Sequelize.QueryTypes.BULKUPDATE,
transaction: t transaction: t
} } : undefined
return ActorFollowModel.sequelize.query(query, options) 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) return OAuthTokenModel.scope(ScopeNames.WITH_ACCOUNT)
.findOne(query) .findOne(query)
.then(token => { .then(token => {
token['user'] = token.User if (token) {
token['user'] = token.User
return token return token
} else {
return new OAuthTokenModel()
}
}) })
} }

View File

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

View File

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

View File

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