aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2020-04-22 16:07:04 +0200
committerChocobozzz <chocobozzz@cpy.re>2020-05-04 16:21:39 +0200
commit7fed637506043e4432cbebe041ada0625171cceb (patch)
tree07f174e17c4b4a0b3d43a0fa6944865c06234338 /server/lib
parent8d4197637868d5cde49434e937186b57e40f4b2b (diff)
downloadPeerTube-7fed637506043e4432cbebe041ada0625171cceb.tar.gz
PeerTube-7fed637506043e4432cbebe041ada0625171cceb.tar.zst
PeerTube-7fed637506043e4432cbebe041ada0625171cceb.zip
Begin auth plugin support
Diffstat (limited to 'server/lib')
-rw-r--r--server/lib/auth.ts101
-rw-r--r--server/lib/oauth-model.ts76
-rw-r--r--server/lib/plugins/plugin-manager.ts55
-rw-r--r--server/lib/plugins/register-helpers-store.ts43
-rw-r--r--server/lib/user.ts3
-rw-r--r--server/lib/video-channel.ts4
6 files changed, 243 insertions, 39 deletions
diff --git a/server/lib/auth.ts b/server/lib/auth.ts
new file mode 100644
index 000000000..18d52fa5a
--- /dev/null
+++ b/server/lib/auth.ts
@@ -0,0 +1,101 @@
1import * as express from 'express'
2import { OAUTH_LIFETIME } from '@server/initializers/constants'
3import * as OAuthServer from 'express-oauth-server'
4import { PluginManager } from '@server/lib/plugins/plugin-manager'
5import { RegisterServerAuthPassOptions } from '@shared/models/plugins/register-server-auth.model'
6import { logger } from '@server/helpers/logger'
7import { UserRole } from '@shared/models'
8
9const oAuthServer = new OAuthServer({
10 useErrorHandler: true,
11 accessTokenLifetime: OAUTH_LIFETIME.ACCESS_TOKEN,
12 refreshTokenLifetime: OAUTH_LIFETIME.REFRESH_TOKEN,
13 continueMiddleware: true,
14 model: require('./oauth-model')
15})
16
17function onExternalAuthPlugin (npmName: string, username: string, email: string) {
18
19}
20
21async function handleIdAndPassLogin (req: express.Request, res: express.Response, next: express.NextFunction) {
22 const plugins = PluginManager.Instance.getIdAndPassAuths()
23 const pluginAuths: { npmName?: string, registerAuthOptions: RegisterServerAuthPassOptions }[] = []
24
25 for (const plugin of plugins) {
26 const auths = plugin.idAndPassAuths
27
28 for (const auth of auths) {
29 pluginAuths.push({
30 npmName: plugin.npmName,
31 registerAuthOptions: auth
32 })
33 }
34 }
35
36 pluginAuths.sort((a, b) => {
37 const aWeight = a.registerAuthOptions.getWeight()
38 const bWeight = b.registerAuthOptions.getWeight()
39
40 if (aWeight === bWeight) return 0
41 if (aWeight > bWeight) return 1
42 return -1
43 })
44
45 const loginOptions = {
46 id: req.body.username,
47 password: req.body.password
48 }
49
50 for (const pluginAuth of pluginAuths) {
51 logger.debug(
52 'Using auth method of %s to login %s with weight %d.',
53 pluginAuth.npmName, loginOptions.id, pluginAuth.registerAuthOptions.getWeight()
54 )
55
56 const loginResult = await pluginAuth.registerAuthOptions.login(loginOptions)
57 if (loginResult) {
58 logger.info('Login success with plugin %s for %s.', pluginAuth.npmName, loginOptions.id)
59
60 res.locals.bypassLogin = {
61 bypass: true,
62 pluginName: pluginAuth.npmName,
63 user: {
64 username: loginResult.username,
65 email: loginResult.email,
66 role: loginResult.role || UserRole.USER,
67 displayName: loginResult.displayName || loginResult.username
68 }
69 }
70
71 break
72 }
73 }
74
75 return localLogin(req, res, next)
76}
77
78// ---------------------------------------------------------------------------
79
80export {
81 oAuthServer,
82 handleIdAndPassLogin,
83 onExternalAuthPlugin
84}
85
86// ---------------------------------------------------------------------------
87
88function localLogin (req: express.Request, res: express.Response, next: express.NextFunction) {
89 return oAuthServer.token()(req, res, err => {
90 if (err) {
91 return res.status(err.status)
92 .json({
93 error: err.message,
94 code: err.name
95 })
96 .end()
97 }
98
99 return next()
100 })
101}
diff --git a/server/lib/oauth-model.ts b/server/lib/oauth-model.ts
index 086856f41..ea4a67802 100644
--- a/server/lib/oauth-model.ts
+++ b/server/lib/oauth-model.ts
@@ -1,4 +1,5 @@
1import * as Bluebird from 'bluebird' 1import * as Bluebird from 'bluebird'
2import * as express from 'express'
2import { AccessDeniedError } from 'oauth2-server' 3import { AccessDeniedError } from 'oauth2-server'
3import { logger } from '../helpers/logger' 4import { logger } from '../helpers/logger'
4import { UserModel } from '../models/account/user' 5import { UserModel } from '../models/account/user'
@@ -9,6 +10,10 @@ import { Transaction } from 'sequelize'
9import { CONFIG } from '../initializers/config' 10import { CONFIG } from '../initializers/config'
10import * as LRUCache from 'lru-cache' 11import * as LRUCache from 'lru-cache'
11import { MOAuthTokenUser } from '@server/typings/models/oauth/oauth-token' 12import { MOAuthTokenUser } from '@server/typings/models/oauth/oauth-token'
13import { MUser } from '@server/typings/models/user/user'
14import { UserAdminFlag } from '@shared/models/users/user-flag.model'
15import { createUserAccountAndChannelAndPlaylist } from './user'
16import { UserRole } from '@shared/models/users/user-role'
12 17
13type TokenInfo = { accessToken: string, refreshToken: string, accessTokenExpiresAt: Date, refreshTokenExpiresAt: Date } 18type TokenInfo = { accessToken: string, refreshToken: string, accessTokenExpiresAt: Date, refreshTokenExpiresAt: Date }
14 19
@@ -49,14 +54,14 @@ function getAccessToken (bearerToken: string) {
49 if (accessTokenCache.has(bearerToken)) return Bluebird.resolve(accessTokenCache.get(bearerToken)) 54 if (accessTokenCache.has(bearerToken)) return Bluebird.resolve(accessTokenCache.get(bearerToken))
50 55
51 return OAuthTokenModel.getByTokenAndPopulateUser(bearerToken) 56 return OAuthTokenModel.getByTokenAndPopulateUser(bearerToken)
52 .then(tokenModel => { 57 .then(tokenModel => {
53 if (tokenModel) { 58 if (tokenModel) {
54 accessTokenCache.set(bearerToken, tokenModel) 59 accessTokenCache.set(bearerToken, tokenModel)
55 userHavingToken.set(tokenModel.userId, tokenModel.accessToken) 60 userHavingToken.set(tokenModel.userId, tokenModel.accessToken)
56 } 61 }
57 62
58 return tokenModel 63 return tokenModel
59 }) 64 })
60} 65}
61 66
62function getClient (clientId: string, clientSecret: string) { 67function getClient (clientId: string, clientSecret: string) {
@@ -72,6 +77,20 @@ function getRefreshToken (refreshToken: string) {
72} 77}
73 78
74async function getUser (usernameOrEmail: string, password: string) { 79async function getUser (usernameOrEmail: string, password: string) {
80 const res: express.Response = this.request.res
81 if (res.locals.bypassLogin && res.locals.bypassLogin.bypass === true) {
82 const obj = res.locals.bypassLogin
83 logger.info('Bypassing oauth login by plugin %s.', obj.pluginName)
84
85 let user = await UserModel.loadByEmail(obj.user.username)
86 if (!user) user = await createUserFromExternal(obj.pluginName, obj.user)
87
88 // This user does not belong to this plugin, skip it
89 if (user.pluginAuth !== obj.pluginName) return null
90
91 return user
92 }
93
75 logger.debug('Getting User (username/email: ' + usernameOrEmail + ', password: ******).') 94 logger.debug('Getting User (username/email: ' + usernameOrEmail + ', password: ******).')
76 95
77 const user = await UserModel.loadByUsernameOrEmail(usernameOrEmail) 96 const user = await UserModel.loadByUsernameOrEmail(usernameOrEmail)
@@ -96,19 +115,11 @@ async function revokeToken (tokenInfo: TokenInfo) {
96 115
97 token.destroy() 116 token.destroy()
98 .catch(err => logger.error('Cannot destroy token when revoking token.', { err })) 117 .catch(err => logger.error('Cannot destroy token when revoking token.', { err }))
118
119 return true
99 } 120 }
100 121
101 /* 122 return false
102 * Thanks to https://github.com/manjeshpv/node-oauth2-server-implementation/blob/master/components/oauth/mongo-models.js
103 * "As per the discussion we need set older date
104 * revokeToken will expected return a boolean in future version
105 * https://github.com/oauthjs/node-oauth2-server/pull/274
106 * https://github.com/oauthjs/node-oauth2-server/issues/290"
107 */
108 const expiredToken = token
109 expiredToken.refreshTokenExpiresAt = new Date('2015-05-28T06:59:53.000Z')
110
111 return expiredToken
112} 123}
113 124
114async function saveToken (token: TokenInfo, client: OAuthClientModel, user: UserModel) { 125async function saveToken (token: TokenInfo, client: OAuthClientModel, user: UserModel) {
@@ -141,3 +152,30 @@ export {
141 revokeToken, 152 revokeToken,
142 saveToken 153 saveToken
143} 154}
155
156async function createUserFromExternal (pluginAuth: string, options: {
157 username: string
158 email: string
159 role: UserRole
160 displayName: string
161}) {
162 const userToCreate = new UserModel({
163 username: options.username,
164 password: null,
165 email: options.email,
166 nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
167 autoPlayVideo: true,
168 role: options.role,
169 videoQuota: CONFIG.USER.VIDEO_QUOTA,
170 videoQuotaDaily: CONFIG.USER.VIDEO_QUOTA_DAILY,
171 adminFlags: UserAdminFlag.NONE,
172 pluginAuth
173 }) as MUser
174
175 const { user } = await createUserAccountAndChannelAndPlaylist({
176 userToCreate,
177 userDisplayName: options.displayName
178 })
179
180 return user
181}
diff --git a/server/lib/plugins/plugin-manager.ts b/server/lib/plugins/plugin-manager.ts
index 37fb07716..f78b989f5 100644
--- a/server/lib/plugins/plugin-manager.ts
+++ b/server/lib/plugins/plugin-manager.ts
@@ -39,6 +39,7 @@ export interface RegisteredPlugin {
39 css: string[] 39 css: string[]
40 40
41 // Only if this is a plugin 41 // Only if this is a plugin
42 registerHelpersStore?: RegisterHelpersStore
42 unregister?: Function 43 unregister?: Function
43} 44}
44 45
@@ -58,11 +59,10 @@ export class PluginManager implements ServerHook {
58 private static instance: PluginManager 59 private static instance: PluginManager
59 60
60 private registeredPlugins: { [name: string]: RegisteredPlugin } = {} 61 private registeredPlugins: { [name: string]: RegisteredPlugin } = {}
62
61 private hooks: { [name: string]: HookInformationValue[] } = {} 63 private hooks: { [name: string]: HookInformationValue[] } = {}
62 private translations: PluginLocalesTranslations = {} 64 private translations: PluginLocalesTranslations = {}
63 65
64 private registerHelpersStore: { [npmName: string]: RegisterHelpersStore } = {}
65
66 private constructor () { 66 private constructor () {
67 } 67 }
68 68
@@ -102,18 +102,30 @@ export class PluginManager implements ServerHook {
102 return this.getRegisteredPluginsOrThemes(PluginType.THEME) 102 return this.getRegisteredPluginsOrThemes(PluginType.THEME)
103 } 103 }
104 104
105 getIdAndPassAuths () {
106 return this.getRegisteredPlugins()
107 .map(p => ({ npmName: p.npmName, idAndPassAuths: p.registerHelpersStore.getIdAndPassAuths() }))
108 .filter(v => v.idAndPassAuths.length !== 0)
109 }
110
111 getExternalAuths () {
112 return this.getRegisteredPlugins()
113 .map(p => ({ npmName: p.npmName, externalAuths: p.registerHelpersStore.getExternalAuths() }))
114 .filter(v => v.externalAuths.length !== 0)
115 }
116
105 getRegisteredSettings (npmName: string) { 117 getRegisteredSettings (npmName: string) {
106 const store = this.registerHelpersStore[npmName] 118 const result = this.getRegisteredPluginOrTheme(npmName)
107 if (store) return store.getSettings() 119 if (!result || result.type !== PluginType.PLUGIN) return []
108 120
109 return [] 121 return result.registerHelpersStore.getSettings()
110 } 122 }
111 123
112 getRouter (npmName: string) { 124 getRouter (npmName: string) {
113 const store = this.registerHelpersStore[npmName] 125 const result = this.getRegisteredPluginOrTheme(npmName)
114 if (!store) return null 126 if (!result || result.type !== PluginType.PLUGIN) return null
115 127
116 return store.getRouter() 128 return result.registerHelpersStore.getRouter()
117 } 129 }
118 130
119 getTranslations (locale: string) { 131 getTranslations (locale: string) {
@@ -185,11 +197,9 @@ export class PluginManager implements ServerHook {
185 this.hooks[key] = this.hooks[key].filter(h => h.npmName !== npmName) 197 this.hooks[key] = this.hooks[key].filter(h => h.npmName !== npmName)
186 } 198 }
187 199
188 const store = this.registerHelpersStore[plugin.npmName] 200 const store = plugin.registerHelpersStore
189 store.reinitVideoConstants(plugin.npmName) 201 store.reinitVideoConstants(plugin.npmName)
190 202
191 delete this.registerHelpersStore[plugin.npmName]
192
193 logger.info('Regenerating registered plugin CSS to global file.') 203 logger.info('Regenerating registered plugin CSS to global file.')
194 await this.regeneratePluginGlobalCSS() 204 await this.regeneratePluginGlobalCSS()
195 } 205 }
@@ -294,8 +304,11 @@ export class PluginManager implements ServerHook {
294 this.sanitizeAndCheckPackageJSONOrThrow(packageJSON, plugin.type) 304 this.sanitizeAndCheckPackageJSONOrThrow(packageJSON, plugin.type)
295 305
296 let library: PluginLibrary 306 let library: PluginLibrary
307 let registerHelpersStore: RegisterHelpersStore
297 if (plugin.type === PluginType.PLUGIN) { 308 if (plugin.type === PluginType.PLUGIN) {
298 library = await this.registerPlugin(plugin, pluginPath, packageJSON) 309 const result = await this.registerPlugin(plugin, pluginPath, packageJSON)
310 library = result.library
311 registerHelpersStore = result.registerStore
299 } 312 }
300 313
301 const clientScripts: { [id: string]: ClientScript } = {} 314 const clientScripts: { [id: string]: ClientScript } = {}
@@ -314,6 +327,7 @@ export class PluginManager implements ServerHook {
314 staticDirs: packageJSON.staticDirs, 327 staticDirs: packageJSON.staticDirs,
315 clientScripts, 328 clientScripts,
316 css: packageJSON.css, 329 css: packageJSON.css,
330 registerHelpersStore: registerHelpersStore || undefined,
317 unregister: library ? library.unregister : undefined 331 unregister: library ? library.unregister : undefined
318 } 332 }
319 333
@@ -332,15 +346,15 @@ export class PluginManager implements ServerHook {
332 throw new Error('Library code is not valid (miss register or unregister function)') 346 throw new Error('Library code is not valid (miss register or unregister function)')
333 } 347 }
334 348
335 const registerHelpers = this.getRegisterHelpers(npmName, plugin) 349 const { registerOptions, registerStore } = this.getRegisterHelpers(npmName, plugin)
336 library.register(registerHelpers) 350 library.register(registerOptions)
337 .catch(err => logger.error('Cannot register plugin %s.', npmName, { err })) 351 .catch(err => logger.error('Cannot register plugin %s.', npmName, { err }))
338 352
339 logger.info('Add plugin %s CSS to global file.', npmName) 353 logger.info('Add plugin %s CSS to global file.', npmName)
340 354
341 await this.addCSSToGlobalFile(pluginPath, packageJSON.css) 355 await this.addCSSToGlobalFile(pluginPath, packageJSON.css)
342 356
343 return library 357 return { library, registerStore }
344 } 358 }
345 359
346 // ###################### Translations ###################### 360 // ###################### Translations ######################
@@ -440,7 +454,10 @@ export class PluginManager implements ServerHook {
440 454
441 // ###################### Generate register helpers ###################### 455 // ###################### Generate register helpers ######################
442 456
443 private getRegisterHelpers (npmName: string, plugin: PluginModel): RegisterServerOptions { 457 private getRegisterHelpers (
458 npmName: string,
459 plugin: PluginModel
460 ): { registerStore: RegisterHelpersStore, registerOptions: RegisterServerOptions } {
444 const onHookAdded = (options: RegisterServerHookOptions) => { 461 const onHookAdded = (options: RegisterServerHookOptions) => {
445 if (!this.hooks[options.target]) this.hooks[options.target] = [] 462 if (!this.hooks[options.target]) this.hooks[options.target] = []
446 463
@@ -453,9 +470,11 @@ export class PluginManager implements ServerHook {
453 } 470 }
454 471
455 const registerHelpersStore = new RegisterHelpersStore(npmName, plugin, onHookAdded.bind(this)) 472 const registerHelpersStore = new RegisterHelpersStore(npmName, plugin, onHookAdded.bind(this))
456 this.registerHelpersStore[npmName] = registerHelpersStore
457 473
458 return registerHelpersStore.buildRegisterHelpers() 474 return {
475 registerStore: registerHelpersStore,
476 registerOptions: registerHelpersStore.buildRegisterHelpers()
477 }
459 } 478 }
460 479
461 private sanitizeAndCheckPackageJSONOrThrow (packageJSON: PluginPackageJson, pluginType: PluginType) { 480 private sanitizeAndCheckPackageJSONOrThrow (packageJSON: PluginPackageJson, pluginType: PluginType) {
diff --git a/server/lib/plugins/register-helpers-store.ts b/server/lib/plugins/register-helpers-store.ts
index 5ca52b151..7e827401f 100644
--- a/server/lib/plugins/register-helpers-store.ts
+++ b/server/lib/plugins/register-helpers-store.ts
@@ -20,6 +20,12 @@ import { RegisterServerSettingOptions } from '@shared/models/plugins/register-se
20import * as express from 'express' 20import * as express from 'express'
21import { PluginVideoPrivacyManager } from '@shared/models/plugins/plugin-video-privacy-manager.model' 21import { PluginVideoPrivacyManager } from '@shared/models/plugins/plugin-video-privacy-manager.model'
22import { PluginPlaylistPrivacyManager } from '@shared/models/plugins/plugin-playlist-privacy-manager.model' 22import { PluginPlaylistPrivacyManager } from '@shared/models/plugins/plugin-playlist-privacy-manager.model'
23import {
24 RegisterServerAuthExternalOptions,
25 RegisterServerAuthExternalResult,
26 RegisterServerAuthPassOptions
27} from '@shared/models/plugins/register-server-auth.model'
28import { onExternalAuthPlugin } from '@server/lib/auth'
23 29
24type AlterableVideoConstant = 'language' | 'licence' | 'category' | 'privacy' | 'playlistPrivacy' 30type AlterableVideoConstant = 'language' | 'licence' | 'category' | 'privacy' | 'playlistPrivacy'
25type VideoConstant = { [key in number | string]: string } 31type VideoConstant = { [key in number | string]: string }
@@ -42,6 +48,9 @@ export class RegisterHelpersStore {
42 48
43 private readonly settings: RegisterServerSettingOptions[] = [] 49 private readonly settings: RegisterServerSettingOptions[] = []
44 50
51 private readonly idAndPassAuths: RegisterServerAuthPassOptions[] = []
52 private readonly externalAuths: RegisterServerAuthExternalOptions[] = []
53
45 private readonly router: express.Router 54 private readonly router: express.Router
46 55
47 constructor ( 56 constructor (
@@ -69,6 +78,9 @@ export class RegisterHelpersStore {
69 const videoPrivacyManager = this.buildVideoPrivacyManager() 78 const videoPrivacyManager = this.buildVideoPrivacyManager()
70 const playlistPrivacyManager = this.buildPlaylistPrivacyManager() 79 const playlistPrivacyManager = this.buildPlaylistPrivacyManager()
71 80
81 const registerIdAndPassAuth = this.buildRegisterIdAndPassAuth()
82 const registerExternalAuth = this.buildRegisterExternalAuth()
83
72 const peertubeHelpers = buildPluginHelpers(this.npmName) 84 const peertubeHelpers = buildPluginHelpers(this.npmName)
73 85
74 return { 86 return {
@@ -87,6 +99,9 @@ export class RegisterHelpersStore {
87 videoPrivacyManager, 99 videoPrivacyManager,
88 playlistPrivacyManager, 100 playlistPrivacyManager,
89 101
102 registerIdAndPassAuth,
103 registerExternalAuth,
104
90 peertubeHelpers 105 peertubeHelpers
91 } 106 }
92 } 107 }
@@ -125,6 +140,14 @@ export class RegisterHelpersStore {
125 return this.router 140 return this.router
126 } 141 }
127 142
143 getIdAndPassAuths () {
144 return this.idAndPassAuths
145 }
146
147 getExternalAuths () {
148 return this.externalAuths
149 }
150
128 private buildGetRouter () { 151 private buildGetRouter () {
129 return () => this.router 152 return () => this.router
130 } 153 }
@@ -146,6 +169,26 @@ export class RegisterHelpersStore {
146 } 169 }
147 } 170 }
148 171
172 private buildRegisterIdAndPassAuth () {
173 return (options: RegisterServerAuthPassOptions) => {
174 this.idAndPassAuths.push(options)
175 }
176 }
177
178 private buildRegisterExternalAuth () {
179 const self = this
180
181 return (options: RegisterServerAuthExternalOptions) => {
182 this.externalAuths.push(options)
183
184 return {
185 onAuth (options: { username: string, email: string }): void {
186 onExternalAuthPlugin(self.npmName, options.username, options.email)
187 }
188 } as RegisterServerAuthExternalResult
189 }
190 }
191
149 private buildSettingsManager (): PluginSettingsManager { 192 private buildSettingsManager (): PluginSettingsManager {
150 return { 193 return {
151 getSetting: (name: string) => PluginModel.getSetting(this.plugin.name, this.plugin.type, name), 194 getSetting: (name: string) => PluginModel.getSetting(this.plugin.name, this.plugin.type, name),
diff --git a/server/lib/user.ts b/server/lib/user.ts
index 316c57359..8b447583e 100644
--- a/server/lib/user.ts
+++ b/server/lib/user.ts
@@ -2,7 +2,7 @@ import { v4 as uuidv4 } from 'uuid'
2import { ActivityPubActorType } from '../../shared/models/activitypub' 2import { ActivityPubActorType } from '../../shared/models/activitypub'
3import { SERVER_ACTOR_NAME, WEBSERVER } from '../initializers/constants' 3import { SERVER_ACTOR_NAME, WEBSERVER } from '../initializers/constants'
4import { AccountModel } from '../models/account/account' 4import { AccountModel } from '../models/account/account'
5import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub' 5import { buildActorInstance, setAsyncActorKeys } from './activitypub/actor'
6import { createLocalVideoChannel } from './video-channel' 6import { createLocalVideoChannel } from './video-channel'
7import { ActorModel } from '../models/activitypub/actor' 7import { ActorModel } from '../models/activitypub/actor'
8import { UserNotificationSettingModel } from '../models/account/user-notification-setting' 8import { UserNotificationSettingModel } from '../models/account/user-notification-setting'
@@ -14,6 +14,7 @@ import { Redis } from './redis'
14import { Emailer } from './emailer' 14import { Emailer } from './emailer'
15import { MAccountDefault, MActorDefault, MChannelActor } from '../typings/models' 15import { MAccountDefault, MActorDefault, MChannelActor } from '../typings/models'
16import { MUser, MUserDefault, MUserId } from '../typings/models/user' 16import { MUser, MUserDefault, MUserId } from '../typings/models/user'
17import { getAccountActivityPubUrl } from './activitypub/url'
17 18
18type ChannelNames = { name: string, displayName: string } 19type ChannelNames = { name: string, displayName: string }
19 20
diff --git a/server/lib/video-channel.ts b/server/lib/video-channel.ts
index c9887c667..102c1088d 100644
--- a/server/lib/video-channel.ts
+++ b/server/lib/video-channel.ts
@@ -2,9 +2,11 @@ import * as Sequelize from 'sequelize'
2import { v4 as uuidv4 } from 'uuid' 2import { v4 as uuidv4 } from 'uuid'
3import { VideoChannelCreate } from '../../shared/models' 3import { VideoChannelCreate } from '../../shared/models'
4import { VideoChannelModel } from '../models/video/video-channel' 4import { VideoChannelModel } from '../models/video/video-channel'
5import { buildActorInstance, federateVideoIfNeeded, getVideoChannelActivityPubUrl } from './activitypub' 5import { buildActorInstance } from './activitypub/actor'
6import { VideoModel } from '../models/video/video' 6import { VideoModel } from '../models/video/video'
7import { MAccountId, MChannelDefault, MChannelId } from '../typings/models' 7import { MAccountId, MChannelDefault, MChannelId } from '../typings/models'
8import { getVideoChannelActivityPubUrl } from './activitypub/url'
9import { federateVideoIfNeeded } from './activitypub/videos'
8 10
9type CustomVideoChannelModelAccount <T extends MAccountId> = MChannelDefault & { Account?: T } 11type CustomVideoChannelModelAccount <T extends MAccountId> = MChannelDefault & { Account?: T }
10 12