mirror of https://github.com/Chocobozzz/PeerTube
Add ability to update some configuration keys
parent
9581cabc59
commit
fd206f0b2d
|
@ -7,6 +7,7 @@
|
|||
/test6/
|
||||
/storage/
|
||||
/config/production.yaml
|
||||
/config/local.json
|
||||
/ffmpeg/
|
||||
/*.sublime-project
|
||||
/*.sublime-workspace
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import { NgModule } from '@angular/core'
|
||||
import { RouterModule, Routes } from '@angular/router'
|
||||
import { ConfigRoutes } from '@app/+admin/config'
|
||||
|
||||
import { MetaGuard } from '@ngx-meta/core'
|
||||
|
||||
import { AdminComponent } from './admin.component'
|
||||
import { FollowsRoutes } from './follows'
|
||||
import { JobsRoutes } from './jobs/job.routes'
|
||||
import { UsersRoutes } from './users'
|
||||
import { VideoAbusesRoutes } from './video-abuses'
|
||||
import { VideoBlacklistRoutes } from './video-blacklist'
|
||||
import { JobsRoutes } from './jobs/job.routes'
|
||||
|
||||
const adminRoutes: Routes = [
|
||||
{
|
||||
|
@ -26,7 +27,8 @@ const adminRoutes: Routes = [
|
|||
...UsersRoutes,
|
||||
...VideoAbusesRoutes,
|
||||
...VideoBlacklistRoutes,
|
||||
...JobsRoutes
|
||||
...JobsRoutes,
|
||||
...ConfigRoutes
|
||||
]
|
||||
}
|
||||
]
|
||||
|
|
|
@ -19,6 +19,10 @@
|
|||
<a *ngIf="hasJobsRight()" routerLink="/admin/jobs" routerLinkActive="active" class="title-page">
|
||||
Jobs
|
||||
</a>
|
||||
|
||||
<a *ngIf="hasConfigRight()" routerLink="/admin/config" routerLinkActive="active" class="title-page">
|
||||
Configuration
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="margin-content">
|
||||
|
|
|
@ -28,4 +28,8 @@ export class AdminComponent {
|
|||
hasJobsRight () {
|
||||
return this.auth.getUser().hasRight(UserRight.MANAGE_JOBS)
|
||||
}
|
||||
|
||||
hasConfigRight () {
|
||||
return this.auth.getUser().hasRight(UserRight.MANAGE_CONFIGURATION)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { NgModule } from '@angular/core'
|
||||
import { ConfigComponent, EditCustomConfigComponent } from '@app/+admin/config'
|
||||
import { ConfigService } from '@app/+admin/config/shared/config.service'
|
||||
import { TabsModule } from 'ngx-bootstrap/tabs'
|
||||
import { DataTableModule } from 'primeng/components/datatable/datatable'
|
||||
import { SharedModule } from '../shared'
|
||||
|
@ -41,7 +43,10 @@ import { VideoBlacklistComponent, VideoBlacklistListComponent } from './video-bl
|
|||
VideoAbuseListComponent,
|
||||
|
||||
JobsComponent,
|
||||
JobsListComponent
|
||||
JobsListComponent,
|
||||
|
||||
ConfigComponent,
|
||||
EditCustomConfigComponent
|
||||
],
|
||||
|
||||
exports: [
|
||||
|
@ -51,7 +56,8 @@ import { VideoBlacklistComponent, VideoBlacklistListComponent } from './video-bl
|
|||
providers: [
|
||||
FollowService,
|
||||
UserService,
|
||||
JobService
|
||||
JobService,
|
||||
ConfigService
|
||||
]
|
||||
})
|
||||
export class AdminModule { }
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
import { Component } from '@angular/core'
|
||||
|
||||
@Component({
|
||||
template: '<router-outlet></router-outlet>'
|
||||
})
|
||||
export class ConfigComponent {
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
import { Routes } from '@angular/router'
|
||||
import { EditCustomConfigComponent } from '@app/+admin/config/edit-custom-config'
|
||||
import { UserRightGuard } from '@app/core'
|
||||
import { UserRight } from '../../../../../shared/models/users'
|
||||
import { ConfigComponent } from './config.component'
|
||||
|
||||
export const ConfigRoutes: Routes = [
|
||||
{
|
||||
path: 'config',
|
||||
component: ConfigComponent,
|
||||
canActivate: [ UserRightGuard ],
|
||||
data: {
|
||||
userRight: UserRight.MANAGE_CONFIGURATION
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'edit-custom',
|
||||
pathMatch: 'full'
|
||||
},
|
||||
{
|
||||
path: 'edit-custom',
|
||||
component: EditCustomConfigComponent,
|
||||
data: {
|
||||
meta: {
|
||||
title: 'Following list'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
|
@ -0,0 +1,97 @@
|
|||
<div class="admin-sub-title">Update PeerTube configuration</div>
|
||||
|
||||
<form role="form" (ngSubmit)="formValidated()" [formGroup]="form">
|
||||
|
||||
<div class="inner-form-title">Cache</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="cachePreviewsSize">Preview cache size</label>
|
||||
<input
|
||||
type="text" id="cachePreviewsSize"
|
||||
formControlName="cachePreviewsSize" [ngClass]="{ 'input-error': formErrors['cachePreviewsSize'] }"
|
||||
>
|
||||
<div *ngIf="formErrors.cachePreviewsSize" class="form-error">
|
||||
{{ formErrors.cachePreviewsSize }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="inner-form-title">Signup</div>
|
||||
|
||||
<div class="form-group">
|
||||
<input type="checkbox" id="signupEnabled" formControlName="signupEnabled">
|
||||
|
||||
<label for="signupEnabled"></label>
|
||||
<label for="signupEnabled">Signup enabled</label>
|
||||
</div>
|
||||
|
||||
<div *ngIf="isSignupEnabled()" class="form-group">
|
||||
<label for="signupLimit">Signup limit</label>
|
||||
<input
|
||||
type="text" id="signupLimit"
|
||||
formControlName="signupLimit" [ngClass]="{ 'input-error': formErrors['signupLimit'] }"
|
||||
>
|
||||
<div *ngIf="formErrors.signupLimit" class="form-error">
|
||||
{{ formErrors.signupLimit }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="inner-form-title">Administrator</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="adminEmail">Admin email</label>
|
||||
<input
|
||||
type="text" id="adminEmail"
|
||||
formControlName="adminEmail" [ngClass]="{ 'input-error': formErrors['adminEmail'] }"
|
||||
>
|
||||
<div *ngIf="formErrors.adminEmail" class="form-error">
|
||||
{{ formErrors.adminEmail }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="inner-form-title">Users</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="userVideoQuota">User default video quota</label>
|
||||
<div class="peertube-select-container">
|
||||
<select id="userVideoQuota" formControlName="userVideoQuota">
|
||||
<option *ngFor="let videoQuotaOption of videoQuotaOptions" [value]="videoQuotaOption.value">
|
||||
{{ videoQuotaOption.label }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="inner-form-title">Transcoding</div>
|
||||
|
||||
<div class="form-group">
|
||||
<input type="checkbox" id="transcodingEnabled" formControlName="transcodingEnabled">
|
||||
|
||||
<label for="transcodingEnabled"></label>
|
||||
<label for="transcodingEnabled">Transcoding enabled</label>
|
||||
</div>
|
||||
|
||||
<ng-template [ngIf]="isTranscodingEnabled()">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="transcodingThreads">Transcoding threads</label>
|
||||
<div class="peertube-select-container">
|
||||
<select id="transcodingThreads" formControlName="transcodingThreads">
|
||||
<option *ngFor="let transcodingThreadOption of transcodingThreadOptions" [value]="transcodingThreadOption.value">
|
||||
{{ transcodingThreadOption.label }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" *ngFor="let resolution of resolutions">
|
||||
<input
|
||||
type="checkbox" [id]="getResolutionKey(resolution)"
|
||||
[formControlName]="getResolutionKey(resolution)"
|
||||
>
|
||||
<label [for]="getResolutionKey(resolution)"></label>
|
||||
<label [for]="getResolutionKey(resolution)">Resolution {{ resolution }} enabled</label>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<input type="submit" value="Update configuration" [disabled]="!form.valid">
|
||||
</form>
|
|
@ -0,0 +1,31 @@
|
|||
@import '_variables';
|
||||
@import '_mixins';
|
||||
|
||||
input[type=text] {
|
||||
@include peertube-input-text(340px);
|
||||
display: block;
|
||||
}
|
||||
|
||||
input[type=checkbox] {
|
||||
@include peertube-checkbox(1px);
|
||||
}
|
||||
|
||||
.peertube-select-container {
|
||||
@include peertube-select-container(340px);
|
||||
}
|
||||
|
||||
input[type=submit] {
|
||||
@include peertube-button;
|
||||
@include orange-button;
|
||||
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.inner-form-title {
|
||||
text-transform: uppercase;
|
||||
color: $orange-color;
|
||||
font-weight: $font-bold;
|
||||
font-size: 13px;
|
||||
margin-top: 30px;
|
||||
margin-bottom: 10px;
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
import { FormBuilder, FormGroup } from '@angular/forms'
|
||||
import { Router } from '@angular/router'
|
||||
import { ConfigService } from '@app/+admin/config/shared/config.service'
|
||||
import { ServerService } from '@app/core/server/server.service'
|
||||
import { FormReactive, USER_VIDEO_QUOTA } from '@app/shared'
|
||||
import { ADMIN_EMAIL, CACHE_PREVIEWS_SIZE, SIGNUP_LIMIT, TRANSCODING_THREADS } from '@app/shared/forms/form-validators/custom-config'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { CustomConfig } from '../../../../../../shared/models/config/custom-config.model'
|
||||
|
||||
@Component({
|
||||
selector: 'my-edit-custom-config',
|
||||
templateUrl: './edit-custom-config.component.html',
|
||||
styleUrls: [ './edit-custom-config.component.scss' ]
|
||||
})
|
||||
export class EditCustomConfigComponent extends FormReactive implements OnInit {
|
||||
customConfig: CustomConfig
|
||||
resolutions = [ '240p', '360p', '480p', '720p', '1080p' ]
|
||||
|
||||
videoQuotaOptions = [
|
||||
{ value: -1, label: 'Unlimited' },
|
||||
{ value: 0, label: '0' },
|
||||
{ value: 100 * 1024 * 1024, label: '100MB' },
|
||||
{ value: 500 * 1024 * 1024, label: '500MB' },
|
||||
{ value: 1024 * 1024 * 1024, label: '1GB' },
|
||||
{ value: 5 * 1024 * 1024 * 1024, label: '5GB' },
|
||||
{ value: 20 * 1024 * 1024 * 1024, label: '20GB' },
|
||||
{ value: 50 * 1024 * 1024 * 1024, label: '50GB' }
|
||||
]
|
||||
transcodingThreadOptions = [
|
||||
{ value: 1, label: '1' },
|
||||
{ value: 2, label: '2' },
|
||||
{ value: 4, label: '4' },
|
||||
{ value: 8, label: '8' }
|
||||
]
|
||||
|
||||
form: FormGroup
|
||||
formErrors = {
|
||||
cachePreviewsSize: '',
|
||||
signupLimit: '',
|
||||
adminEmail: '',
|
||||
userVideoQuota: '',
|
||||
transcodingThreads: ''
|
||||
}
|
||||
validationMessages = {
|
||||
cachePreviewsSize: CACHE_PREVIEWS_SIZE.MESSAGES,
|
||||
signupLimit: SIGNUP_LIMIT.MESSAGES,
|
||||
adminEmail: ADMIN_EMAIL.MESSAGES,
|
||||
userVideoQuota: USER_VIDEO_QUOTA.MESSAGES
|
||||
}
|
||||
|
||||
constructor (
|
||||
private formBuilder: FormBuilder,
|
||||
private router: Router,
|
||||
private notificationsService: NotificationsService,
|
||||
private configService: ConfigService,
|
||||
private serverService: ServerService
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
getResolutionKey (resolution: string) {
|
||||
return 'transcodingResolution' + resolution
|
||||
}
|
||||
|
||||
buildForm () {
|
||||
const formGroupData = {
|
||||
cachePreviewsSize: [ '', CACHE_PREVIEWS_SIZE.VALIDATORS ],
|
||||
signupEnabled: [ ],
|
||||
signupLimit: [ '', SIGNUP_LIMIT.VALIDATORS ],
|
||||
adminEmail: [ '', ADMIN_EMAIL.VALIDATORS ],
|
||||
userVideoQuota: [ '', USER_VIDEO_QUOTA.VALIDATORS ],
|
||||
transcodingThreads: [ '', TRANSCODING_THREADS.VALIDATORS ],
|
||||
transcodingEnabled: [ ]
|
||||
}
|
||||
|
||||
for (const resolution of this.resolutions) {
|
||||
const key = this.getResolutionKey(resolution)
|
||||
formGroupData[key] = [ false ]
|
||||
}
|
||||
|
||||
this.form = this.formBuilder.group(formGroupData)
|
||||
|
||||
this.form.valueChanges.subscribe(data => this.onValueChanged(data))
|
||||
}
|
||||
|
||||
ngOnInit () {
|
||||
this.buildForm()
|
||||
|
||||
this.configService.getCustomConfig()
|
||||
.subscribe(
|
||||
res => {
|
||||
this.customConfig = res
|
||||
|
||||
this.updateForm()
|
||||
},
|
||||
|
||||
err => this.notificationsService.error('Error', err.message)
|
||||
)
|
||||
}
|
||||
|
||||
isTranscodingEnabled () {
|
||||
return this.form.value['transcodingEnabled'] === true
|
||||
}
|
||||
|
||||
isSignupEnabled () {
|
||||
return this.form.value['signupEnabled'] === true
|
||||
}
|
||||
|
||||
formValidated () {
|
||||
const data = {
|
||||
cache: {
|
||||
previews: {
|
||||
size: this.form.value['cachePreviewsSize']
|
||||
}
|
||||
},
|
||||
signup: {
|
||||
enabled: this.form.value['signupEnabled'],
|
||||
limit: this.form.value['signupLimit']
|
||||
},
|
||||
admin: {
|
||||
email: this.form.value['adminEmail']
|
||||
},
|
||||
user: {
|
||||
videoQuota: this.form.value['userVideoQuota']
|
||||
},
|
||||
transcoding: {
|
||||
enabled: this.form.value['transcodingEnabled'],
|
||||
threads: this.form.value['transcodingThreads'],
|
||||
resolutions: {
|
||||
'240p': this.form.value[this.getResolutionKey('240p')],
|
||||
'360p': this.form.value[this.getResolutionKey('360p')],
|
||||
'480p': this.form.value[this.getResolutionKey('480p')],
|
||||
'720p': this.form.value[this.getResolutionKey('720p')],
|
||||
'1080p': this.form.value[this.getResolutionKey('1080p')]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.configService.updateCustomConfig(data)
|
||||
.subscribe(
|
||||
res => {
|
||||
this.customConfig = res
|
||||
|
||||
// Reload general configuration
|
||||
this.serverService.loadConfig()
|
||||
|
||||
this.updateForm()
|
||||
},
|
||||
|
||||
err => this.notificationsService.error('Error', err.message)
|
||||
)
|
||||
}
|
||||
|
||||
private updateForm () {
|
||||
const data = {
|
||||
cachePreviewsSize: this.customConfig.cache.previews.size,
|
||||
signupEnabled: this.customConfig.signup.enabled,
|
||||
signupLimit: this.customConfig.signup.limit,
|
||||
adminEmail: this.customConfig.admin.email,
|
||||
userVideoQuota: this.customConfig.user.videoQuota,
|
||||
transcodingThreads: this.customConfig.transcoding.threads,
|
||||
transcodingEnabled: this.customConfig.transcoding.enabled
|
||||
}
|
||||
|
||||
for (const resolution of this.resolutions) {
|
||||
const key = this.getResolutionKey(resolution)
|
||||
data[key] = this.customConfig.transcoding.resolutions[resolution]
|
||||
}
|
||||
|
||||
this.form.patchValue(data)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export * from './edit-custom-config.component'
|
|
@ -0,0 +1,3 @@
|
|||
export * from './edit-custom-config'
|
||||
export * from './config.component'
|
||||
export * from './config.routes'
|
|
@ -0,0 +1,26 @@
|
|||
import { HttpClient } from '@angular/common/http'
|
||||
import { Injectable } from '@angular/core'
|
||||
import { CustomConfig } from '../../../../../../shared/models/config/custom-config.model'
|
||||
import { environment } from '../../../../environments/environment'
|
||||
import { RestExtractor, RestService } from '../../../shared'
|
||||
|
||||
@Injectable()
|
||||
export class ConfigService {
|
||||
private static BASE_APPLICATION_URL = environment.apiUrl + '/api/v1/config'
|
||||
|
||||
constructor (
|
||||
private authHttp: HttpClient,
|
||||
private restService: RestService,
|
||||
private restExtractor: RestExtractor
|
||||
) {}
|
||||
|
||||
getCustomConfig () {
|
||||
return this.authHttp.get<CustomConfig>(ConfigService.BASE_APPLICATION_URL + '/custom')
|
||||
.catch(res => this.restExtractor.handleError(res))
|
||||
}
|
||||
|
||||
updateCustomConfig (data: CustomConfig) {
|
||||
return this.authHttp.put<CustomConfig>(ConfigService.BASE_APPLICATION_URL + '/custom', data)
|
||||
.catch(res => this.restExtractor.handleError(res))
|
||||
}
|
||||
}
|
|
@ -1,5 +1,3 @@
|
|||
<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
|
||||
|
||||
<form role="form" (ngSubmit)="updateDetails()" [formGroup]="form">
|
||||
<div class="form-group">
|
||||
<input
|
||||
|
|
|
@ -14,8 +14,6 @@ import { FormReactive, User, UserService } from '../../../shared'
|
|||
export class AccountDetailsComponent extends FormReactive implements OnInit {
|
||||
@Input() user: User = null
|
||||
|
||||
error: string = null
|
||||
|
||||
form: FormGroup
|
||||
formErrors = {}
|
||||
validationMessages = {}
|
||||
|
@ -50,7 +48,6 @@ export class AccountDetailsComponent extends FormReactive implements OnInit {
|
|||
autoPlayVideo
|
||||
}
|
||||
|
||||
this.error = null
|
||||
this.userService.updateMyDetails(details).subscribe(
|
||||
() => {
|
||||
this.notificationsService.success('Success', 'Information updated.')
|
||||
|
@ -58,7 +55,7 @@ export class AccountDetailsComponent extends FormReactive implements OnInit {
|
|||
this.authService.refreshUserInformation()
|
||||
},
|
||||
|
||||
err => this.error = err.message
|
||||
err => this.notificationsService.error('Error', err.message)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
<ul *dropdownMenu class="dropdown-menu">
|
||||
<li>
|
||||
<a routerLink="/account/settings" class="dropdown-item" title="My account">
|
||||
<a i18n routerLink="/account/settings" class="dropdown-item" title="My account">
|
||||
My account
|
||||
</a>
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import { Validators } from '@angular/forms'
|
||||
|
||||
export const CACHE_PREVIEWS_SIZE = {
|
||||
VALIDATORS: [ Validators.required, Validators.min(1), Validators.pattern('[0-9]+') ],
|
||||
MESSAGES: {
|
||||
'required': 'Preview cache size is required.',
|
||||
'min': 'Preview cache size must be greater than 1.',
|
||||
'pattern': 'Preview cache size must be a number.'
|
||||
}
|
||||
}
|
||||
|
||||
export const SIGNUP_LIMIT = {
|
||||
VALIDATORS: [ Validators.required, Validators.min(1), Validators.pattern('[0-9]+') ],
|
||||
MESSAGES: {
|
||||
'required': 'Signup limit is required.',
|
||||
'min': 'Signup limit must be greater than 1.',
|
||||
'pattern': 'Preview cache size must be a number.'
|
||||
}
|
||||
}
|
||||
|
||||
export const ADMIN_EMAIL = {
|
||||
VALIDATORS: [ Validators.required, Validators.email ],
|
||||
MESSAGES: {
|
||||
'required': 'Admin email is required.',
|
||||
'email': 'Admin email must be valid.'
|
||||
}
|
||||
}
|
||||
|
||||
export const TRANSCODING_THREADS = {
|
||||
VALIDATORS: [ Validators.required, Validators.min(1) ],
|
||||
MESSAGES: {
|
||||
'required': 'Transcoding threads is required.',
|
||||
'min': 'Transcoding threads must be greater than 1.'
|
||||
}
|
||||
}
|
|
@ -3,5 +3,7 @@
|
|||
for i in $(seq 1 6); do
|
||||
dropdb "peertube_test$i"
|
||||
rm -rf "./test$i"
|
||||
rm -f "./config/local-test.json"
|
||||
rm -f "./config/local-test-$i.json"
|
||||
createdb "peertube_test$i"
|
||||
done
|
||||
|
|
|
@ -35,6 +35,7 @@ git commit package.json client/package.json -m "Bumped to version $version" || e
|
|||
git tag -s -a "$version" -m "$version"
|
||||
|
||||
npm run build || exit -1
|
||||
rm "./client/dist/stats.json" || exit -1
|
||||
|
||||
cd ../ || exit -1
|
||||
|
||||
|
|
|
@ -1,15 +1,34 @@
|
|||
import * as express from 'express'
|
||||
import { ServerConfig, UserRight } from '../../../shared'
|
||||
import { CustomConfig } from '../../../shared/models/config/custom-config.model'
|
||||
import { unlinkPromise, writeFilePromise } from '../../helpers/core-utils'
|
||||
import { isSignupAllowed } from '../../helpers/utils'
|
||||
|
||||
import { CONFIG, CONSTRAINTS_FIELDS } from '../../initializers'
|
||||
import { asyncMiddleware } from '../../middlewares'
|
||||
import { ServerConfig } from '../../../shared'
|
||||
import { CONFIG, CONSTRAINTS_FIELDS, reloadConfig } from '../../initializers'
|
||||
import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../middlewares'
|
||||
import { customConfigUpdateValidator } from '../../middlewares/validators/config'
|
||||
import { omit } from 'lodash'
|
||||
|
||||
const configRouter = express.Router()
|
||||
|
||||
configRouter.get('/',
|
||||
asyncMiddleware(getConfig)
|
||||
)
|
||||
configRouter.get('/custom',
|
||||
authenticate,
|
||||
ensureUserHasRight(UserRight.MANAGE_CONFIGURATION),
|
||||
asyncMiddleware(getCustomConfig)
|
||||
)
|
||||
configRouter.put('/custom',
|
||||
authenticate,
|
||||
ensureUserHasRight(UserRight.MANAGE_CONFIGURATION),
|
||||
asyncMiddleware(customConfigUpdateValidator),
|
||||
asyncMiddleware(updateCustomConfig)
|
||||
)
|
||||
configRouter.delete('/custom',
|
||||
authenticate,
|
||||
ensureUserHasRight(UserRight.MANAGE_CONFIGURATION),
|
||||
asyncMiddleware(deleteCustomConfig)
|
||||
)
|
||||
|
||||
async function getConfig (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||
const allowed = await isSignupAllowed()
|
||||
|
@ -43,8 +62,72 @@ async function getConfig (req: express.Request, res: express.Response, next: exp
|
|||
return res.json(json)
|
||||
}
|
||||
|
||||
async function getCustomConfig (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||
const data = customConfig()
|
||||
|
||||
return res.json(data).end()
|
||||
}
|
||||
|
||||
async function deleteCustomConfig (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||
await unlinkPromise(CONFIG.CUSTOM_FILE)
|
||||
|
||||
reloadConfig()
|
||||
|
||||
const data = customConfig()
|
||||
|
||||
return res.json(data).end()
|
||||
}
|
||||
|
||||
async function updateCustomConfig (req: express.Request, res: express.Response, next: express.NextFunction) {
|
||||
const toUpdate: CustomConfig = req.body
|
||||
|
||||
// Need to change the videoQuota key a little bit
|
||||
const toUpdateJSON = omit(toUpdate, 'videoQuota')
|
||||
toUpdateJSON.user['video_quota'] = toUpdate.user.videoQuota
|
||||
|
||||
await writeFilePromise(CONFIG.CUSTOM_FILE, JSON.stringify(toUpdateJSON))
|
||||
|
||||
reloadConfig()
|
||||
|
||||
const data = customConfig()
|
||||
return res.json(data).end()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
configRouter
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function customConfig (): CustomConfig {
|
||||
return {
|
||||
cache: {
|
||||
previews: {
|
||||
size: CONFIG.CACHE.PREVIEWS.SIZE
|
||||
}
|
||||
},
|
||||
signup: {
|
||||
enabled: CONFIG.SIGNUP.ENABLED,
|
||||
limit: CONFIG.SIGNUP.LIMIT
|
||||
},
|
||||
admin: {
|
||||
email: CONFIG.ADMIN.EMAIL
|
||||
},
|
||||
user: {
|
||||
videoQuota: CONFIG.USER.VIDEO_QUOTA
|
||||
},
|
||||
transcoding: {
|
||||
enabled: CONFIG.TRANSCODING.ENABLED,
|
||||
threads: CONFIG.TRANSCODING.THREADS,
|
||||
resolutions: {
|
||||
'240p': CONFIG.TRANSCODING.RESOLUTIONS[ '240p' ],
|
||||
'360p': CONFIG.TRANSCODING.RESOLUTIONS[ '360p' ],
|
||||
'480p': CONFIG.TRANSCODING.RESOLUTIONS[ '480p' ],
|
||||
'720p': CONFIG.TRANSCODING.RESOLUTIONS[ '720p' ],
|
||||
'1080p': CONFIG.TRANSCODING.RESOLUTIONS[ '1080p' ]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,7 +104,7 @@ function computeResolutionsToTranscode (videoFileHeight: number) {
|
|||
]
|
||||
|
||||
for (const resolution of resolutions) {
|
||||
if (configResolutions[resolution.toString()] === true && videoFileHeight > resolution) {
|
||||
if (configResolutions[resolution + 'p'] === true && videoFileHeight > resolution) {
|
||||
resolutionsEnabled.push(resolution)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import * as config from 'config'
|
||||
import { join } from 'path'
|
||||
import { IConfig } from 'config'
|
||||
import { dirname, join } from 'path'
|
||||
import { JobCategory, JobState, VideoRateType } from '../../shared/models'
|
||||
import { ActivityPubActorType } from '../../shared/models/activitypub'
|
||||
import { FollowState } from '../../shared/models/actors'
|
||||
import { VideoPrivacy } from '../../shared/models/videos'
|
||||
// Do not use barrels, remain constants as independent as possible
|
||||
import { buildPath, isTestInstance, sanitizeHost, sanitizeUrl } from '../helpers/core-utils'
|
||||
import { buildPath, isTestInstance, root, sanitizeHost, sanitizeUrl } from '../helpers/core-utils'
|
||||
|
||||
// Use a variable to reload the configuration if we need
|
||||
let config: IConfig = require('config')
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
@ -82,6 +85,7 @@ let SCHEDULER_INTERVAL = 60000 * 60
|
|||
// ---------------------------------------------------------------------------
|
||||
|
||||
const CONFIG = {
|
||||
CUSTOM_FILE: getLocalConfigFilePath(),
|
||||
LISTEN: {
|
||||
PORT: config.get<number>('listen.port')
|
||||
},
|
||||
|
@ -110,29 +114,29 @@ const CONFIG = {
|
|||
HOST: ''
|
||||
},
|
||||
ADMIN: {
|
||||
EMAIL: config.get<string>('admin.email')
|
||||
get EMAIL () { return config.get<string>('admin.email') }
|
||||
},
|
||||
SIGNUP: {
|
||||
ENABLED: config.get<boolean>('signup.enabled'),
|
||||
LIMIT: config.get<number>('signup.limit')
|
||||
get ENABLED () { return config.get<boolean>('signup.enabled') },
|
||||
get LIMIT () { return config.get<number>('signup.limit') }
|
||||
},
|
||||
USER: {
|
||||
VIDEO_QUOTA: config.get<number>('user.video_quota')
|
||||
get VIDEO_QUOTA () { return config.get<number>('user.video_quota') }
|
||||
},
|
||||
TRANSCODING: {
|
||||
ENABLED: config.get<boolean>('transcoding.enabled'),
|
||||
THREADS: config.get<number>('transcoding.threads'),
|
||||
get ENABLED () { return config.get<boolean>('transcoding.enabled') },
|
||||
get THREADS () { return config.get<number>('transcoding.threads') },
|
||||
RESOLUTIONS: {
|
||||
'240' : config.get<boolean>('transcoding.resolutions.240p'),
|
||||
'360': config.get<boolean>('transcoding.resolutions.360p'),
|
||||
'480': config.get<boolean>('transcoding.resolutions.480p'),
|
||||
'720': config.get<boolean>('transcoding.resolutions.720p'),
|
||||
'1080': config.get<boolean>('transcoding.resolutions.1080p')
|
||||
get '240p' () { return config.get<boolean>('transcoding.resolutions.240p') },
|
||||
get '360p' () { return config.get<boolean>('transcoding.resolutions.360p') },
|
||||
get '480p' () { return config.get<boolean>('transcoding.resolutions.480p') },
|
||||
get '720p' () { return config.get<boolean>('transcoding.resolutions.720p') },
|
||||
get '1080p' () { return config.get<boolean>('transcoding.resolutions.1080p') }
|
||||
}
|
||||
},
|
||||
CACHE: {
|
||||
PREVIEWS: {
|
||||
SIZE: config.get<number>('cache.previews.size')
|
||||
get SIZE () { return config.get<number>('cache.previews.size') }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -361,8 +365,7 @@ if (isTestInstance() === true) {
|
|||
SCHEDULER_INTERVAL = 10000
|
||||
}
|
||||
|
||||
CONFIG.WEBSERVER.URL = sanitizeUrl(CONFIG.WEBSERVER.SCHEME + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT)
|
||||
CONFIG.WEBSERVER.HOST = sanitizeHost(CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT, REMOTE_SCHEME.HTTP)
|
||||
updateWebserverConfig()
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
@ -404,3 +407,50 @@ export {
|
|||
AVATAR_MIMETYPE_EXT,
|
||||
SCHEDULER_INTERVAL
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function getLocalConfigFilePath () {
|
||||
const configSources = config.util.getConfigSources()
|
||||
if (configSources.length === 0) throw new Error('Invalid config source.')
|
||||
|
||||
let filename = 'local'
|
||||
if (process.env.NODE_ENV) filename += `-${process.env.NODE_ENV}`
|
||||
if (process.env.NODE_APP_INSTANCE) filename += `-${process.env.NODE_APP_INSTANCE}`
|
||||
|
||||
return join(dirname(configSources[ 0 ].name), filename + '.json')
|
||||
}
|
||||
|
||||
function updateWebserverConfig () {
|
||||
CONFIG.WEBSERVER.URL = sanitizeUrl(CONFIG.WEBSERVER.SCHEME + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT)
|
||||
CONFIG.WEBSERVER.HOST = sanitizeHost(CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT, REMOTE_SCHEME.HTTP)
|
||||
}
|
||||
|
||||
export function reloadConfig () {
|
||||
|
||||
function directory () {
|
||||
if (process.env.NODE_CONFIG_DIR) {
|
||||
return process.env.NODE_CONFIG_DIR
|
||||
}
|
||||
|
||||
return join(root(), 'config')
|
||||
}
|
||||
|
||||
function purge () {
|
||||
for (const fileName in require.cache) {
|
||||
if (-1 === fileName.indexOf(directory())) {
|
||||
continue
|
||||
}
|
||||
|
||||
delete require.cache[fileName]
|
||||
}
|
||||
|
||||
delete require.cache[require.resolve('config')]
|
||||
}
|
||||
|
||||
purge()
|
||||
|
||||
config = require('config')
|
||||
|
||||
updateWebserverConfig()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
import * as express from 'express'
|
||||
import { body } from 'express-validator/check'
|
||||
import { isUserVideoQuotaValid } from '../../helpers/custom-validators/users'
|
||||
import { logger } from '../../helpers/logger'
|
||||
import { areValidationErrors } from './utils'
|
||||
|
||||
const customConfigUpdateValidator = [
|
||||
body('cache.previews.size').isInt().withMessage('Should have a valid previews size'),
|
||||
body('signup.enabled').isBoolean().withMessage('Should have a valid signup enabled boolean'),
|
||||
body('signup.limit').isInt().withMessage('Should have a valid signup limit'),
|
||||
body('admin.email').isEmail().withMessage('Should have a valid administrator email'),
|
||||
body('user.videoQuota').custom(isUserVideoQuotaValid).withMessage('Should have a valid video quota'),
|
||||
body('transcoding.enabled').isBoolean().withMessage('Should have a valid transcoding enabled boolean'),
|
||||
body('transcoding.threads').isInt().withMessage('Should have a valid transcoding threads number'),
|
||||
body('transcoding.resolutions.240p').isBoolean().withMessage('Should have a valid transcoding 240p resolution enabled boolean'),
|
||||
body('transcoding.resolutions.360p').isBoolean().withMessage('Should have a valid transcoding 360p resolution enabled boolean'),
|
||||
body('transcoding.resolutions.480p').isBoolean().withMessage('Should have a valid transcoding 480p resolution enabled boolean'),
|
||||
body('transcoding.resolutions.720p').isBoolean().withMessage('Should have a valid transcoding 720p resolution enabled boolean'),
|
||||
body('transcoding.resolutions.1080p').isBoolean().withMessage('Should have a valid transcoding 1080p resolution enabled boolean'),
|
||||
|
||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
logger.debug('Checking customConfigUpdateValidator parameters', { parameters: req.body })
|
||||
|
||||
if (areValidationErrors(req, res)) return
|
||||
|
||||
return next()
|
||||
}
|
||||
]
|
||||
|
||||
export {
|
||||
customConfigUpdateValidator
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
/* tslint:disable:no-unused-expression */
|
||||
|
||||
import { omit } from 'lodash'
|
||||
import 'mocha'
|
||||
import { CustomConfig } from '../../../../shared/models/config/custom-config.model'
|
||||
|
||||
import {
|
||||
createUser, flushTests, killallServers, makeDeleteRequest, makeGetRequest, makePutBodyRequest, runServer, ServerInfo,
|
||||
setAccessTokensToServers, userLogin
|
||||
} from '../../utils'
|
||||
|
||||
describe('Test config API validators', function () {
|
||||
const path = '/api/v1/config/custom'
|
||||
let server: ServerInfo
|
||||
let userAccessToken: string
|
||||
const updateParams: CustomConfig = {
|
||||
cache: {
|
||||
previews: {
|
||||
size: 2
|
||||
}
|
||||
},
|
||||
signup: {
|
||||
enabled: false,
|
||||
limit: 5
|
||||
},
|
||||
admin: {
|
||||
email: 'superadmin1@example.com'
|
||||
},
|
||||
user: {
|
||||
videoQuota: 5242881
|
||||
},
|
||||
transcoding: {
|
||||
enabled: true,
|
||||
threads: 1,
|
||||
resolutions: {
|
||||
'240p': false,
|
||||
'360p': true,
|
||||
'480p': true,
|
||||
'720p': false,
|
||||
'1080p': false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
before(async function () {
|
||||
this.timeout(20000)
|
||||
|
||||
await flushTests()
|
||||
server = await runServer(1)
|
||||
|
||||
await setAccessTokensToServers([ server ])
|
||||
|
||||
const user = {
|
||||
username: 'user1',
|
||||
password: 'password'
|
||||
}
|
||||
await createUser(server.url, server.accessToken, user.username, user.password)
|
||||
userAccessToken = await userLogin(server, user)
|
||||
})
|
||||
|
||||
describe('When getting the configuration', function () {
|
||||
it('Should fail without token', async function () {
|
||||
await makeGetRequest({
|
||||
url: server.url,
|
||||
path,
|
||||
statusCodeExpected: 401
|
||||
})
|
||||
})
|
||||
|
||||
it('Should fail if the user is not an administrator', async function () {
|
||||
await makeGetRequest({
|
||||
url: server.url,
|
||||
path,
|
||||
token: userAccessToken,
|
||||
statusCodeExpected: 403
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('When updating the configuration', function () {
|
||||
it('Should fail without token', async function () {
|
||||
await makePutBodyRequest({
|
||||
url: server.url,
|
||||
path,
|
||||
fields: updateParams,
|
||||
statusCodeExpected: 401
|
||||
})
|
||||
})
|
||||
|
||||
it('Should fail if the user is not an administrator', async function () {
|
||||
await makePutBodyRequest({
|
||||
url: server.url,
|
||||
path,
|
||||
fields: updateParams,
|
||||
token: userAccessToken,
|
||||
statusCodeExpected: 403
|
||||
})
|
||||
})
|
||||
|
||||
it('Should fail if it misses a key', async function () {
|
||||
const newUpdateParams = omit(updateParams, 'admin.email')
|
||||
|
||||
await makePutBodyRequest({
|
||||
url: server.url,
|
||||
path,
|
||||
fields: newUpdateParams,
|
||||
token: server.accessToken,
|
||||
statusCodeExpected: 400
|
||||
})
|
||||
})
|
||||
|
||||
it('Should success with the correct parameters', async function () {
|
||||
await makePutBodyRequest({
|
||||
url: server.url,
|
||||
path,
|
||||
fields: updateParams,
|
||||
token: server.accessToken,
|
||||
statusCodeExpected: 200
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('When deleting the configuration', function () {
|
||||
it('Should fail without token', async function () {
|
||||
await makeDeleteRequest({
|
||||
url: server.url,
|
||||
path,
|
||||
statusCodeExpected: 401
|
||||
})
|
||||
})
|
||||
|
||||
it('Should fail if the user is not an administrator', async function () {
|
||||
await makeDeleteRequest({
|
||||
url: server.url,
|
||||
path,
|
||||
token: userAccessToken,
|
||||
statusCodeExpected: 403
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
killallServers([ server ])
|
||||
|
||||
// Keep the logs if the test failed
|
||||
if (this['ok']) {
|
||||
await flushTests()
|
||||
}
|
||||
})
|
||||
})
|
|
@ -2,13 +2,14 @@
|
|||
|
||||
import 'mocha'
|
||||
import * as chai from 'chai'
|
||||
import { deleteCustomConfig, killallServers, reRunServer } from '../../utils'
|
||||
const expect = chai.expect
|
||||
|
||||
import {
|
||||
getConfig,
|
||||
flushTests,
|
||||
runServer,
|
||||
registerUser
|
||||
registerUser, getCustomConfig, setAccessTokensToServers, updateCustomConfig
|
||||
} from '../../utils/index'
|
||||
|
||||
describe('Test config', function () {
|
||||
|
@ -19,6 +20,7 @@ describe('Test config', function () {
|
|||
|
||||
await flushTests()
|
||||
server = await runServer(1)
|
||||
await setAccessTokensToServers([ server ])
|
||||
})
|
||||
|
||||
it('Should have a correct config on a server with registration enabled', async function () {
|
||||
|
@ -43,6 +45,114 @@ describe('Test config', function () {
|
|||
expect(data.signup.allowed).to.be.false
|
||||
})
|
||||
|
||||
it('Should get the customized configuration', async function () {
|
||||
const res = await getCustomConfig(server.url, server.accessToken)
|
||||
const data = res.body
|
||||
|
||||
expect(data.cache.previews.size).to.equal(1)
|
||||
expect(data.signup.enabled).to.be.true
|
||||
expect(data.signup.limit).to.equal(4)
|
||||
expect(data.admin.email).to.equal('admin1@example.com')
|
||||
expect(data.user.videoQuota).to.equal(5242880)
|
||||
expect(data.transcoding.enabled).to.be.false
|
||||
expect(data.transcoding.threads).to.equal(2)
|
||||
expect(data.transcoding.resolutions['240p']).to.be.true
|
||||
expect(data.transcoding.resolutions['360p']).to.be.true
|
||||
expect(data.transcoding.resolutions['480p']).to.be.true
|
||||
expect(data.transcoding.resolutions['720p']).to.be.true
|
||||
expect(data.transcoding.resolutions['1080p']).to.be.true
|
||||
})
|
||||
|
||||
it('Should update the customized configuration', async function () {
|
||||
const newCustomConfig = {
|
||||
cache: {
|
||||
previews: {
|
||||
size: 2
|
||||
}
|
||||
},
|
||||
signup: {
|
||||
enabled: false,
|
||||
limit: 5
|
||||
},
|
||||
admin: {
|
||||
email: 'superadmin1@example.com'
|
||||
},
|
||||
user: {
|
||||
videoQuota: 5242881
|
||||
},
|
||||
transcoding: {
|
||||
enabled: true,
|
||||
threads: 1,
|
||||
resolutions: {
|
||||
'240p': false,
|
||||
'360p': true,
|
||||
'480p': true,
|
||||
'720p': false,
|
||||
'1080p': false
|
||||
}
|
||||
}
|
||||
}
|
||||
await updateCustomConfig(server.url, server.accessToken, newCustomConfig)
|
||||
|
||||
const res = await getCustomConfig(server.url, server.accessToken)
|
||||
const data = res.body
|
||||
|
||||
expect(data.cache.previews.size).to.equal(2)
|
||||
expect(data.signup.enabled).to.be.false
|
||||
expect(data.signup.limit).to.equal(5)
|
||||
expect(data.admin.email).to.equal('superadmin1@example.com')
|
||||
expect(data.user.videoQuota).to.equal(5242881)
|
||||
expect(data.transcoding.enabled).to.be.true
|
||||
expect(data.transcoding.threads).to.equal(1)
|
||||
expect(data.transcoding.resolutions['240p']).to.be.false
|
||||
expect(data.transcoding.resolutions['360p']).to.be.true
|
||||
expect(data.transcoding.resolutions['480p']).to.be.true
|
||||
expect(data.transcoding.resolutions['720p']).to.be.false
|
||||
expect(data.transcoding.resolutions['1080p']).to.be.false
|
||||
})
|
||||
|
||||
it('Should have the configuration updated after a restart', async function () {
|
||||
killallServers([ server ])
|
||||
|
||||
await reRunServer(server)
|
||||
|
||||
const res = await getCustomConfig(server.url, server.accessToken)
|
||||
const data = res.body
|
||||
|
||||
expect(data.cache.previews.size).to.equal(2)
|
||||
expect(data.signup.enabled).to.be.false
|
||||
expect(data.signup.limit).to.equal(5)
|
||||
expect(data.admin.email).to.equal('superadmin1@example.com')
|
||||
expect(data.user.videoQuota).to.equal(5242881)
|
||||
expect(data.transcoding.enabled).to.be.true
|
||||
expect(data.transcoding.threads).to.equal(1)
|
||||
expect(data.transcoding.resolutions['240p']).to.be.false
|
||||
expect(data.transcoding.resolutions['360p']).to.be.true
|
||||
expect(data.transcoding.resolutions['480p']).to.be.true
|
||||
expect(data.transcoding.resolutions['720p']).to.be.false
|
||||
expect(data.transcoding.resolutions['1080p']).to.be.false
|
||||
})
|
||||
|
||||
it('Should remove the custom configuration', async function () {
|
||||
await deleteCustomConfig(server.url, server.accessToken)
|
||||
|
||||
const res = await getCustomConfig(server.url, server.accessToken)
|
||||
const data = res.body
|
||||
|
||||
expect(data.cache.previews.size).to.equal(1)
|
||||
expect(data.signup.enabled).to.be.true
|
||||
expect(data.signup.limit).to.equal(4)
|
||||
expect(data.admin.email).to.equal('admin1@example.com')
|
||||
expect(data.user.videoQuota).to.equal(5242880)
|
||||
expect(data.transcoding.enabled).to.be.false
|
||||
expect(data.transcoding.threads).to.equal(2)
|
||||
expect(data.transcoding.resolutions['240p']).to.be.true
|
||||
expect(data.transcoding.resolutions['360p']).to.be.true
|
||||
expect(data.transcoding.resolutions['480p']).to.be.true
|
||||
expect(data.transcoding.resolutions['720p']).to.be.true
|
||||
expect(data.transcoding.resolutions['1080p']).to.be.true
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
process.kill(-server.app.pid)
|
||||
|
||||
|
|
|
@ -5,9 +5,10 @@ import { keyBy } from 'lodash'
|
|||
import 'mocha'
|
||||
import { join } from 'path'
|
||||
import { VideoPrivacy } from '../../../../shared/models/videos'
|
||||
import { readdirPromise } from '../../../helpers/core-utils'
|
||||
import {
|
||||
completeVideoCheck, flushTests, getVideo, getVideoCategories, getVideoLanguages, getVideoLicences, getVideoPrivacies,
|
||||
getVideosList, getVideosListPagination, getVideosListSort, killallServers, rateVideo, readdirPromise, removeVideo, runServer, searchVideo,
|
||||
getVideosList, getVideosListPagination, getVideosListSort, killallServers, rateVideo, removeVideo, runServer, searchVideo,
|
||||
searchVideoWithPagination, searchVideoWithSort, ServerInfo, setAccessTokensToServers, testVideoImage, updateVideo, uploadVideo, viewVideo
|
||||
} from '../../utils'
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import * as WebTorrent from 'webtorrent'
|
||||
import { readFile, readdir } from 'fs'
|
||||
|
||||
let webtorrent = new WebTorrent()
|
||||
|
||||
|
@ -7,26 +6,6 @@ function immutableAssign <T, U> (target: T, source: U) {
|
|||
return Object.assign<{}, T, U>({}, target, source)
|
||||
}
|
||||
|
||||
function readFilePromise (path: string) {
|
||||
return new Promise<Buffer>((res, rej) => {
|
||||
readFile(path, (err, data) => {
|
||||
if (err) return rej(err)
|
||||
|
||||
return res(data)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function readdirPromise (path: string) {
|
||||
return new Promise<string[]>((res, rej) => {
|
||||
readdir(path, (err, files) => {
|
||||
if (err) return rej(err)
|
||||
|
||||
return res(files)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Default interval -> 5 minutes
|
||||
function dateIsValid (dateString: string, interval = 300000) {
|
||||
const dateToCheck = new Date(dateString)
|
||||
|
@ -48,8 +27,6 @@ function webtorrentAdd (torrent: string, refreshWebTorrent = false) {
|
|||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
readFilePromise,
|
||||
readdirPromise,
|
||||
dateIsValid,
|
||||
wait,
|
||||
webtorrentAdd,
|
||||
|
|
|
@ -99,7 +99,7 @@ function makePostBodyRequest (options: {
|
|||
function makePutBodyRequest (options: {
|
||||
url: string,
|
||||
path: string,
|
||||
token: string,
|
||||
token?: string,
|
||||
fields: { [ fieldName: string ]: any },
|
||||
statusCodeExpected?: number
|
||||
}) {
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import * as request from 'supertest'
|
||||
import { makeDeleteRequest, makeGetRequest, makePutBodyRequest } from '../'
|
||||
import { CustomConfig } from '../../../../shared/models/config/custom-config.model'
|
||||
|
||||
function getConfig (url: string) {
|
||||
const path = '/api/v1/config'
|
||||
|
@ -10,8 +12,45 @@ function getConfig (url: string) {
|
|||
.expect('Content-Type', /json/)
|
||||
}
|
||||
|
||||
function getCustomConfig (url: string, token: string, statusCodeExpected = 200) {
|
||||
const path = '/api/v1/config/custom'
|
||||
|
||||
return makeGetRequest({
|
||||
url,
|
||||
token,
|
||||
path,
|
||||
statusCodeExpected
|
||||
})
|
||||
}
|
||||
|
||||
function updateCustomConfig (url: string, token: string, newCustomConfig: CustomConfig, statusCodeExpected = 200) {
|
||||
const path = '/api/v1/config/custom'
|
||||
|
||||
return makePutBodyRequest({
|
||||
url,
|
||||
token,
|
||||
path,
|
||||
fields: newCustomConfig,
|
||||
statusCodeExpected
|
||||
})
|
||||
}
|
||||
|
||||
function deleteCustomConfig (url: string, token: string, statusCodeExpected = 200) {
|
||||
const path = '/api/v1/config/custom'
|
||||
|
||||
return makeDeleteRequest({
|
||||
url,
|
||||
token,
|
||||
path,
|
||||
statusCodeExpected
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
getConfig
|
||||
getConfig,
|
||||
getCustomConfig,
|
||||
updateCustomConfig,
|
||||
deleteCustomConfig
|
||||
}
|
||||
|
|
|
@ -5,8 +5,9 @@ import { readFile } from 'fs'
|
|||
import * as parseTorrent from 'parse-torrent'
|
||||
import { extname, isAbsolute, join } from 'path'
|
||||
import * as request from 'supertest'
|
||||
import { getMyUserInformation, makeGetRequest, readFilePromise, ServerInfo } from '../'
|
||||
import { getMyUserInformation, makeGetRequest, ServerInfo } from '../'
|
||||
import { VideoPrivacy } from '../../../../shared/models/videos'
|
||||
import { readFileBufferPromise } from '../../../helpers/core-utils'
|
||||
import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../initializers'
|
||||
import { dateIsValid, webtorrentAdd } from '../index'
|
||||
|
||||
|
@ -210,7 +211,7 @@ async function testVideoImage (url: string, imageName: string, imagePath: string
|
|||
.get(imagePath)
|
||||
.expect(200)
|
||||
|
||||
const data = await readFilePromise(join(__dirname, '..', '..', 'api', 'fixtures', imageName + extension))
|
||||
const data = await readFileBufferPromise(join(__dirname, '..', '..', 'api', 'fixtures', imageName + extension))
|
||||
|
||||
return data.equals(res.body)
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
export interface CustomConfig {
|
||||
cache: {
|
||||
previews: {
|
||||
size: number
|
||||
}
|
||||
}
|
||||
|
||||
signup: {
|
||||
enabled: boolean
|
||||
limit: number
|
||||
}
|
||||
|
||||
admin: {
|
||||
email: string
|
||||
}
|
||||
|
||||
user: {
|
||||
videoQuota: number
|
||||
}
|
||||
|
||||
transcoding: {
|
||||
enabled: boolean
|
||||
threads: number
|
||||
resolutions: {
|
||||
'240p': boolean
|
||||
'360p': boolean
|
||||
'480p': boolean
|
||||
'720p': boolean
|
||||
'1080p': boolean
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,4 +5,4 @@ export * from './videos'
|
|||
export * from './job.model'
|
||||
export * from './oauth-client-local.model'
|
||||
export * from './result-list.model'
|
||||
export * from './server-config.model'
|
||||
export * from './config/server-config.model'
|
||||
|
|
|
@ -5,6 +5,7 @@ export enum UserRight {
|
|||
MANAGE_VIDEO_ABUSES,
|
||||
MANAGE_VIDEO_BLACKLIST,
|
||||
MANAGE_JOBS,
|
||||
MANAGE_CONFIGURATION,
|
||||
REMOVE_ANY_VIDEO,
|
||||
REMOVE_ANY_VIDEO_CHANNEL,
|
||||
REMOVE_ANY_VIDEO_COMMENT
|
||||
|
|
|
@ -31,7 +31,7 @@ $ VERSION=$(curl -s https://api.github.com/repos/chocobozzz/peertube/releases/la
|
|||
cd /home/peertube && \
|
||||
sudo -u peertube mkdir config storage versions && \
|
||||
cd versions && \
|
||||
sudo -u peertube wget "https://github.com/Chocobozzz/PeerTube/releases/download/${VERSION}/peertube-${VERSION}.zip" && \
|
||||
sudo -u peertube wget -q "https://github.com/Chocobozzz/PeerTube/releases/download/${VERSION}/peertube-${VERSION}.zip" && \
|
||||
sudo -u peertube unzip peertube-${VERSION}.zip && sudo -u peertube rm peertube-${VERSION}.zip && \
|
||||
cd ../ && sudo -u peertube ln -s versions/peertube-${VERSION} ./peertube-latest && \
|
||||
cd ./peertube-latest && sudo -u peertube yarn install --production --pure-lockfile
|
||||
|
@ -227,7 +227,7 @@ $ NODE_ENV=production npm run reset-password -- -u root
|
|||
```
|
||||
$ VERSION=$(curl -s https://api.github.com/repos/chocobozzz/peertube/releases/latest | grep tag_name | cut -d '"' -f 4) && \
|
||||
cd /home/peertube/versions && \
|
||||
sudo -u peertube wget "https://github.com/Chocobozzz/PeerTube/releases/download/${VERSION}/peertube-${VERSION}.zip" && \
|
||||
sudo -u peertube wget -q "https://github.com/Chocobozzz/PeerTube/releases/download/${VERSION}/peertube-${VERSION}.zip" && \
|
||||
sudo -u peertube unzip -o peertube-${VERSION}.zip && sudo -u peertube rm peertube-${VERSION}.zip && \
|
||||
cd ../ && sudo rm ./peertube-latest && sudo -u peertube ln -s versions/peertube-${VERSION} ./peertube-latest && \
|
||||
cd ./peertube-latest && sudo -u peertube yarn install --production --pure-lockfile && \
|
||||
|
|
Loading…
Reference in New Issue