@@ -31,7 +31,7 @@
+
+
+
+
+
diff --git a/client/src/app/+admin/users/user-add/user-add.component.ts b/client/src/app/+admin/users/user-add/user-add.component.ts
index 0dd99eccd..91377a933 100644
--- a/client/src/app/+admin/users/user-add/user-add.component.ts
+++ b/client/src/app/+admin/users/user-add/user-add.component.ts
@@ -9,7 +9,8 @@ import {
FormReactive,
USER_USERNAME,
USER_EMAIL,
- USER_PASSWORD
+ USER_PASSWORD,
+ USER_VIDEO_QUOTA
} from '../../../shared'
import { UserCreate } from '../../../../../../shared'
@@ -24,12 +25,14 @@ export class UserAddComponent extends FormReactive implements OnInit {
formErrors = {
'username': '',
'email': '',
- 'password': ''
+ 'password': '',
+ 'videoQuota': ''
}
validationMessages = {
'username': USER_USERNAME.MESSAGES,
'email': USER_EMAIL.MESSAGES,
- 'password': USER_PASSWORD.MESSAGES
+ 'password': USER_PASSWORD.MESSAGES,
+ 'videoQuota': USER_VIDEO_QUOTA.MESSAGES
}
constructor (
@@ -45,7 +48,8 @@ export class UserAddComponent extends FormReactive implements OnInit {
this.form = this.formBuilder.group({
username: [ '', USER_USERNAME.VALIDATORS ],
email: [ '', USER_EMAIL.VALIDATORS ],
- password: [ '', USER_PASSWORD.VALIDATORS ]
+ password: [ '', USER_PASSWORD.VALIDATORS ],
+ videoQuota: [ '-1', USER_VIDEO_QUOTA.VALIDATORS ]
})
this.form.valueChanges.subscribe(data => this.onValueChanged(data))
@@ -60,6 +64,9 @@ export class UserAddComponent extends FormReactive implements OnInit {
const userCreate: UserCreate = this.form.value
+ // A select in HTML is always mapped as a string, we convert it to number
+ userCreate.videoQuota = parseInt(this.form.value['videoQuota'], 10)
+
this.userService.addUser(userCreate).subscribe(
() => {
this.notificationsService.success('Success', `User ${userCreate.username} created.`)
diff --git a/client/src/app/+admin/users/user-list/user-list.component.ts b/client/src/app/+admin/users/user-list/user-list.component.ts
index 12826741c..dbb85cedd 100644
--- a/client/src/app/+admin/users/user-list/user-list.component.ts
+++ b/client/src/app/+admin/users/user-list/user-list.component.ts
@@ -30,7 +30,7 @@ export class UserListComponent {
},
pager: {
display: true,
- perPage: 10
+ perPage: 1
},
columns: {
id: {
@@ -43,6 +43,9 @@ export class UserListComponent {
email: {
title: 'Email'
},
+ videoQuota: {
+ title: 'Video quota'
+ },
role: {
title: 'Role',
sort: false
diff --git a/client/src/app/shared/forms/form-validators/user.ts b/client/src/app/shared/forms/form-validators/user.ts
index fd316583e..087a99760 100644
--- a/client/src/app/shared/forms/form-validators/user.ts
+++ b/client/src/app/shared/forms/form-validators/user.ts
@@ -22,3 +22,10 @@ export const USER_PASSWORD = {
'minlength': 'Password must be at least 6 characters long.'
}
}
+export const USER_VIDEO_QUOTA = {
+ VALIDATORS: [ Validators.required, Validators.min(-1) ],
+ MESSAGES: {
+ 'required': 'Video quota is required.',
+ 'min': 'Quota must be greater than -1.'
+ }
+}
\ No newline at end of file
diff --git a/client/src/app/shared/rest/rest-data-source.ts b/client/src/app/shared/rest/rest-data-source.ts
index 7956637e0..5c205d280 100644
--- a/client/src/app/shared/rest/rest-data-source.ts
+++ b/client/src/app/shared/rest/rest-data-source.ts
@@ -3,14 +3,31 @@ import { Http, RequestOptionsArgs, URLSearchParams, Response } from '@angular/ht
import { ServerDataSource } from 'ng2-smart-table'
export class RestDataSource extends ServerDataSource {
- constructor (http: Http, endpoint: string) {
+ private updateResponse: (input: any[]) => any[]
+
+ constructor (http: Http, endpoint: string, updateResponse?: (input: any[]) => any[]) {
const options = {
endPoint: endpoint,
sortFieldKey: 'sort',
dataKey: 'data'
}
-
super(http, options)
+
+ if (updateResponse) {
+ this.updateResponse = updateResponse
+ }
+ }
+
+ protected extractDataFromResponse (res: Response) {
+ const json = res.json()
+ if (!json) return []
+ let data = json.data
+
+ if (this.updateResponse !== undefined) {
+ data = this.updateResponse(data)
+ }
+
+ return data
}
protected extractTotalFromResponse (res: Response) {
diff --git a/client/src/app/shared/users/user.model.ts b/client/src/app/shared/users/user.model.ts
index 1c2b481e3..bf12876c7 100644
--- a/client/src/app/shared/users/user.model.ts
+++ b/client/src/app/shared/users/user.model.ts
@@ -6,6 +6,7 @@ export class User implements UserServerModel {
email: string
role: UserRole
displayNSFW: boolean
+ videoQuota: number
createdAt: Date
constructor (hash: {
@@ -13,6 +14,7 @@ export class User implements UserServerModel {
username: string,
email: string,
role: UserRole,
+ videoQuota?: number,
displayNSFW?: boolean,
createdAt?: Date
}) {
@@ -20,9 +22,16 @@ export class User implements UserServerModel {
this.username = hash.username
this.email = hash.email
this.role = hash.role
- this.displayNSFW = hash.displayNSFW
- if (hash.createdAt) {
+ if (hash.videoQuota !== undefined) {
+ this.videoQuota = hash.videoQuota
+ }
+
+ if (hash.displayNSFW !== undefined) {
+ this.displayNSFW = hash.displayNSFW
+ }
+
+ if (hash.createdAt !== undefined) {
this.createdAt = hash.createdAt
}
}
diff --git a/client/tslint.json b/client/tslint.json
index cfad2a5d9..b1e211ee9 100644
--- a/client/tslint.json
+++ b/client/tslint.json
@@ -4,7 +4,6 @@
"rules": {
"no-inferrable-types": true,
"eofline": true,
- "indent": ["spaces"],
"max-line-length": [true, 140],
"no-floating-promises": false,
"no-unused-variable": false, // Bug, wait TypeScript 2.4
diff --git a/client/yarn.lock b/client/yarn.lock
index 0fc5ec418..9478e23b2 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -6740,9 +6740,9 @@ tslint-loader@^3.3.0:
rimraf "^2.4.4"
semver "^5.3.0"
-tslint@^5.4.3:
- version "5.6.0"
- resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.6.0.tgz#088aa6c6026623338650b2900828ab3edf59f6cf"
+tslint@^5.7.0:
+ version "5.7.0"
+ resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.7.0.tgz#c25e0d0c92fa1201c2bc30e844e08e682b4f3552"
dependencies:
babel-code-frame "^6.22.0"
colors "^1.1.2"
@@ -6753,7 +6753,7 @@ tslint@^5.4.3:
resolve "^1.3.2"
semver "^5.3.0"
tslib "^1.7.1"
- tsutils "^2.7.1"
+ tsutils "^2.8.1"
tsml@1.0.1:
version "1.0.1"
@@ -6763,9 +6763,9 @@ tsutils@^1.4.0:
version "1.9.1"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-1.9.1.tgz#b9f9ab44e55af9681831d5f28d0aeeaf5c750cb0"
-tsutils@^2.7.1:
- version "2.8.1"
- resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.8.1.tgz#3771404e7ca9f0bedf5d919a47a4b1890a68efff"
+tsutils@^2.8.1:
+ version "2.8.2"
+ resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.8.2.tgz#2c1486ba431260845b0ac6f902afd9d708a8ea6a"
dependencies:
tslib "^1.7.1"
@@ -6806,9 +6806,9 @@ typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
-typescript@~2.4.0:
- version "2.4.2"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.2.tgz#f8395f85d459276067c988aa41837a8f82870844"
+typescript@^2.5.2:
+ version "2.5.2"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.5.2.tgz#038a95f7d9bbb420b1bf35ba31d4c5c1dd3ffe34"
uglify-js@3.0.x, uglify-js@^3.0.6:
version "3.0.28"
diff --git a/config/default.yaml b/config/default.yaml
index a97d3ff78..4c19a5b2d 100644
--- a/config/default.yaml
+++ b/config/default.yaml
@@ -35,6 +35,11 @@ signup:
enabled: false
limit: 10 # When the limit is reached, registrations are disabled. -1 == unlimited
+user:
+ # Default value of maximum video BYTES the user can upload (does not take into account transcoded files).
+ # -1 == unlimited
+ video_quota: -1
+
# If enabled, the video will be transcoded to mp4 (x264) with "faststart" flag
# Uses a lot of CPU!
transcoding:
diff --git a/config/production.yaml.example b/config/production.yaml.example
index 90e07f577..987da12cc 100644
--- a/config/production.yaml.example
+++ b/config/production.yaml.example
@@ -36,6 +36,11 @@ signup:
enabled: false
limit: 10 # When the limit is reached, registrations are disabled. -1 == unlimited
+user:
+ # Default value of maximum video BYTES the user can upload (does not take into account transcoded files).
+ # -1 == unlimited
+ video_quota: -1
+
# If enabled, the video will be transcoded to mp4 (x264) with "faststart" flag
# Uses a lot of CPU!
transcoding:
diff --git a/package.json b/package.json
index 2a1b0bde3..900d04052 100644
--- a/package.json
+++ b/package.json
@@ -79,7 +79,7 @@
"scripty": "^1.5.0",
"sequelize": "^4.7.5",
"ts-node": "^3.0.6",
- "typescript": "^2.4.1",
+ "typescript": "^2.5.2",
"validator": "^8.1.0",
"winston": "^2.1.1",
"ws": "^3.1.0"
@@ -109,7 +109,7 @@
"source-map-support": "^0.4.15",
"standard": "^10.0.0",
"supertest": "^3.0.0",
- "tslint": "^5.2.0",
+ "tslint": "^5.7.0",
"tslint-config-standard": "^6.0.0",
"webtorrent": "^0.98.0"
},
diff --git a/server/controllers/api/users.ts b/server/controllers/api/users.ts
index 04d885185..1b5b7f903 100644
--- a/server/controllers/api/users.ts
+++ b/server/controllers/api/users.ts
@@ -1,7 +1,7 @@
import * as express from 'express'
import { database as db } from '../../initializers/database'
-import { USER_ROLES } from '../../initializers'
+import { USER_ROLES, CONFIG } from '../../initializers'
import { logger, getFormattedObjects } from '../../helpers'
import {
authenticate,
@@ -80,12 +80,18 @@ export {
function createUser (req: express.Request, res: express.Response, next: express.NextFunction) {
const body: UserCreate = req.body
+ // On registration, we set the user video quota
+ if (body.videoQuota === undefined) {
+ body.videoQuota = CONFIG.USER.VIDEO_QUOTA
+ }
+
const user = db.User.build({
username: body.username,
password: body.password,
email: body.email,
displayNSFW: false,
- role: USER_ROLES.USER
+ role: USER_ROLES.USER,
+ videoQuota: body.videoQuota
})
user.save()
@@ -140,6 +146,7 @@ function updateUser (req: express.Request, res: express.Response, next: express.
.then(user => {
if (body.password) user.password = body.password
if (body.displayNSFW !== undefined) user.displayNSFW = body.displayNSFW
+ if (body.videoQuota !== undefined) user.videoQuota = body.videoQuota
return user.save()
})
diff --git a/server/helpers/custom-validators/users.ts b/server/helpers/custom-validators/users.ts
index 2b37bdde8..00061f9df 100644
--- a/server/helpers/custom-validators/users.ts
+++ b/server/helpers/custom-validators/users.ts
@@ -15,6 +15,10 @@ function isUserRoleValid (value: string) {
return values(USER_ROLES).indexOf(value as UserRole) !== -1
}
+function isUserVideoQuotaValid (value: string) {
+ return exists(value) && validator.isInt(value + '', USERS_CONSTRAINTS_FIELDS.VIDEO_QUOTA)
+}
+
function isUserUsernameValid (value: string) {
const max = USERS_CONSTRAINTS_FIELDS.USERNAME.max
const min = USERS_CONSTRAINTS_FIELDS.USERNAME.min
@@ -30,6 +34,7 @@ function isUserDisplayNSFWValid (value: any) {
export {
isUserPasswordValid,
isUserRoleValid,
+ isUserVideoQuotaValid,
isUserUsernameValid,
isUserDisplayNSFWValid
}
@@ -39,6 +44,7 @@ declare module 'express-validator' {
isUserPasswordValid,
isUserRoleValid,
isUserUsernameValid,
- isUserDisplayNSFWValid
+ isUserDisplayNSFWValid,
+ isUserVideoQuotaValid
}
}
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index 50a939083..b93a85859 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -15,7 +15,7 @@ import {
// ---------------------------------------------------------------------------
-const LAST_MIGRATION_VERSION = 65
+const LAST_MIGRATION_VERSION = 70
// ---------------------------------------------------------------------------
@@ -77,7 +77,10 @@ const CONFIG = {
},
SIGNUP: {
ENABLED: config.get
('signup.enabled'),
- LIMIT: config.get('signup.limit')
+ LIMIT: config.get('signup.limit'),
+ },
+ USER: {
+ VIDEO_QUOTA: config.get('user.video_quota')
},
TRANSCODING: {
ENABLED: config.get('transcoding.enabled'),
@@ -97,7 +100,8 @@ CONFIG.WEBSERVER.HOST = CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT
const CONSTRAINTS_FIELDS = {
USERS: {
USERNAME: { min: 3, max: 20 }, // Length
- PASSWORD: { min: 6, max: 255 } // Length
+ PASSWORD: { min: 6, max: 255 }, // Length
+ VIDEO_QUOTA: { min: -1 }
},
VIDEO_ABUSES: {
REASON: { min: 2, max: 300 } // Length
diff --git a/server/initializers/database.ts b/server/initializers/database.ts
index c0df2b63a..d04c8db1b 100644
--- a/server/initializers/database.ts
+++ b/server/initializers/database.ts
@@ -1,5 +1,6 @@
import { join } from 'path'
import { flattenDepth } from 'lodash'
+require('pg').defaults.parseInt8 = true // Avoid BIGINT to be converted to string
import * as Sequelize from 'sequelize'
import * as Promise from 'bluebird'
diff --git a/server/initializers/installer.ts b/server/initializers/installer.ts
index 43b5adfed..10b74b85f 100644
--- a/server/initializers/installer.ts
+++ b/server/initializers/installer.ts
@@ -38,12 +38,12 @@ function removeCacheDirectories () {
}
function createDirectoriesIfNotExist () {
- const storages = CONFIG.STORAGE
+ const storage = CONFIG.STORAGE
const cacheDirectories = CACHE.DIRECTORIES
const tasks = []
- Object.keys(storages).forEach(key => {
- const dir = storages[key]
+ Object.keys(storage).forEach(key => {
+ const dir = storage[key]
tasks.push(mkdirpPromise(dir))
})
@@ -112,7 +112,8 @@ function createOAuthAdminIfNotExist () {
username,
email,
password,
- role
+ role,
+ videoQuota: -1
}
return db.User.create(userData, createOptions).then(createdUser => {
diff --git a/server/initializers/migrations/0070-user-video-quota.ts b/server/initializers/migrations/0070-user-video-quota.ts
new file mode 100644
index 000000000..dec4d46dd
--- /dev/null
+++ b/server/initializers/migrations/0070-user-video-quota.ts
@@ -0,0 +1,32 @@
+import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+function up (utils: {
+ transaction: Sequelize.Transaction,
+ queryInterface: Sequelize.QueryInterface,
+ sequelize: Sequelize.Sequelize,
+ db: any
+}): Promise {
+ const q = utils.queryInterface
+
+ const data = {
+ type: Sequelize.BIGINT,
+ allowNull: false,
+ defaultValue: -1
+ }
+
+ return q.addColumn('Users', 'videoQuota', data)
+ .then(() => {
+ data.defaultValue = null
+ return q.changeColumn('Users', 'videoQuota', data)
+ })
+}
+
+function down (options) {
+ throw new Error('Not implemented.')
+}
+
+export {
+ up,
+ down
+}
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts
index 71e529872..eeb0e3557 100644
--- a/server/middlewares/validators/users.ts
+++ b/server/middlewares/validators/users.ts
@@ -12,6 +12,7 @@ function usersAddValidator (req: express.Request, res: express.Response, next: e
req.checkBody('username', 'Should have a valid username').isUserUsernameValid()
req.checkBody('password', 'Should have a valid password').isUserPasswordValid()
req.checkBody('email', 'Should have a valid email').isEmail()
+ req.checkBody('videoQuota', 'Should have a valid user quota').isUserVideoQuotaValid()
logger.debug('Checking usersAdd parameters', { parameters: req.body })
@@ -55,6 +56,7 @@ function usersUpdateValidator (req: express.Request, res: express.Response, next
// Add old password verification
req.checkBody('password', 'Should have a valid password').optional().isUserPasswordValid()
req.checkBody('displayNSFW', 'Should have a valid display Not Safe For Work attribute').optional().isUserDisplayNSFWValid()
+ req.checkBody('videoQuota', 'Should have a valid user quota').optional().isUserVideoQuotaValid()
logger.debug('Checking usersUpdate parameters', { parameters: req.body })
diff --git a/server/middlewares/validators/videos.ts b/server/middlewares/validators/videos.ts
index 29c1ee0ef..1d19ebfd9 100644
--- a/server/middlewares/validators/videos.ts
+++ b/server/middlewares/validators/videos.ts
@@ -24,10 +24,23 @@ function videosAddValidator (req: express.Request, res: express.Response, next:
logger.debug('Checking videosAdd parameters', { parameters: req.body, files: req.files })
checkErrors(req, res, () => {
- const videoFile = req.files['videofile'][0]
+ const videoFile: Express.Multer.File = req.files['videofile'][0]
+ const user = res.locals.oauth.token.User
- db.Video.getDurationFromFile(videoFile.path)
+ user.isAbleToUploadVideo(videoFile)
+ .then(isAble => {
+ if (isAble === false) {
+ res.status(403).send('The user video quota is exceeded with this video.')
+
+ return undefined
+ }
+
+ return db.Video.getDurationFromFile(videoFile.path)
+ })
.then(duration => {
+ // Previous test failed, abort
+ if (duration === undefined) return
+
if (!isVideoDurationValid('' + duration)) {
return res.status(400).send('Duration of the video file is too big (max: ' + CONSTRAINTS_FIELDS.VIDEOS.DURATION.max + 's).')
}
diff --git a/server/models/user/user-interface.ts b/server/models/user/user-interface.ts
index 0b97a8f6d..8974a9a97 100644
--- a/server/models/user/user-interface.ts
+++ b/server/models/user/user-interface.ts
@@ -11,6 +11,7 @@ export namespace UserMethods {
export type ToFormattedJSON = (this: UserInstance) => FormattedUser
export type IsAdmin = (this: UserInstance) => boolean
+ export type IsAbleToUploadVideo = (this: UserInstance, videoFile: Express.Multer.File) => Promise
export type CountTotal = () => Promise
@@ -31,6 +32,7 @@ export interface UserClass {
isPasswordMatch: UserMethods.IsPasswordMatch,
toFormattedJSON: UserMethods.ToFormattedJSON,
isAdmin: UserMethods.IsAdmin,
+ isAbleToUploadVideo: UserMethods.IsAbleToUploadVideo,
countTotal: UserMethods.CountTotal,
getByUsername: UserMethods.GetByUsername,
@@ -42,11 +44,13 @@ export interface UserClass {
}
export interface UserAttributes {
+ id?: number
password: string
username: string
email: string
displayNSFW?: boolean
role: UserRole
+ videoQuota: number
}
export interface UserInstance extends UserClass, UserAttributes, Sequelize.Instance {
diff --git a/server/models/user/user.ts b/server/models/user/user.ts
index d481fa13c..12a7547f5 100644
--- a/server/models/user/user.ts
+++ b/server/models/user/user.ts
@@ -1,5 +1,6 @@
import { values } from 'lodash'
import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
import { getSort } from '../utils'
import { USER_ROLES } from '../../initializers'
@@ -8,7 +9,8 @@ import {
comparePassword,
isUserPasswordValid,
isUserUsernameValid,
- isUserDisplayNSFWValid
+ isUserDisplayNSFWValid,
+ isUserVideoQuotaValid
} from '../../helpers'
import { addMethodsToModel } from '../utils'
@@ -30,6 +32,7 @@ let listForApi: UserMethods.ListForApi
let loadById: UserMethods.LoadById
let loadByUsername: UserMethods.LoadByUsername
let loadByUsernameOrEmail: UserMethods.LoadByUsernameOrEmail
+let isAbleToUploadVideo: UserMethods.IsAbleToUploadVideo
export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
User = sequelize.define('User',
@@ -75,6 +78,16 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
role: {
type: DataTypes.ENUM(values(USER_ROLES)),
allowNull: false
+ },
+ videoQuota: {
+ type: DataTypes.BIGINT,
+ allowNull: false,
+ validate: {
+ videoQuotaValid: value => {
+ const res = isUserVideoQuotaValid(value)
+ if (res === false) throw new Error('Video quota is not valid.')
+ }
+ }
}
},
{
@@ -109,7 +122,8 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
const instanceMethods = [
isPasswordMatch,
toFormattedJSON,
- isAdmin
+ isAdmin,
+ isAbleToUploadVideo
]
addMethodsToModel(User, classMethods, instanceMethods)
@@ -136,6 +150,7 @@ toFormattedJSON = function (this: UserInstance) {
email: this.email,
displayNSFW: this.displayNSFW,
role: this.role,
+ videoQuota: this.videoQuota,
createdAt: this.createdAt
}
}
@@ -144,6 +159,14 @@ isAdmin = function (this: UserInstance) {
return this.role === USER_ROLES.ADMIN
}
+isAbleToUploadVideo = function (this: UserInstance, videoFile: Express.Multer.File) {
+ if (this.videoQuota === -1) return Promise.resolve(true)
+
+ return getOriginalVideoFileTotalFromUser(this).then(totalBytes => {
+ return (videoFile.size + totalBytes) < this.videoQuota
+ })
+}
+
// ------------------------------ STATICS ------------------------------
function associate (models) {
@@ -215,3 +238,36 @@ loadByUsernameOrEmail = function (username: string, email: string) {
// FIXME: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18387
return (User as any).findOne(query)
}
+
+// ---------------------------------------------------------------------------
+
+function getOriginalVideoFileTotalFromUser (user: UserInstance) {
+ const query = {
+ attributes: [
+ Sequelize.fn('COUNT', Sequelize.col('VideoFile.size'), 'totalVideoBytes')
+ ],
+ where: {
+ id: user.id
+ },
+ include: [
+ {
+ model: User['sequelize'].models.Author,
+ include: [
+ {
+ model: User['sequelize'].models.Video,
+ include: [
+ {
+ model: User['sequelize'].models.VideoFile
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+
+ // FIXME: cast to any because of bad typing...
+ return User.findAll(query).then((res: any) => {
+ return res.totalVideoBytes
+ })
+}
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 7dfea8ac9..4fb4485d8 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -9,6 +9,7 @@ import * as Sequelize from 'sequelize'
import * as Promise from 'bluebird'
import { TagInstance } from './tag-interface'
+import { UserInstance } from '../user/user-interface'
import {
logger,
isVideoNameValid,
@@ -582,7 +583,7 @@ transcodeVideofile = function (this: VideoInstance, inputVideoFile: VideoFileIns
return res()
})
.catch(err => {
- // Autodestruction...
+ // Auto destruction...
this.destroy().catch(err => logger.error('Cannot destruct video after transcoding failure.', err))
return rej(err)
@@ -608,8 +609,8 @@ removeFile = function (this: VideoInstance, videoFile: VideoFileInstance) {
}
removeTorrent = function (this: VideoInstance, videoFile: VideoFileInstance) {
- const torrenPath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile))
- return unlinkPromise(torrenPath)
+ const torrentPath = join(CONFIG.STORAGE.TORRENTS_DIR, this.getTorrentFileName(videoFile))
+ return unlinkPromise(torrentPath)
}
// ------------------------------ STATICS ------------------------------
diff --git a/shared/models/users/user-create.model.ts b/shared/models/users/user-create.model.ts
index 2cddcdcb0..49fa2549d 100644
--- a/shared/models/users/user-create.model.ts
+++ b/shared/models/users/user-create.model.ts
@@ -2,4 +2,5 @@ export interface UserCreate {
username: string
password: string
email: string
+ videoQuota: number
}
diff --git a/shared/models/users/user-update.model.ts b/shared/models/users/user-update.model.ts
index 8b9abfb15..895ec0681 100644
--- a/shared/models/users/user-update.model.ts
+++ b/shared/models/users/user-update.model.ts
@@ -1,4 +1,5 @@
export interface UserUpdate {
displayNSFW?: boolean
password?: string
+ videoQuota?: number
}
diff --git a/shared/models/users/user.model.ts b/shared/models/users/user.model.ts
index 5c48a17b2..867a6dde5 100644
--- a/shared/models/users/user.model.ts
+++ b/shared/models/users/user.model.ts
@@ -6,5 +6,6 @@ export interface User {
email: string
displayNSFW: boolean
role: UserRole
+ videoQuota: number
createdAt: Date
}
diff --git a/tslint.json b/tslint.json
index 70e5d9bb4..6e982ca85 100644
--- a/tslint.json
+++ b/tslint.json
@@ -4,6 +4,7 @@
"no-inferrable-types": true,
"eofline": true,
"indent": ["spaces"],
+ "ter-indent": [true, 2],
"max-line-length": [true, 140],
"no-floating-promises": false
}
diff --git a/yarn.lock b/yarn.lock
index c0f35b21d..1a6af175a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3755,9 +3755,9 @@ tslint-eslint-rules@^4.0.0:
tslib "^1.0.0"
tsutils "^1.4.0"
-tslint@^5.2.0:
- version "5.6.0"
- resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.6.0.tgz#088aa6c6026623338650b2900828ab3edf59f6cf"
+tslint@^5.7.0:
+ version "5.7.0"
+ resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.7.0.tgz#c25e0d0c92fa1201c2bc30e844e08e682b4f3552"
dependencies:
babel-code-frame "^6.22.0"
colors "^1.1.2"
@@ -3768,15 +3768,15 @@ tslint@^5.2.0:
resolve "^1.3.2"
semver "^5.3.0"
tslib "^1.7.1"
- tsutils "^2.7.1"
+ tsutils "^2.8.1"
tsutils@^1.4.0:
version "1.9.1"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-1.9.1.tgz#b9f9ab44e55af9681831d5f28d0aeeaf5c750cb0"
-tsutils@^2.7.1:
- version "2.8.1"
- resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.8.1.tgz#3771404e7ca9f0bedf5d919a47a4b1890a68efff"
+tsutils@^2.8.1:
+ version "2.8.2"
+ resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.8.2.tgz#2c1486ba431260845b0ac6f902afd9d708a8ea6a"
dependencies:
tslib "^1.7.1"
@@ -3821,9 +3821,9 @@ typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
-typescript@^2.4.1:
- version "2.5.1"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.5.1.tgz#ce7cc93ada3de19475cc9d17e3adea7aee1832aa"
+typescript@^2.5.2:
+ version "2.5.2"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.5.2.tgz#038a95f7d9bbb420b1bf35ba31d4c5c1dd3ffe34"
uid-number@^0.0.6:
version "0.0.6"