diff options
author | Chocobozzz <me@florianbigard.com> | 2023-07-31 14:34:36 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2023-08-11 15:02:33 +0200 |
commit | 3a4992633ee62d5edfbb484d9c6bcb3cf158489d (patch) | |
tree | e4510b39bdac9c318fdb4b47018d08f15368b8f0 /packages/models/src/server | |
parent | 04d1da5621d25d59bd5fa1543b725c497bf5d9a8 (diff) | |
download | PeerTube-3a4992633ee62d5edfbb484d9c6bcb3cf158489d.tar.gz PeerTube-3a4992633ee62d5edfbb484d9c6bcb3cf158489d.tar.zst PeerTube-3a4992633ee62d5edfbb484d9c6bcb3cf158489d.zip |
Migrate server to ESM
Sorry for the very big commit that may lead to git log issues and merge
conflicts, but it's a major step forward:
* Server can be faster at startup because imports() are async and we can
easily lazy import big modules
* Angular doesn't seem to support ES import (with .js extension), so we
had to correctly organize peertube into a monorepo:
* Use yarn workspace feature
* Use typescript reference projects for dependencies
* Shared projects have been moved into "packages", each one is now a
node module (with a dedicated package.json/tsconfig.json)
* server/tools have been moved into apps/ and is now a dedicated app
bundled and published on NPM so users don't have to build peertube
cli tools manually
* server/tests have been moved into packages/ so we don't compile
them every time we want to run the server
* Use isolatedModule option:
* Had to move from const enum to const
(https://www.typescriptlang.org/docs/handbook/enums.html#objects-vs-enums)
* Had to explictely specify "type" imports when used in decorators
* Prefer tsx (that uses esbuild under the hood) instead of ts-node to
load typescript files (tests with mocha or scripts):
* To reduce test complexity as esbuild doesn't support decorator
metadata, we only test server files that do not import server
models
* We still build tests files into js files for a faster CI
* Remove unmaintained peertube CLI import script
* Removed some barrels to speed up execution (less imports)
Diffstat (limited to 'packages/models/src/server')
17 files changed, 1163 insertions, 0 deletions
diff --git a/packages/models/src/server/about.model.ts b/packages/models/src/server/about.model.ts new file mode 100644 index 000000000..6d4ba63c4 --- /dev/null +++ b/packages/models/src/server/about.model.ts | |||
@@ -0,0 +1,20 @@ | |||
1 | export interface About { | ||
2 | instance: { | ||
3 | name: string | ||
4 | shortDescription: string | ||
5 | description: string | ||
6 | terms: string | ||
7 | |||
8 | codeOfConduct: string | ||
9 | hardwareInformation: string | ||
10 | |||
11 | creationReason: string | ||
12 | moderationInformation: string | ||
13 | administrator: string | ||
14 | maintenanceLifetime: string | ||
15 | businessModel: string | ||
16 | |||
17 | languages: string[] | ||
18 | categories: number[] | ||
19 | } | ||
20 | } | ||
diff --git a/packages/models/src/server/broadcast-message-level.type.ts b/packages/models/src/server/broadcast-message-level.type.ts new file mode 100644 index 000000000..bf43e18b5 --- /dev/null +++ b/packages/models/src/server/broadcast-message-level.type.ts | |||
@@ -0,0 +1 @@ | |||
export type BroadcastMessageLevel = 'info' | 'warning' | 'error' | |||
diff --git a/packages/models/src/server/client-log-create.model.ts b/packages/models/src/server/client-log-create.model.ts new file mode 100644 index 000000000..543af0d3d --- /dev/null +++ b/packages/models/src/server/client-log-create.model.ts | |||
@@ -0,0 +1,11 @@ | |||
1 | import { ClientLogLevel } from './client-log-level.type.js' | ||
2 | |||
3 | export interface ClientLogCreate { | ||
4 | message: string | ||
5 | url: string | ||
6 | level: ClientLogLevel | ||
7 | |||
8 | stackTrace?: string | ||
9 | userAgent?: string | ||
10 | meta?: string | ||
11 | } | ||
diff --git a/packages/models/src/server/client-log-level.type.ts b/packages/models/src/server/client-log-level.type.ts new file mode 100644 index 000000000..18dea2751 --- /dev/null +++ b/packages/models/src/server/client-log-level.type.ts | |||
@@ -0,0 +1 @@ | |||
export type ClientLogLevel = 'warn' | 'error' | |||
diff --git a/packages/models/src/server/contact-form.model.ts b/packages/models/src/server/contact-form.model.ts new file mode 100644 index 000000000..c23e6d1ba --- /dev/null +++ b/packages/models/src/server/contact-form.model.ts | |||
@@ -0,0 +1,6 @@ | |||
1 | export interface ContactForm { | ||
2 | fromEmail: string | ||
3 | fromName: string | ||
4 | subject: string | ||
5 | body: string | ||
6 | } | ||
diff --git a/packages/models/src/server/custom-config.model.ts b/packages/models/src/server/custom-config.model.ts new file mode 100644 index 000000000..df4176ba7 --- /dev/null +++ b/packages/models/src/server/custom-config.model.ts | |||
@@ -0,0 +1,259 @@ | |||
1 | import { NSFWPolicyType } from '../videos/nsfw-policy.type.js' | ||
2 | import { BroadcastMessageLevel } from './broadcast-message-level.type.js' | ||
3 | |||
4 | export type ConfigResolutions = { | ||
5 | '144p': boolean | ||
6 | '240p': boolean | ||
7 | '360p': boolean | ||
8 | '480p': boolean | ||
9 | '720p': boolean | ||
10 | '1080p': boolean | ||
11 | '1440p': boolean | ||
12 | '2160p': boolean | ||
13 | } | ||
14 | |||
15 | export interface CustomConfig { | ||
16 | instance: { | ||
17 | name: string | ||
18 | shortDescription: string | ||
19 | description: string | ||
20 | terms: string | ||
21 | codeOfConduct: string | ||
22 | |||
23 | creationReason: string | ||
24 | moderationInformation: string | ||
25 | administrator: string | ||
26 | maintenanceLifetime: string | ||
27 | businessModel: string | ||
28 | hardwareInformation: string | ||
29 | |||
30 | languages: string[] | ||
31 | categories: number[] | ||
32 | |||
33 | isNSFW: boolean | ||
34 | defaultNSFWPolicy: NSFWPolicyType | ||
35 | |||
36 | defaultClientRoute: string | ||
37 | |||
38 | customizations: { | ||
39 | javascript?: string | ||
40 | css?: string | ||
41 | } | ||
42 | } | ||
43 | |||
44 | theme: { | ||
45 | default: string | ||
46 | } | ||
47 | |||
48 | services: { | ||
49 | twitter: { | ||
50 | username: string | ||
51 | whitelisted: boolean | ||
52 | } | ||
53 | } | ||
54 | |||
55 | client: { | ||
56 | videos: { | ||
57 | miniature: { | ||
58 | preferAuthorDisplayName: boolean | ||
59 | } | ||
60 | } | ||
61 | |||
62 | menu: { | ||
63 | login: { | ||
64 | redirectOnSingleExternalAuth: boolean | ||
65 | } | ||
66 | } | ||
67 | } | ||
68 | |||
69 | cache: { | ||
70 | previews: { | ||
71 | size: number | ||
72 | } | ||
73 | |||
74 | captions: { | ||
75 | size: number | ||
76 | } | ||
77 | |||
78 | torrents: { | ||
79 | size: number | ||
80 | } | ||
81 | |||
82 | storyboards: { | ||
83 | size: number | ||
84 | } | ||
85 | } | ||
86 | |||
87 | signup: { | ||
88 | enabled: boolean | ||
89 | limit: number | ||
90 | requiresApproval: boolean | ||
91 | requiresEmailVerification: boolean | ||
92 | minimumAge: number | ||
93 | } | ||
94 | |||
95 | admin: { | ||
96 | email: string | ||
97 | } | ||
98 | |||
99 | contactForm: { | ||
100 | enabled: boolean | ||
101 | } | ||
102 | |||
103 | user: { | ||
104 | history: { | ||
105 | videos: { | ||
106 | enabled: boolean | ||
107 | } | ||
108 | } | ||
109 | videoQuota: number | ||
110 | videoQuotaDaily: number | ||
111 | } | ||
112 | |||
113 | videoChannels: { | ||
114 | maxPerUser: number | ||
115 | } | ||
116 | |||
117 | transcoding: { | ||
118 | enabled: boolean | ||
119 | |||
120 | allowAdditionalExtensions: boolean | ||
121 | allowAudioFiles: boolean | ||
122 | |||
123 | remoteRunners: { | ||
124 | enabled: boolean | ||
125 | } | ||
126 | |||
127 | threads: number | ||
128 | concurrency: number | ||
129 | |||
130 | profile: string | ||
131 | |||
132 | resolutions: ConfigResolutions & { '0p': boolean } | ||
133 | |||
134 | alwaysTranscodeOriginalResolution: boolean | ||
135 | |||
136 | webVideos: { | ||
137 | enabled: boolean | ||
138 | } | ||
139 | |||
140 | hls: { | ||
141 | enabled: boolean | ||
142 | } | ||
143 | } | ||
144 | |||
145 | live: { | ||
146 | enabled: boolean | ||
147 | |||
148 | allowReplay: boolean | ||
149 | |||
150 | latencySetting: { | ||
151 | enabled: boolean | ||
152 | } | ||
153 | |||
154 | maxDuration: number | ||
155 | maxInstanceLives: number | ||
156 | maxUserLives: number | ||
157 | |||
158 | transcoding: { | ||
159 | enabled: boolean | ||
160 | remoteRunners: { | ||
161 | enabled: boolean | ||
162 | } | ||
163 | threads: number | ||
164 | profile: string | ||
165 | resolutions: ConfigResolutions | ||
166 | alwaysTranscodeOriginalResolution: boolean | ||
167 | } | ||
168 | } | ||
169 | |||
170 | videoStudio: { | ||
171 | enabled: boolean | ||
172 | |||
173 | remoteRunners: { | ||
174 | enabled: boolean | ||
175 | } | ||
176 | } | ||
177 | |||
178 | videoFile: { | ||
179 | update: { | ||
180 | enabled: boolean | ||
181 | } | ||
182 | } | ||
183 | |||
184 | import: { | ||
185 | videos: { | ||
186 | concurrency: number | ||
187 | |||
188 | http: { | ||
189 | enabled: boolean | ||
190 | } | ||
191 | torrent: { | ||
192 | enabled: boolean | ||
193 | } | ||
194 | } | ||
195 | videoChannelSynchronization: { | ||
196 | enabled: boolean | ||
197 | maxPerUser: number | ||
198 | } | ||
199 | } | ||
200 | |||
201 | trending: { | ||
202 | videos: { | ||
203 | algorithms: { | ||
204 | enabled: string[] | ||
205 | default: string | ||
206 | } | ||
207 | } | ||
208 | } | ||
209 | |||
210 | autoBlacklist: { | ||
211 | videos: { | ||
212 | ofUsers: { | ||
213 | enabled: boolean | ||
214 | } | ||
215 | } | ||
216 | } | ||
217 | |||
218 | followers: { | ||
219 | instance: { | ||
220 | enabled: boolean | ||
221 | manualApproval: boolean | ||
222 | } | ||
223 | } | ||
224 | |||
225 | followings: { | ||
226 | instance: { | ||
227 | autoFollowBack: { | ||
228 | enabled: boolean | ||
229 | } | ||
230 | |||
231 | autoFollowIndex: { | ||
232 | enabled: boolean | ||
233 | indexUrl: string | ||
234 | } | ||
235 | } | ||
236 | } | ||
237 | |||
238 | broadcastMessage: { | ||
239 | enabled: boolean | ||
240 | message: string | ||
241 | level: BroadcastMessageLevel | ||
242 | dismissable: boolean | ||
243 | } | ||
244 | |||
245 | search: { | ||
246 | remoteUri: { | ||
247 | users: boolean | ||
248 | anonymous: boolean | ||
249 | } | ||
250 | |||
251 | searchIndex: { | ||
252 | enabled: boolean | ||
253 | url: string | ||
254 | disableLocalSearch: boolean | ||
255 | isDefaultSearch: boolean | ||
256 | } | ||
257 | } | ||
258 | |||
259 | } | ||
diff --git a/packages/models/src/server/debug.model.ts b/packages/models/src/server/debug.model.ts new file mode 100644 index 000000000..41f2109af --- /dev/null +++ b/packages/models/src/server/debug.model.ts | |||
@@ -0,0 +1,12 @@ | |||
1 | export interface Debug { | ||
2 | ip: string | ||
3 | activityPubMessagesWaiting: number | ||
4 | } | ||
5 | |||
6 | export interface SendDebugCommand { | ||
7 | command: 'remove-dandling-resumable-uploads' | ||
8 | | 'process-video-views-buffer' | ||
9 | | 'process-video-viewers' | ||
10 | | 'process-video-channel-sync-latest' | ||
11 | | 'process-update-videos-scheduler' | ||
12 | } | ||
diff --git a/packages/models/src/server/emailer.model.ts b/packages/models/src/server/emailer.model.ts new file mode 100644 index 000000000..39512d306 --- /dev/null +++ b/packages/models/src/server/emailer.model.ts | |||
@@ -0,0 +1,49 @@ | |||
1 | type From = string | { name?: string, address: string } | ||
2 | |||
3 | interface Base extends Partial<SendEmailDefaultMessageOptions> { | ||
4 | to: string[] | string | ||
5 | } | ||
6 | |||
7 | interface MailTemplate extends Base { | ||
8 | template: string | ||
9 | locals?: { [key: string]: any } | ||
10 | text?: undefined | ||
11 | } | ||
12 | |||
13 | interface MailText extends Base { | ||
14 | text: string | ||
15 | |||
16 | locals?: Partial<SendEmailDefaultLocalsOptions> & { | ||
17 | title?: string | ||
18 | action?: { | ||
19 | url: string | ||
20 | text: string | ||
21 | } | ||
22 | } | ||
23 | } | ||
24 | |||
25 | interface SendEmailDefaultLocalsOptions { | ||
26 | instanceName: string | ||
27 | text: string | ||
28 | subject: string | ||
29 | } | ||
30 | |||
31 | interface SendEmailDefaultMessageOptions { | ||
32 | to: string[] | string | ||
33 | from: From | ||
34 | subject: string | ||
35 | replyTo: string | ||
36 | } | ||
37 | |||
38 | export type SendEmailDefaultOptions = { | ||
39 | template: 'common' | ||
40 | |||
41 | message: SendEmailDefaultMessageOptions | ||
42 | |||
43 | locals: SendEmailDefaultLocalsOptions & { | ||
44 | WEBSERVER: any | ||
45 | EMAIL: any | ||
46 | } | ||
47 | } | ||
48 | |||
49 | export type SendEmailOptions = MailTemplate | MailText | ||
diff --git a/packages/models/src/server/index.ts b/packages/models/src/server/index.ts new file mode 100644 index 000000000..ba6af8f6f --- /dev/null +++ b/packages/models/src/server/index.ts | |||
@@ -0,0 +1,16 @@ | |||
1 | export * from './about.model.js' | ||
2 | export * from './broadcast-message-level.type.js' | ||
3 | export * from './client-log-create.model.js' | ||
4 | export * from './client-log-level.type.js' | ||
5 | export * from './contact-form.model.js' | ||
6 | export * from './custom-config.model.js' | ||
7 | export * from './debug.model.js' | ||
8 | export * from './emailer.model.js' | ||
9 | export * from './job.model.js' | ||
10 | export * from './peertube-problem-document.model.js' | ||
11 | export * from './server-config.model.js' | ||
12 | export * from './server-debug.model.js' | ||
13 | export * from './server-error-code.enum.js' | ||
14 | export * from './server-follow-create.model.js' | ||
15 | export * from './server-log-level.type.js' | ||
16 | export * from './server-stats.model.js' | ||
diff --git a/packages/models/src/server/job.model.ts b/packages/models/src/server/job.model.ts new file mode 100644 index 000000000..f86a20e28 --- /dev/null +++ b/packages/models/src/server/job.model.ts | |||
@@ -0,0 +1,303 @@ | |||
1 | import { ContextType } from '../activitypub/context.js' | ||
2 | import { VideoStateType } from '../videos/index.js' | ||
3 | import { VideoStudioTaskCut } from '../videos/studio/index.js' | ||
4 | import { SendEmailOptions } from './emailer.model.js' | ||
5 | |||
6 | export type JobState = 'active' | 'completed' | 'failed' | 'waiting' | 'delayed' | 'paused' | 'waiting-children' | ||
7 | |||
8 | export type JobType = | ||
9 | | 'activitypub-cleaner' | ||
10 | | 'activitypub-follow' | ||
11 | | 'activitypub-http-broadcast-parallel' | ||
12 | | 'activitypub-http-broadcast' | ||
13 | | 'activitypub-http-fetcher' | ||
14 | | 'activitypub-http-unicast' | ||
15 | | 'activitypub-refresher' | ||
16 | | 'actor-keys' | ||
17 | | 'after-video-channel-import' | ||
18 | | 'email' | ||
19 | | 'federate-video' | ||
20 | | 'transcoding-job-builder' | ||
21 | | 'manage-video-torrent' | ||
22 | | 'move-to-object-storage' | ||
23 | | 'notify' | ||
24 | | 'video-channel-import' | ||
25 | | 'video-file-import' | ||
26 | | 'video-import' | ||
27 | | 'video-live-ending' | ||
28 | | 'video-redundancy' | ||
29 | | 'video-studio-edition' | ||
30 | | 'video-transcoding' | ||
31 | | 'videos-views-stats' | ||
32 | | 'generate-video-storyboard' | ||
33 | |||
34 | export interface Job { | ||
35 | id: number | string | ||
36 | state: JobState | 'unknown' | ||
37 | type: JobType | ||
38 | data: any | ||
39 | priority: number | ||
40 | progress: number | ||
41 | error: any | ||
42 | createdAt: Date | string | ||
43 | finishedOn: Date | string | ||
44 | processedOn: Date | string | ||
45 | |||
46 | parent?: { | ||
47 | id: string | ||
48 | } | ||
49 | } | ||
50 | |||
51 | export type ActivitypubHttpBroadcastPayload = { | ||
52 | uris: string[] | ||
53 | contextType: ContextType | ||
54 | body: any | ||
55 | signatureActorId?: number | ||
56 | } | ||
57 | |||
58 | export type ActivitypubFollowPayload = { | ||
59 | followerActorId: number | ||
60 | name: string | ||
61 | host: string | ||
62 | isAutoFollow?: boolean | ||
63 | assertIsChannel?: boolean | ||
64 | } | ||
65 | |||
66 | export type FetchType = 'activity' | 'video-shares' | 'video-comments' | 'account-playlists' | ||
67 | export type ActivitypubHttpFetcherPayload = { | ||
68 | uri: string | ||
69 | type: FetchType | ||
70 | videoId?: number | ||
71 | } | ||
72 | |||
73 | export type ActivitypubHttpUnicastPayload = { | ||
74 | uri: string | ||
75 | contextType: ContextType | ||
76 | signatureActorId?: number | ||
77 | body: object | ||
78 | } | ||
79 | |||
80 | export type RefreshPayload = { | ||
81 | type: 'video' | 'video-playlist' | 'actor' | ||
82 | url: string | ||
83 | } | ||
84 | |||
85 | export type EmailPayload = SendEmailOptions | ||
86 | |||
87 | export type VideoFileImportPayload = { | ||
88 | videoUUID: string | ||
89 | filePath: string | ||
90 | } | ||
91 | |||
92 | // --------------------------------------------------------------------------- | ||
93 | |||
94 | export type VideoImportTorrentPayloadType = 'magnet-uri' | 'torrent-file' | ||
95 | export type VideoImportYoutubeDLPayloadType = 'youtube-dl' | ||
96 | |||
97 | export interface VideoImportYoutubeDLPayload { | ||
98 | type: VideoImportYoutubeDLPayloadType | ||
99 | videoImportId: number | ||
100 | |||
101 | fileExt?: string | ||
102 | } | ||
103 | |||
104 | export interface VideoImportTorrentPayload { | ||
105 | type: VideoImportTorrentPayloadType | ||
106 | videoImportId: number | ||
107 | } | ||
108 | |||
109 | export type VideoImportPayload = (VideoImportYoutubeDLPayload | VideoImportTorrentPayload) & { | ||
110 | preventException: boolean | ||
111 | } | ||
112 | |||
113 | export interface VideoImportPreventExceptionResult { | ||
114 | resultType: 'success' | 'error' | ||
115 | } | ||
116 | |||
117 | // --------------------------------------------------------------------------- | ||
118 | |||
119 | export type VideoRedundancyPayload = { | ||
120 | videoId: number | ||
121 | } | ||
122 | |||
123 | export type ManageVideoTorrentPayload = | ||
124 | { | ||
125 | action: 'create' | ||
126 | videoId: number | ||
127 | videoFileId: number | ||
128 | } | { | ||
129 | action: 'update-metadata' | ||
130 | |||
131 | videoId?: number | ||
132 | streamingPlaylistId?: number | ||
133 | |||
134 | videoFileId: number | ||
135 | } | ||
136 | |||
137 | // Video transcoding payloads | ||
138 | |||
139 | interface BaseTranscodingPayload { | ||
140 | videoUUID: string | ||
141 | isNewVideo?: boolean | ||
142 | } | ||
143 | |||
144 | export interface HLSTranscodingPayload extends BaseTranscodingPayload { | ||
145 | type: 'new-resolution-to-hls' | ||
146 | resolution: number | ||
147 | fps: number | ||
148 | copyCodecs: boolean | ||
149 | |||
150 | deleteWebVideoFiles: boolean | ||
151 | } | ||
152 | |||
153 | export interface NewWebVideoResolutionTranscodingPayload extends BaseTranscodingPayload { | ||
154 | type: 'new-resolution-to-web-video' | ||
155 | resolution: number | ||
156 | fps: number | ||
157 | } | ||
158 | |||
159 | export interface MergeAudioTranscodingPayload extends BaseTranscodingPayload { | ||
160 | type: 'merge-audio-to-web-video' | ||
161 | |||
162 | resolution: number | ||
163 | fps: number | ||
164 | |||
165 | hasChildren: boolean | ||
166 | } | ||
167 | |||
168 | export interface OptimizeTranscodingPayload extends BaseTranscodingPayload { | ||
169 | type: 'optimize-to-web-video' | ||
170 | |||
171 | quickTranscode: boolean | ||
172 | |||
173 | hasChildren: boolean | ||
174 | } | ||
175 | |||
176 | export type VideoTranscodingPayload = | ||
177 | HLSTranscodingPayload | ||
178 | | NewWebVideoResolutionTranscodingPayload | ||
179 | | OptimizeTranscodingPayload | ||
180 | | MergeAudioTranscodingPayload | ||
181 | |||
182 | export interface VideoLiveEndingPayload { | ||
183 | videoId: number | ||
184 | publishedAt: string | ||
185 | liveSessionId: number | ||
186 | streamingPlaylistId: number | ||
187 | |||
188 | replayDirectory?: string | ||
189 | } | ||
190 | |||
191 | export interface ActorKeysPayload { | ||
192 | actorId: number | ||
193 | } | ||
194 | |||
195 | export interface DeleteResumableUploadMetaFilePayload { | ||
196 | filepath: string | ||
197 | } | ||
198 | |||
199 | export interface MoveObjectStoragePayload { | ||
200 | videoUUID: string | ||
201 | isNewVideo: boolean | ||
202 | previousVideoState: VideoStateType | ||
203 | } | ||
204 | |||
205 | export type VideoStudioTaskCutPayload = VideoStudioTaskCut | ||
206 | |||
207 | export type VideoStudioTaskIntroPayload = { | ||
208 | name: 'add-intro' | ||
209 | |||
210 | options: { | ||
211 | file: string | ||
212 | } | ||
213 | } | ||
214 | |||
215 | export type VideoStudioTaskOutroPayload = { | ||
216 | name: 'add-outro' | ||
217 | |||
218 | options: { | ||
219 | file: string | ||
220 | } | ||
221 | } | ||
222 | |||
223 | export type VideoStudioTaskWatermarkPayload = { | ||
224 | name: 'add-watermark' | ||
225 | |||
226 | options: { | ||
227 | file: string | ||
228 | |||
229 | watermarkSizeRatio: number | ||
230 | horitonzalMarginRatio: number | ||
231 | verticalMarginRatio: number | ||
232 | } | ||
233 | } | ||
234 | |||
235 | export type VideoStudioTaskPayload = | ||
236 | VideoStudioTaskCutPayload | | ||
237 | VideoStudioTaskIntroPayload | | ||
238 | VideoStudioTaskOutroPayload | | ||
239 | VideoStudioTaskWatermarkPayload | ||
240 | |||
241 | export interface VideoStudioEditionPayload { | ||
242 | videoUUID: string | ||
243 | tasks: VideoStudioTaskPayload[] | ||
244 | } | ||
245 | |||
246 | // --------------------------------------------------------------------------- | ||
247 | |||
248 | export interface VideoChannelImportPayload { | ||
249 | externalChannelUrl: string | ||
250 | videoChannelId: number | ||
251 | |||
252 | partOfChannelSyncId?: number | ||
253 | } | ||
254 | |||
255 | export interface AfterVideoChannelImportPayload { | ||
256 | channelSyncId: number | ||
257 | } | ||
258 | |||
259 | // --------------------------------------------------------------------------- | ||
260 | |||
261 | export type NotifyPayload = | ||
262 | { | ||
263 | action: 'new-video' | ||
264 | videoUUID: string | ||
265 | } | ||
266 | |||
267 | // --------------------------------------------------------------------------- | ||
268 | |||
269 | export interface FederateVideoPayload { | ||
270 | videoUUID: string | ||
271 | isNewVideo: boolean | ||
272 | } | ||
273 | |||
274 | // --------------------------------------------------------------------------- | ||
275 | |||
276 | export interface TranscodingJobBuilderPayload { | ||
277 | videoUUID: string | ||
278 | |||
279 | optimizeJob?: { | ||
280 | isNewVideo: boolean | ||
281 | } | ||
282 | |||
283 | // Array of jobs to create | ||
284 | jobs?: { | ||
285 | type: 'video-transcoding' | ||
286 | payload: VideoTranscodingPayload | ||
287 | priority?: number | ||
288 | }[] | ||
289 | |||
290 | // Array of sequential jobs to create | ||
291 | sequentialJobs?: { | ||
292 | type: 'video-transcoding' | ||
293 | payload: VideoTranscodingPayload | ||
294 | priority?: number | ||
295 | }[][] | ||
296 | } | ||
297 | |||
298 | // --------------------------------------------------------------------------- | ||
299 | |||
300 | export interface GenerateStoryboardPayload { | ||
301 | videoUUID: string | ||
302 | federate: boolean | ||
303 | } | ||
diff --git a/packages/models/src/server/peertube-problem-document.model.ts b/packages/models/src/server/peertube-problem-document.model.ts new file mode 100644 index 000000000..c717fc152 --- /dev/null +++ b/packages/models/src/server/peertube-problem-document.model.ts | |||
@@ -0,0 +1,32 @@ | |||
1 | import { HttpStatusCodeType } from '../http/http-status-codes.js' | ||
2 | import { OAuth2ErrorCodeType, ServerErrorCodeType } from './server-error-code.enum.js' | ||
3 | |||
4 | export interface PeerTubeProblemDocumentData { | ||
5 | 'invalid-params'?: Record<string, object> | ||
6 | |||
7 | originUrl?: string | ||
8 | |||
9 | keyId?: string | ||
10 | |||
11 | targetUrl?: string | ||
12 | |||
13 | actorUrl?: string | ||
14 | |||
15 | // Feeds | ||
16 | format?: string | ||
17 | url?: string | ||
18 | } | ||
19 | |||
20 | export interface PeerTubeProblemDocument extends PeerTubeProblemDocumentData { | ||
21 | type: string | ||
22 | title: string | ||
23 | |||
24 | detail: string | ||
25 | // FIXME: Compat PeerTube <= 3.2 | ||
26 | error: string | ||
27 | |||
28 | status: HttpStatusCodeType | ||
29 | |||
30 | docs?: string | ||
31 | code?: OAuth2ErrorCodeType | ServerErrorCodeType | ||
32 | } | ||
diff --git a/packages/models/src/server/server-config.model.ts b/packages/models/src/server/server-config.model.ts new file mode 100644 index 000000000..a2a2bd5aa --- /dev/null +++ b/packages/models/src/server/server-config.model.ts | |||
@@ -0,0 +1,305 @@ | |||
1 | import { ClientScriptJSON } from '../plugins/plugin-package-json.model.js' | ||
2 | import { NSFWPolicyType } from '../videos/nsfw-policy.type.js' | ||
3 | import { VideoPrivacyType } from '../videos/video-privacy.enum.js' | ||
4 | import { BroadcastMessageLevel } from './broadcast-message-level.type.js' | ||
5 | |||
6 | export interface ServerConfigPlugin { | ||
7 | name: string | ||
8 | npmName: string | ||
9 | version: string | ||
10 | description: string | ||
11 | clientScripts: { [name: string]: ClientScriptJSON } | ||
12 | } | ||
13 | |||
14 | export interface ServerConfigTheme extends ServerConfigPlugin { | ||
15 | css: string[] | ||
16 | } | ||
17 | |||
18 | export interface RegisteredExternalAuthConfig { | ||
19 | npmName: string | ||
20 | name: string | ||
21 | version: string | ||
22 | authName: string | ||
23 | authDisplayName: string | ||
24 | } | ||
25 | |||
26 | export interface RegisteredIdAndPassAuthConfig { | ||
27 | npmName: string | ||
28 | name: string | ||
29 | version: string | ||
30 | authName: string | ||
31 | weight: number | ||
32 | } | ||
33 | |||
34 | export interface ServerConfig { | ||
35 | serverVersion: string | ||
36 | serverCommit?: string | ||
37 | |||
38 | client: { | ||
39 | videos: { | ||
40 | miniature: { | ||
41 | displayAuthorAvatar: boolean | ||
42 | preferAuthorDisplayName: boolean | ||
43 | } | ||
44 | resumableUpload: { | ||
45 | maxChunkSize: number | ||
46 | } | ||
47 | } | ||
48 | |||
49 | menu: { | ||
50 | login: { | ||
51 | redirectOnSingleExternalAuth: boolean | ||
52 | } | ||
53 | } | ||
54 | } | ||
55 | |||
56 | defaults: { | ||
57 | publish: { | ||
58 | downloadEnabled: boolean | ||
59 | commentsEnabled: boolean | ||
60 | privacy: VideoPrivacyType | ||
61 | licence: number | ||
62 | } | ||
63 | |||
64 | p2p: { | ||
65 | webapp: { | ||
66 | enabled: boolean | ||
67 | } | ||
68 | |||
69 | embed: { | ||
70 | enabled: boolean | ||
71 | } | ||
72 | } | ||
73 | } | ||
74 | |||
75 | webadmin: { | ||
76 | configuration: { | ||
77 | edition: { | ||
78 | allowed: boolean | ||
79 | } | ||
80 | } | ||
81 | } | ||
82 | |||
83 | instance: { | ||
84 | name: string | ||
85 | shortDescription: string | ||
86 | isNSFW: boolean | ||
87 | defaultNSFWPolicy: NSFWPolicyType | ||
88 | defaultClientRoute: string | ||
89 | customizations: { | ||
90 | javascript: string | ||
91 | css: string | ||
92 | } | ||
93 | } | ||
94 | |||
95 | search: { | ||
96 | remoteUri: { | ||
97 | users: boolean | ||
98 | anonymous: boolean | ||
99 | } | ||
100 | |||
101 | searchIndex: { | ||
102 | enabled: boolean | ||
103 | url: string | ||
104 | disableLocalSearch: boolean | ||
105 | isDefaultSearch: boolean | ||
106 | } | ||
107 | } | ||
108 | |||
109 | plugin: { | ||
110 | registered: ServerConfigPlugin[] | ||
111 | |||
112 | registeredExternalAuths: RegisteredExternalAuthConfig[] | ||
113 | |||
114 | registeredIdAndPassAuths: RegisteredIdAndPassAuthConfig[] | ||
115 | } | ||
116 | |||
117 | theme: { | ||
118 | registered: ServerConfigTheme[] | ||
119 | default: string | ||
120 | } | ||
121 | |||
122 | email: { | ||
123 | enabled: boolean | ||
124 | } | ||
125 | |||
126 | contactForm: { | ||
127 | enabled: boolean | ||
128 | } | ||
129 | |||
130 | signup: { | ||
131 | allowed: boolean | ||
132 | allowedForCurrentIP: boolean | ||
133 | requiresEmailVerification: boolean | ||
134 | requiresApproval: boolean | ||
135 | minimumAge: number | ||
136 | } | ||
137 | |||
138 | transcoding: { | ||
139 | hls: { | ||
140 | enabled: boolean | ||
141 | } | ||
142 | |||
143 | web_videos: { | ||
144 | enabled: boolean | ||
145 | } | ||
146 | |||
147 | enabledResolutions: number[] | ||
148 | |||
149 | profile: string | ||
150 | availableProfiles: string[] | ||
151 | |||
152 | remoteRunners: { | ||
153 | enabled: boolean | ||
154 | } | ||
155 | } | ||
156 | |||
157 | live: { | ||
158 | enabled: boolean | ||
159 | |||
160 | allowReplay: boolean | ||
161 | latencySetting: { | ||
162 | enabled: boolean | ||
163 | } | ||
164 | |||
165 | maxDuration: number | ||
166 | maxInstanceLives: number | ||
167 | maxUserLives: number | ||
168 | |||
169 | transcoding: { | ||
170 | enabled: boolean | ||
171 | |||
172 | remoteRunners: { | ||
173 | enabled: boolean | ||
174 | } | ||
175 | |||
176 | enabledResolutions: number[] | ||
177 | |||
178 | profile: string | ||
179 | availableProfiles: string[] | ||
180 | } | ||
181 | |||
182 | rtmp: { | ||
183 | port: number | ||
184 | } | ||
185 | } | ||
186 | |||
187 | videoStudio: { | ||
188 | enabled: boolean | ||
189 | |||
190 | remoteRunners: { | ||
191 | enabled: boolean | ||
192 | } | ||
193 | } | ||
194 | |||
195 | videoFile: { | ||
196 | update: { | ||
197 | enabled: boolean | ||
198 | } | ||
199 | } | ||
200 | |||
201 | import: { | ||
202 | videos: { | ||
203 | http: { | ||
204 | enabled: boolean | ||
205 | } | ||
206 | torrent: { | ||
207 | enabled: boolean | ||
208 | } | ||
209 | } | ||
210 | videoChannelSynchronization: { | ||
211 | enabled: boolean | ||
212 | } | ||
213 | } | ||
214 | |||
215 | autoBlacklist: { | ||
216 | videos: { | ||
217 | ofUsers: { | ||
218 | enabled: boolean | ||
219 | } | ||
220 | } | ||
221 | } | ||
222 | |||
223 | avatar: { | ||
224 | file: { | ||
225 | size: { | ||
226 | max: number | ||
227 | } | ||
228 | extensions: string[] | ||
229 | } | ||
230 | } | ||
231 | |||
232 | banner: { | ||
233 | file: { | ||
234 | size: { | ||
235 | max: number | ||
236 | } | ||
237 | extensions: string[] | ||
238 | } | ||
239 | } | ||
240 | |||
241 | video: { | ||
242 | image: { | ||
243 | size: { | ||
244 | max: number | ||
245 | } | ||
246 | extensions: string[] | ||
247 | } | ||
248 | file: { | ||
249 | extensions: string[] | ||
250 | } | ||
251 | } | ||
252 | |||
253 | videoCaption: { | ||
254 | file: { | ||
255 | size: { | ||
256 | max: number | ||
257 | } | ||
258 | extensions: string[] | ||
259 | } | ||
260 | } | ||
261 | |||
262 | user: { | ||
263 | videoQuota: number | ||
264 | videoQuotaDaily: number | ||
265 | } | ||
266 | |||
267 | videoChannels: { | ||
268 | maxPerUser: number | ||
269 | } | ||
270 | |||
271 | trending: { | ||
272 | videos: { | ||
273 | intervalDays: number | ||
274 | algorithms: { | ||
275 | enabled: string[] | ||
276 | default: string | ||
277 | } | ||
278 | } | ||
279 | } | ||
280 | |||
281 | tracker: { | ||
282 | enabled: boolean | ||
283 | } | ||
284 | |||
285 | followings: { | ||
286 | instance: { | ||
287 | autoFollowIndex: { | ||
288 | indexUrl: string | ||
289 | } | ||
290 | } | ||
291 | } | ||
292 | |||
293 | broadcastMessage: { | ||
294 | enabled: boolean | ||
295 | message: string | ||
296 | level: BroadcastMessageLevel | ||
297 | dismissable: boolean | ||
298 | } | ||
299 | |||
300 | homepage: { | ||
301 | enabled: boolean | ||
302 | } | ||
303 | } | ||
304 | |||
305 | export type HTMLServerConfig = Omit<ServerConfig, 'signup'> | ||
diff --git a/packages/models/src/server/server-debug.model.ts b/packages/models/src/server/server-debug.model.ts new file mode 100644 index 000000000..4b731bb90 --- /dev/null +++ b/packages/models/src/server/server-debug.model.ts | |||
@@ -0,0 +1,4 @@ | |||
1 | export interface ServerDebug { | ||
2 | ip: string | ||
3 | activityPubMessagesWaiting: number | ||
4 | } | ||
diff --git a/packages/models/src/server/server-error-code.enum.ts b/packages/models/src/server/server-error-code.enum.ts new file mode 100644 index 000000000..dc200c1ea --- /dev/null +++ b/packages/models/src/server/server-error-code.enum.ts | |||
@@ -0,0 +1,92 @@ | |||
1 | export const ServerErrorCode = { | ||
2 | /** | ||
3 | * The simplest form of payload too large: when the file size is over the | ||
4 | * global file size limit | ||
5 | */ | ||
6 | MAX_FILE_SIZE_REACHED:'max_file_size_reached', | ||
7 | |||
8 | /** | ||
9 | * The payload is too large for the user quota set | ||
10 | */ | ||
11 | QUOTA_REACHED:'quota_reached', | ||
12 | |||
13 | /** | ||
14 | * Error yielded upon trying to access a video that is not federated, nor can | ||
15 | * be. This may be due to: remote videos on instances that are not followed by | ||
16 | * yours, and with your instance disallowing unknown instances being accessed. | ||
17 | */ | ||
18 | DOES_NOT_RESPECT_FOLLOW_CONSTRAINTS:'does_not_respect_follow_constraints', | ||
19 | |||
20 | LIVE_NOT_ENABLED:'live_not_enabled', | ||
21 | LIVE_NOT_ALLOWING_REPLAY:'live_not_allowing_replay', | ||
22 | LIVE_CONFLICTING_PERMANENT_AND_SAVE_REPLAY:'live_conflicting_permanent_and_save_replay', | ||
23 | /** | ||
24 | * Pretty self-explanatory: the set maximum number of simultaneous lives was | ||
25 | * reached, and this error is typically there to inform the user trying to | ||
26 | * broadcast one. | ||
27 | */ | ||
28 | MAX_INSTANCE_LIVES_LIMIT_REACHED:'max_instance_lives_limit_reached', | ||
29 | /** | ||
30 | * Pretty self-explanatory: the set maximum number of simultaneous lives FOR | ||
31 | * THIS USER was reached, and this error is typically there to inform the user | ||
32 | * trying to broadcast one. | ||
33 | */ | ||
34 | MAX_USER_LIVES_LIMIT_REACHED:'max_user_lives_limit_reached', | ||
35 | |||
36 | /** | ||
37 | * A torrent should have at most one correct video file. Any more and we will | ||
38 | * not be able to choose automatically. | ||
39 | */ | ||
40 | INCORRECT_FILES_IN_TORRENT:'incorrect_files_in_torrent', | ||
41 | |||
42 | COMMENT_NOT_ASSOCIATED_TO_VIDEO:'comment_not_associated_to_video', | ||
43 | |||
44 | MISSING_TWO_FACTOR:'missing_two_factor', | ||
45 | INVALID_TWO_FACTOR:'invalid_two_factor', | ||
46 | |||
47 | ACCOUNT_WAITING_FOR_APPROVAL:'account_waiting_for_approval', | ||
48 | ACCOUNT_APPROVAL_REJECTED:'account_approval_rejected', | ||
49 | |||
50 | RUNNER_JOB_NOT_IN_PROCESSING_STATE:'runner_job_not_in_processing_state', | ||
51 | RUNNER_JOB_NOT_IN_PENDING_STATE:'runner_job_not_in_pending_state', | ||
52 | UNKNOWN_RUNNER_TOKEN:'unknown_runner_token', | ||
53 | |||
54 | VIDEO_REQUIRES_PASSWORD:'video_requires_password', | ||
55 | INCORRECT_VIDEO_PASSWORD:'incorrect_video_password', | ||
56 | |||
57 | VIDEO_ALREADY_BEING_TRANSCODED:'video_already_being_transcoded' | ||
58 | } as const | ||
59 | |||
60 | /** | ||
61 | * oauthjs/oauth2-server error codes | ||
62 | * @see https://datatracker.ietf.org/doc/html/rfc6749#section-5.2 | ||
63 | **/ | ||
64 | export const OAuth2ErrorCode = { | ||
65 | /** | ||
66 | * The provided authorization grant (e.g., authorization code, resource owner | ||
67 | * credentials) or refresh token is invalid, expired, revoked, does not match | ||
68 | * the redirection URI used in the authorization request, or was issued to | ||
69 | * another client. | ||
70 | * | ||
71 | * @see https://github.com/oauthjs/node-oauth2-server/blob/master/lib/errors/invalid-grant-error.js | ||
72 | */ | ||
73 | INVALID_GRANT: 'invalid_grant', | ||
74 | |||
75 | /** | ||
76 | * Client authentication failed (e.g., unknown client, no client authentication | ||
77 | * included, or unsupported authentication method). | ||
78 | * | ||
79 | * @see https://github.com/oauthjs/node-oauth2-server/blob/master/lib/errors/invalid-client-error.js | ||
80 | */ | ||
81 | INVALID_CLIENT: 'invalid_client', | ||
82 | |||
83 | /** | ||
84 | * The access token provided is expired, revoked, malformed, or invalid for other reasons | ||
85 | * | ||
86 | * @see https://github.com/oauthjs/node-oauth2-server/blob/master/lib/errors/invalid-token-error.js | ||
87 | */ | ||
88 | INVALID_TOKEN: 'invalid_token' | ||
89 | } as const | ||
90 | |||
91 | export type OAuth2ErrorCodeType = typeof OAuth2ErrorCode[keyof typeof OAuth2ErrorCode] | ||
92 | export type ServerErrorCodeType = typeof ServerErrorCode[keyof typeof ServerErrorCode] | ||
diff --git a/packages/models/src/server/server-follow-create.model.ts b/packages/models/src/server/server-follow-create.model.ts new file mode 100644 index 000000000..3f90c7d6f --- /dev/null +++ b/packages/models/src/server/server-follow-create.model.ts | |||
@@ -0,0 +1,4 @@ | |||
1 | export interface ServerFollowCreate { | ||
2 | hosts?: string[] | ||
3 | handles?: string[] | ||
4 | } | ||
diff --git a/packages/models/src/server/server-log-level.type.ts b/packages/models/src/server/server-log-level.type.ts new file mode 100644 index 000000000..f0f31a4ae --- /dev/null +++ b/packages/models/src/server/server-log-level.type.ts | |||
@@ -0,0 +1 @@ | |||
export type ServerLogLevel = 'debug' | 'info' | 'warn' | 'error' | 'audit' | |||
diff --git a/packages/models/src/server/server-stats.model.ts b/packages/models/src/server/server-stats.model.ts new file mode 100644 index 000000000..5870ee73d --- /dev/null +++ b/packages/models/src/server/server-stats.model.ts | |||
@@ -0,0 +1,47 @@ | |||
1 | import { ActivityType } from '../activitypub/index.js' | ||
2 | import { VideoRedundancyStrategyWithManual } from '../redundancy/index.js' | ||
3 | |||
4 | type ActivityPubMessagesSuccess = Record<`totalActivityPub${ActivityType}MessagesSuccesses`, number> | ||
5 | type ActivityPubMessagesErrors = Record<`totalActivityPub${ActivityType}MessagesErrors`, number> | ||
6 | |||
7 | export interface ServerStats extends ActivityPubMessagesSuccess, ActivityPubMessagesErrors { | ||
8 | totalUsers: number | ||
9 | totalDailyActiveUsers: number | ||
10 | totalWeeklyActiveUsers: number | ||
11 | totalMonthlyActiveUsers: number | ||
12 | |||
13 | totalLocalVideos: number | ||
14 | totalLocalVideoViews: number | ||
15 | totalLocalVideoComments: number | ||
16 | totalLocalVideoFilesSize: number | ||
17 | |||
18 | totalVideos: number | ||
19 | totalVideoComments: number | ||
20 | |||
21 | totalLocalVideoChannels: number | ||
22 | totalLocalDailyActiveVideoChannels: number | ||
23 | totalLocalWeeklyActiveVideoChannels: number | ||
24 | totalLocalMonthlyActiveVideoChannels: number | ||
25 | |||
26 | totalLocalPlaylists: number | ||
27 | |||
28 | totalInstanceFollowers: number | ||
29 | totalInstanceFollowing: number | ||
30 | |||
31 | videosRedundancy: VideosRedundancyStats[] | ||
32 | |||
33 | totalActivityPubMessagesProcessed: number | ||
34 | totalActivityPubMessagesSuccesses: number | ||
35 | totalActivityPubMessagesErrors: number | ||
36 | |||
37 | activityPubMessagesProcessedPerSecond: number | ||
38 | totalActivityPubMessagesWaiting: number | ||
39 | } | ||
40 | |||
41 | export interface VideosRedundancyStats { | ||
42 | strategy: VideoRedundancyStrategyWithManual | ||
43 | totalSize: number | ||
44 | totalUsed: number | ||
45 | totalVideoFiles: number | ||
46 | totalVideos: number | ||
47 | } | ||