diff options
31 files changed, 207 insertions, 54 deletions
diff --git a/client/src/app/account/account-settings/account-settings.component.html b/client/src/app/account/account-settings/account-settings.component.html index 2509eb5aa..9e9f688d2 100644 --- a/client/src/app/account/account-settings/account-settings.component.html +++ b/client/src/app/account/account-settings/account-settings.component.html | |||
@@ -1,7 +1,13 @@ | |||
1 | <div class="user-info"> | 1 | <div class="user"> |
2 | {{ user.username }} | 2 | <img [src]="getAvatarPath()" alt="Avatar" /> |
3 | |||
4 | <div class="user-info"> | ||
5 | <div class="user-info-username">{{ user.username }}</div> | ||
6 | <div class="user-info-followers">{{ user.account.followersCount }} subscribers</div> | ||
7 | </div> | ||
3 | </div> | 8 | </div> |
4 | 9 | ||
10 | |||
5 | <div class="account-title">Account settings</div> | 11 | <div class="account-title">Account settings</div> |
6 | <my-account-change-password></my-account-change-password> | 12 | <my-account-change-password></my-account-change-password> |
7 | 13 | ||
diff --git a/client/src/app/account/account-settings/account-settings.component.scss b/client/src/app/account/account-settings/account-settings.component.scss index a0822631d..f514809b0 100644 --- a/client/src/app/account/account-settings/account-settings.component.scss +++ b/client/src/app/account/account-settings/account-settings.component.scss | |||
@@ -1,6 +1,21 @@ | |||
1 | .user-info { | 1 | .user { |
2 | font-size: 20px; | 2 | display: flex; |
3 | font-weight: $font-bold; | 3 | |
4 | img { | ||
5 | @include avatar(50px); | ||
6 | margin-right: 15px; | ||
7 | } | ||
8 | |||
9 | .user-info { | ||
10 | .user-info-username { | ||
11 | font-size: 20px; | ||
12 | font-weight: $font-bold; | ||
13 | } | ||
14 | |||
15 | .user-info-followers { | ||
16 | font-size: 15px; | ||
17 | } | ||
18 | } | ||
4 | } | 19 | } |
5 | 20 | ||
6 | .account-title { | 21 | .account-title { |
diff --git a/client/src/app/account/account-settings/account-settings.component.ts b/client/src/app/account/account-settings/account-settings.component.ts index c3b670e02..cba251000 100644 --- a/client/src/app/account/account-settings/account-settings.component.ts +++ b/client/src/app/account/account-settings/account-settings.component.ts | |||
@@ -15,4 +15,8 @@ export class AccountSettingsComponent implements OnInit { | |||
15 | ngOnInit () { | 15 | ngOnInit () { |
16 | this.user = this.authService.getUser() | 16 | this.user = this.authService.getUser() |
17 | } | 17 | } |
18 | |||
19 | getAvatarPath () { | ||
20 | return this.user.getAvatarPath() | ||
21 | } | ||
18 | } | 22 | } |
diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts index 9e6c6b888..fd2708c11 100644 --- a/client/src/app/core/auth/auth.service.ts +++ b/client/src/app/core/auth/auth.service.ts | |||
@@ -1,30 +1,25 @@ | |||
1 | import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http' | ||
1 | import { Injectable } from '@angular/core' | 2 | import { Injectable } from '@angular/core' |
2 | import { Router } from '@angular/router' | 3 | import { Router } from '@angular/router' |
3 | import { Observable } from 'rxjs/Observable' | 4 | |
4 | import { Subject } from 'rxjs/Subject' | 5 | import { NotificationsService } from 'angular2-notifications' |
5 | import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http' | 6 | import 'rxjs/add/observable/throw' |
6 | import { ReplaySubject } from 'rxjs/ReplaySubject' | ||
7 | import 'rxjs/add/operator/do' | 7 | import 'rxjs/add/operator/do' |
8 | import 'rxjs/add/operator/map' | 8 | import 'rxjs/add/operator/map' |
9 | import 'rxjs/add/operator/mergeMap' | 9 | import 'rxjs/add/operator/mergeMap' |
10 | import 'rxjs/add/observable/throw' | 10 | import { Observable } from 'rxjs/Observable' |
11 | 11 | import { ReplaySubject } from 'rxjs/ReplaySubject' | |
12 | import { NotificationsService } from 'angular2-notifications' | 12 | import { Subject } from 'rxjs/Subject' |
13 | 13 | import { OAuthClientLocal, User as UserServerModel, UserRefreshToken, UserRole, VideoChannel } from '../../../../../shared' | |
14 | import { AuthStatus } from './auth-status.model' | 14 | import { Account } from '../../../../../shared/models/accounts' |
15 | import { AuthUser } from './auth-user.model' | 15 | import { UserLogin } from '../../../../../shared/models/users/user-login.model' |
16 | import { | ||
17 | OAuthClientLocal, | ||
18 | UserRole, | ||
19 | UserRefreshToken, | ||
20 | VideoChannel, | ||
21 | User as UserServerModel | ||
22 | } from '../../../../../shared' | ||
23 | // Do not use the barrel (dependency loop) | 16 | // Do not use the barrel (dependency loop) |
24 | import { RestExtractor } from '../../shared/rest' | 17 | import { RestExtractor } from '../../shared/rest' |
25 | import { UserLogin } from '../../../../../shared/models/users/user-login.model' | ||
26 | import { UserConstructorHash } from '../../shared/users/user.model' | 18 | import { UserConstructorHash } from '../../shared/users/user.model' |
27 | 19 | ||
20 | import { AuthStatus } from './auth-status.model' | ||
21 | import { AuthUser } from './auth-user.model' | ||
22 | |||
28 | interface UserLoginWithUsername extends UserLogin { | 23 | interface UserLoginWithUsername extends UserLogin { |
29 | access_token: string | 24 | access_token: string |
30 | refresh_token: string | 25 | refresh_token: string |
@@ -42,10 +37,7 @@ interface UserLoginWithUserInformation extends UserLogin { | |||
42 | displayNSFW: boolean | 37 | displayNSFW: boolean |
43 | email: string | 38 | email: string |
44 | videoQuota: number | 39 | videoQuota: number |
45 | account: { | 40 | account: Account |
46 | id: number | ||
47 | uuid: string | ||
48 | } | ||
49 | videoChannels: VideoChannel[] | 41 | videoChannels: VideoChannel[] |
50 | } | 42 | } |
51 | 43 | ||
diff --git a/client/src/app/menu/menu.component.html b/client/src/app/menu/menu.component.html index 0ed8ec518..7a80fa4de 100644 --- a/client/src/app/menu/menu.component.html +++ b/client/src/app/menu/menu.component.html | |||
@@ -1,5 +1,7 @@ | |||
1 | <menu> | 1 | <menu> |
2 | <div *ngIf="isLoggedIn" class="logged-in-block"> | 2 | <div *ngIf="isLoggedIn" class="logged-in-block"> |
3 | <img [src]="getUserAvatarPath()" alt="Avatar" /> | ||
4 | |||
3 | <div class="logged-in-info"> | 5 | <div class="logged-in-info"> |
4 | <a routerLink="/account/settings" class="logged-in-username">{{ user.username }}</a> | 6 | <a routerLink="/account/settings" class="logged-in-username">{{ user.username }}</a> |
5 | <div class="logged-in-email">{{ user.email }}</div> | 7 | <div class="logged-in-email">{{ user.email }}</div> |
diff --git a/client/src/app/menu/menu.component.scss b/client/src/app/menu/menu.component.scss index 9d67ca66c..5d6fd61c6 100644 --- a/client/src/app/menu/menu.component.scss +++ b/client/src/app/menu/menu.component.scss | |||
@@ -21,9 +21,15 @@ menu { | |||
21 | justify-content: center; | 21 | justify-content: center; |
22 | margin-bottom: 35px; | 22 | margin-bottom: 35px; |
23 | 23 | ||
24 | img { | ||
25 | margin-left: 20px; | ||
26 | margin-right: 10px; | ||
27 | |||
28 | @include avatar(34px); | ||
29 | } | ||
30 | |||
24 | .logged-in-info { | 31 | .logged-in-info { |
25 | flex-grow: 1; | 32 | flex-grow: 1; |
26 | margin-left: 40px; | ||
27 | 33 | ||
28 | .logged-in-username { | 34 | .logged-in-username { |
29 | font-size: 16px; | 35 | font-size: 16px; |
diff --git a/client/src/app/menu/menu.component.ts b/client/src/app/menu/menu.component.ts index 4c35bb3a5..8b8b714a8 100644 --- a/client/src/app/menu/menu.component.ts +++ b/client/src/app/menu/menu.component.ts | |||
@@ -51,6 +51,10 @@ export class MenuComponent implements OnInit { | |||
51 | ) | 51 | ) |
52 | } | 52 | } |
53 | 53 | ||
54 | getUserAvatarPath () { | ||
55 | return this.user.getAvatarPath() | ||
56 | } | ||
57 | |||
54 | isRegistrationAllowed () { | 58 | isRegistrationAllowed () { |
55 | return this.serverService.getConfig().signup.allowed | 59 | return this.serverService.getConfig().signup.allowed |
56 | } | 60 | } |
diff --git a/client/src/app/shared/users/user.model.ts b/client/src/app/shared/users/user.model.ts index b075ab717..83990d8b8 100644 --- a/client/src/app/shared/users/user.model.ts +++ b/client/src/app/shared/users/user.model.ts | |||
@@ -1,10 +1,5 @@ | |||
1 | import { | 1 | import { hasUserRight, User as UserServerModel, UserRight, UserRole, VideoChannel } from '../../../../../shared' |
2 | User as UserServerModel, | 2 | import { Account } from '../../../../../shared/models/accounts' |
3 | UserRole, | ||
4 | VideoChannel, | ||
5 | UserRight, | ||
6 | hasUserRight | ||
7 | } from '../../../../../shared' | ||
8 | 3 | ||
9 | export type UserConstructorHash = { | 4 | export type UserConstructorHash = { |
10 | id: number, | 5 | id: number, |
@@ -14,10 +9,7 @@ export type UserConstructorHash = { | |||
14 | videoQuota?: number, | 9 | videoQuota?: number, |
15 | displayNSFW?: boolean, | 10 | displayNSFW?: boolean, |
16 | createdAt?: Date, | 11 | createdAt?: Date, |
17 | account?: { | 12 | account?: Account, |
18 | id: number | ||
19 | uuid: string | ||
20 | }, | ||
21 | videoChannels?: VideoChannel[] | 13 | videoChannels?: VideoChannel[] |
22 | } | 14 | } |
23 | export class User implements UserServerModel { | 15 | export class User implements UserServerModel { |
@@ -27,10 +19,7 @@ export class User implements UserServerModel { | |||
27 | role: UserRole | 19 | role: UserRole |
28 | displayNSFW: boolean | 20 | displayNSFW: boolean |
29 | videoQuota: number | 21 | videoQuota: number |
30 | account: { | 22 | account: Account |
31 | id: number | ||
32 | uuid: string | ||
33 | } | ||
34 | videoChannels: VideoChannel[] | 23 | videoChannels: VideoChannel[] |
35 | createdAt: Date | 24 | createdAt: Date |
36 | 25 | ||
@@ -61,4 +50,10 @@ export class User implements UserServerModel { | |||
61 | hasRight (right: UserRight) { | 50 | hasRight (right: UserRight) { |
62 | return hasUserRight(this.role, right) | 51 | return hasUserRight(this.role, right) |
63 | } | 52 | } |
53 | |||
54 | getAvatarPath () { | ||
55 | if (this.account && this.account.avatar) return this.account.avatar.path | ||
56 | |||
57 | return '/assets/default-avatar.png' | ||
58 | } | ||
64 | } | 59 | } |
diff --git a/client/src/assets/default-avatar.png b/client/src/assets/default-avatar.png new file mode 100644 index 000000000..4b7fd2c0a --- /dev/null +++ b/client/src/assets/default-avatar.png | |||
Binary files differ | |||
diff --git a/client/src/sass/_mixins.scss b/client/src/sass/_mixins.scss index 5798b8f6e..e44cf064d 100644 --- a/client/src/sass/_mixins.scss +++ b/client/src/sass/_mixins.scss | |||
@@ -39,3 +39,8 @@ | |||
39 | @include peertube-button; | 39 | @include peertube-button; |
40 | @include disable-default-a-behaviour; | 40 | @include disable-default-a-behaviour; |
41 | } | 41 | } |
42 | |||
43 | @mixin avatar ($size) { | ||
44 | width: $size; | ||
45 | height: $size; | ||
46 | } | ||
diff --git a/config/default.yaml b/config/default.yaml index b53fa0d5b..2c1043067 100644 --- a/config/default.yaml +++ b/config/default.yaml | |||
@@ -16,6 +16,7 @@ database: | |||
16 | 16 | ||
17 | # From the project root directory | 17 | # From the project root directory |
18 | storage: | 18 | storage: |
19 | avatars: 'avatars/' | ||
19 | certs: 'certs/' | 20 | certs: 'certs/' |
20 | videos: 'videos/' | 21 | videos: 'videos/' |
21 | logs: 'logs/' | 22 | logs: 'logs/' |
diff --git a/config/production.yaml.example b/config/production.yaml.example index 1af20a9e4..404d35c16 100644 --- a/config/production.yaml.example +++ b/config/production.yaml.example | |||
@@ -17,6 +17,7 @@ database: | |||
17 | 17 | ||
18 | # From the project root directory | 18 | # From the project root directory |
19 | storage: | 19 | storage: |
20 | avatars: 'avatars/' | ||
20 | certs: 'certs/' | 21 | certs: 'certs/' |
21 | videos: 'videos/' | 22 | videos: 'videos/' |
22 | logs: 'logs/' | 23 | logs: 'logs/' |
diff --git a/config/test-1.yaml b/config/test-1.yaml index d9b4d2b1a..49fbebf04 100644 --- a/config/test-1.yaml +++ b/config/test-1.yaml | |||
@@ -10,6 +10,7 @@ database: | |||
10 | 10 | ||
11 | # From the project root directory | 11 | # From the project root directory |
12 | storage: | 12 | storage: |
13 | avatars: 'test1/avatars/' | ||
13 | certs: 'test1/certs/' | 14 | certs: 'test1/certs/' |
14 | videos: 'test1/videos/' | 15 | videos: 'test1/videos/' |
15 | logs: 'test1/logs/' | 16 | logs: 'test1/logs/' |
diff --git a/config/test-2.yaml b/config/test-2.yaml index 236dcb10d..ff0df5962 100644 --- a/config/test-2.yaml +++ b/config/test-2.yaml | |||
@@ -10,6 +10,7 @@ database: | |||
10 | 10 | ||
11 | # From the project root directory | 11 | # From the project root directory |
12 | storage: | 12 | storage: |
13 | avatars: 'test2/avatars/' | ||
13 | certs: 'test2/certs/' | 14 | certs: 'test2/certs/' |
14 | videos: 'test2/videos/' | 15 | videos: 'test2/videos/' |
15 | logs: 'test2/logs/' | 16 | logs: 'test2/logs/' |
diff --git a/config/test-3.yaml b/config/test-3.yaml index 291b43edc..4fbb00050 100644 --- a/config/test-3.yaml +++ b/config/test-3.yaml | |||
@@ -10,6 +10,7 @@ database: | |||
10 | 10 | ||
11 | # From the project root directory | 11 | # From the project root directory |
12 | storage: | 12 | storage: |
13 | avatars: 'test3/avatars/' | ||
13 | certs: 'test3/certs/' | 14 | certs: 'test3/certs/' |
14 | videos: 'test3/videos/' | 15 | videos: 'test3/videos/' |
15 | logs: 'test3/logs/' | 16 | logs: 'test3/logs/' |
diff --git a/config/test-4.yaml b/config/test-4.yaml index 6f80939fc..e4f0f2691 100644 --- a/config/test-4.yaml +++ b/config/test-4.yaml | |||
@@ -10,6 +10,7 @@ database: | |||
10 | 10 | ||
11 | # From the project root directory | 11 | # From the project root directory |
12 | storage: | 12 | storage: |
13 | avatars: 'test4/avatars/' | ||
13 | certs: 'test4/certs/' | 14 | certs: 'test4/certs/' |
14 | videos: 'test4/videos/' | 15 | videos: 'test4/videos/' |
15 | logs: 'test4/logs/' | 16 | logs: 'test4/logs/' |
diff --git a/config/test-5.yaml b/config/test-5.yaml index 0b5eab72e..610f523c8 100644 --- a/config/test-5.yaml +++ b/config/test-5.yaml | |||
@@ -10,6 +10,7 @@ database: | |||
10 | 10 | ||
11 | # From the project root directory | 11 | # From the project root directory |
12 | storage: | 12 | storage: |
13 | avatars: 'test5/avatars/' | ||
13 | certs: 'test5/certs/' | 14 | certs: 'test5/certs/' |
14 | videos: 'test5/videos/' | 15 | videos: 'test5/videos/' |
15 | logs: 'test5/logs/' | 16 | logs: 'test5/logs/' |
diff --git a/config/test-6.yaml b/config/test-6.yaml index 5d33e45b9..088b55c17 100644 --- a/config/test-6.yaml +++ b/config/test-6.yaml | |||
@@ -10,6 +10,7 @@ database: | |||
10 | 10 | ||
11 | # From the project root directory | 11 | # From the project root directory |
12 | storage: | 12 | storage: |
13 | avatars: 'test6/avatars/' | ||
13 | certs: 'test6/certs/' | 14 | certs: 'test6/certs/' |
14 | videos: 'test6/videos/' | 15 | videos: 'test6/videos/' |
15 | logs: 'test6/logs/' | 16 | logs: 'test6/logs/' |
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index e3d779456..144a4edbf 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -14,7 +14,7 @@ import { FollowState } from '../../shared/models/accounts/follow.model' | |||
14 | 14 | ||
15 | // --------------------------------------------------------------------------- | 15 | // --------------------------------------------------------------------------- |
16 | 16 | ||
17 | const LAST_MIGRATION_VERSION = 110 | 17 | const LAST_MIGRATION_VERSION = 115 |
18 | 18 | ||
19 | // --------------------------------------------------------------------------- | 19 | // --------------------------------------------------------------------------- |
20 | 20 | ||
@@ -60,6 +60,7 @@ const CONFIG = { | |||
60 | PASSWORD: config.get<string>('database.password') | 60 | PASSWORD: config.get<string>('database.password') |
61 | }, | 61 | }, |
62 | STORAGE: { | 62 | STORAGE: { |
63 | AVATARS_DIR: join(root(), config.get<string>('storage.avatars')), | ||
63 | LOG_DIR: join(root(), config.get<string>('storage.logs')), | 64 | LOG_DIR: join(root(), config.get<string>('storage.logs')), |
64 | VIDEOS_DIR: join(root(), config.get<string>('storage.videos')), | 65 | VIDEOS_DIR: join(root(), config.get<string>('storage.videos')), |
65 | THUMBNAILS_DIR: join(root(), config.get<string>('storage.thumbnails')), | 66 | THUMBNAILS_DIR: join(root(), config.get<string>('storage.thumbnails')), |
@@ -105,6 +106,9 @@ const CONFIG = { | |||
105 | CONFIG.WEBSERVER.URL = CONFIG.WEBSERVER.SCHEME + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT | 106 | CONFIG.WEBSERVER.URL = CONFIG.WEBSERVER.SCHEME + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT |
106 | CONFIG.WEBSERVER.HOST = CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT | 107 | CONFIG.WEBSERVER.HOST = CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT |
107 | 108 | ||
109 | const AVATARS_DIR = { | ||
110 | ACCOUNT: join(CONFIG.STORAGE.AVATARS_DIR, 'account') | ||
111 | } | ||
108 | // --------------------------------------------------------------------------- | 112 | // --------------------------------------------------------------------------- |
109 | 113 | ||
110 | const CONSTRAINTS_FIELDS = { | 114 | const CONSTRAINTS_FIELDS = { |
@@ -356,6 +360,7 @@ export { | |||
356 | PREVIEWS_SIZE, | 360 | PREVIEWS_SIZE, |
357 | REMOTE_SCHEME, | 361 | REMOTE_SCHEME, |
358 | FOLLOW_STATES, | 362 | FOLLOW_STATES, |
363 | AVATARS_DIR, | ||
359 | SEARCHABLE_COLUMNS, | 364 | SEARCHABLE_COLUMNS, |
360 | SERVER_ACCOUNT_NAME, | 365 | SERVER_ACCOUNT_NAME, |
361 | PRIVATE_RSA_KEY_SIZE, | 366 | PRIVATE_RSA_KEY_SIZE, |
diff --git a/server/initializers/database.ts b/server/initializers/database.ts index 90dbba5b9..bb95992e1 100644 --- a/server/initializers/database.ts +++ b/server/initializers/database.ts | |||
@@ -2,6 +2,7 @@ import { join } from 'path' | |||
2 | import { flattenDepth } from 'lodash' | 2 | import { flattenDepth } from 'lodash' |
3 | require('pg').defaults.parseInt8 = true // Avoid BIGINT to be converted to string | 3 | require('pg').defaults.parseInt8 = true // Avoid BIGINT to be converted to string |
4 | import * as Sequelize from 'sequelize' | 4 | import * as Sequelize from 'sequelize' |
5 | import { AvatarModel } from '../models/avatar' | ||
5 | 6 | ||
6 | import { CONFIG } from './constants' | 7 | import { CONFIG } from './constants' |
7 | // Do not use barrel, we need to load database first | 8 | // Do not use barrel, we need to load database first |
@@ -36,6 +37,7 @@ export type PeerTubeDatabase = { | |||
36 | init?: (silent: boolean) => Promise<void>, | 37 | init?: (silent: boolean) => Promise<void>, |
37 | 38 | ||
38 | Application?: ApplicationModel, | 39 | Application?: ApplicationModel, |
40 | Avatar?: AvatarModel, | ||
39 | Account?: AccountModel, | 41 | Account?: AccountModel, |
40 | Job?: JobModel, | 42 | Job?: JobModel, |
41 | OAuthClient?: OAuthClientModel, | 43 | OAuthClient?: OAuthClientModel, |
diff --git a/server/initializers/migrations/0115-account-avatar.ts b/server/initializers/migrations/0115-account-avatar.ts new file mode 100644 index 000000000..e3531f5ce --- /dev/null +++ b/server/initializers/migrations/0115-account-avatar.ts | |||
@@ -0,0 +1,31 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | import { PeerTubeDatabase } from '../database' | ||
3 | |||
4 | async function up (utils: { | ||
5 | transaction: Sequelize.Transaction, | ||
6 | queryInterface: Sequelize.QueryInterface, | ||
7 | sequelize: Sequelize.Sequelize, | ||
8 | db: PeerTubeDatabase | ||
9 | }): Promise<void> { | ||
10 | await db.Avatar.sync() | ||
11 | |||
12 | const data = { | ||
13 | type: Sequelize.INTEGER, | ||
14 | allowNull: true, | ||
15 | references: { | ||
16 | model: 'Avatars', | ||
17 | key: 'id' | ||
18 | }, | ||
19 | onDelete: 'CASCADE' | ||
20 | } | ||
21 | await utils.queryInterface.addColumn('Accounts', 'avatarId', data) | ||
22 | } | ||
23 | |||
24 | function down (options) { | ||
25 | throw new Error('Not implemented.') | ||
26 | } | ||
27 | |||
28 | export { | ||
29 | up, | ||
30 | down | ||
31 | } | ||
diff --git a/server/models/account/account-interface.ts b/server/models/account/account-interface.ts index b369766dc..46fe068e3 100644 --- a/server/models/account/account-interface.ts +++ b/server/models/account/account-interface.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import * as Bluebird from 'bluebird' | 1 | import * as Bluebird from 'bluebird' |
2 | import * as Sequelize from 'sequelize' | 2 | import * as Sequelize from 'sequelize' |
3 | import { Account as FormattedAccount, ActivityPubActor } from '../../../shared' | 3 | import { Account as FormattedAccount, ActivityPubActor } from '../../../shared' |
4 | import { AvatarInstance } from '../avatar' | ||
4 | import { ServerInstance } from '../server/server-interface' | 5 | import { ServerInstance } from '../server/server-interface' |
5 | import { VideoChannelInstance } from '../video/video-channel-interface' | 6 | import { VideoChannelInstance } from '../video/video-channel-interface' |
6 | 7 | ||
@@ -51,6 +52,7 @@ export interface AccountAttributes { | |||
51 | serverId?: number | 52 | serverId?: number |
52 | userId?: number | 53 | userId?: number |
53 | applicationId?: number | 54 | applicationId?: number |
55 | avatarId?: number | ||
54 | } | 56 | } |
55 | 57 | ||
56 | export interface AccountInstance extends AccountClass, AccountAttributes, Sequelize.Instance<AccountAttributes> { | 58 | export interface AccountInstance extends AccountClass, AccountAttributes, Sequelize.Instance<AccountAttributes> { |
@@ -68,6 +70,7 @@ export interface AccountInstance extends AccountClass, AccountAttributes, Sequel | |||
68 | 70 | ||
69 | Server: ServerInstance | 71 | Server: ServerInstance |
70 | VideoChannels: VideoChannelInstance[] | 72 | VideoChannels: VideoChannelInstance[] |
73 | Avatar: AvatarInstance | ||
71 | } | 74 | } |
72 | 75 | ||
73 | export interface AccountModel extends AccountClass, Sequelize.Model<AccountInstance, AccountAttributes> {} | 76 | export interface AccountModel extends AccountClass, Sequelize.Model<AccountInstance, AccountAttributes> {} |
diff --git a/server/models/account/account.ts b/server/models/account/account.ts index 61a88524c..15be1126b 100644 --- a/server/models/account/account.ts +++ b/server/models/account/account.ts | |||
@@ -1,4 +1,6 @@ | |||
1 | import { join } from 'path' | ||
1 | import * as Sequelize from 'sequelize' | 2 | import * as Sequelize from 'sequelize' |
3 | import { Avatar } from '../../../shared/models/avatars/avatar.model' | ||
2 | import { | 4 | import { |
3 | activityPubContextify, | 5 | activityPubContextify, |
4 | isAccountFollowersCountValid, | 6 | isAccountFollowersCountValid, |
@@ -8,8 +10,10 @@ import { | |||
8 | isUserUsernameValid | 10 | isUserUsernameValid |
9 | } from '../../helpers' | 11 | } from '../../helpers' |
10 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | 12 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
13 | import { AVATARS_DIR } from '../../initializers' | ||
11 | import { CONFIG, CONSTRAINTS_FIELDS } from '../../initializers/constants' | 14 | import { CONFIG, CONSTRAINTS_FIELDS } from '../../initializers/constants' |
12 | import { sendDeleteAccount } from '../../lib/activitypub/send/send-delete' | 15 | import { sendDeleteAccount } from '../../lib/activitypub/send/send-delete' |
16 | import { AvatarModel } from '../avatar' | ||
13 | import { addMethodsToModel } from '../utils' | 17 | import { addMethodsToModel } from '../utils' |
14 | import { AccountAttributes, AccountInstance, AccountMethods } from './account-interface' | 18 | import { AccountAttributes, AccountInstance, AccountMethods } from './account-interface' |
15 | 19 | ||
@@ -252,6 +256,14 @@ function associate (models) { | |||
252 | as: 'followers', | 256 | as: 'followers', |
253 | onDelete: 'cascade' | 257 | onDelete: 'cascade' |
254 | }) | 258 | }) |
259 | |||
260 | Account.hasOne(models.Avatar, { | ||
261 | foreignKey: { | ||
262 | name: 'avatarId', | ||
263 | allowNull: true | ||
264 | }, | ||
265 | onDelete: 'cascade' | ||
266 | }) | ||
255 | } | 267 | } |
256 | 268 | ||
257 | function afterDestroy (account: AccountInstance) { | 269 | function afterDestroy (account: AccountInstance) { |
@@ -265,6 +277,15 @@ function afterDestroy (account: AccountInstance) { | |||
265 | toFormattedJSON = function (this: AccountInstance) { | 277 | toFormattedJSON = function (this: AccountInstance) { |
266 | let host = CONFIG.WEBSERVER.HOST | 278 | let host = CONFIG.WEBSERVER.HOST |
267 | let score: number | 279 | let score: number |
280 | let avatar: Avatar = null | ||
281 | |||
282 | if (this.Avatar) { | ||
283 | avatar = { | ||
284 | path: join(AVATARS_DIR.ACCOUNT, this.Avatar.filename), | ||
285 | createdAt: this.Avatar.createdAt, | ||
286 | updatedAt: this.Avatar.updatedAt | ||
287 | } | ||
288 | } | ||
268 | 289 | ||
269 | if (this.Server) { | 290 | if (this.Server) { |
270 | host = this.Server.host | 291 | host = this.Server.host |
@@ -273,11 +294,15 @@ toFormattedJSON = function (this: AccountInstance) { | |||
273 | 294 | ||
274 | const json = { | 295 | const json = { |
275 | id: this.id, | 296 | id: this.id, |
297 | uuid: this.uuid, | ||
276 | host, | 298 | host, |
277 | score, | 299 | score, |
278 | name: this.name, | 300 | name: this.name, |
301 | followingCount: this.followingCount, | ||
302 | followersCount: this.followersCount, | ||
279 | createdAt: this.createdAt, | 303 | createdAt: this.createdAt, |
280 | updatedAt: this.updatedAt | 304 | updatedAt: this.updatedAt, |
305 | avatar | ||
281 | } | 306 | } |
282 | 307 | ||
283 | return json | 308 | return json |
diff --git a/server/models/account/user.ts b/server/models/account/user.ts index 8f7c9b013..3705947c0 100644 --- a/server/models/account/user.ts +++ b/server/models/account/user.ts | |||
@@ -157,10 +157,7 @@ toFormattedJSON = function (this: UserInstance) { | |||
157 | roleLabel: USER_ROLE_LABELS[this.role], | 157 | roleLabel: USER_ROLE_LABELS[this.role], |
158 | videoQuota: this.videoQuota, | 158 | videoQuota: this.videoQuota, |
159 | createdAt: this.createdAt, | 159 | createdAt: this.createdAt, |
160 | account: { | 160 | account: this.Account.toFormattedJSON() |
161 | id: this.Account.id, | ||
162 | uuid: this.Account.uuid | ||
163 | } | ||
164 | } | 161 | } |
165 | 162 | ||
166 | if (Array.isArray(this.Account.VideoChannels) === true) { | 163 | if (Array.isArray(this.Account.VideoChannels) === true) { |
diff --git a/server/models/avatar/avatar-interface.ts b/server/models/avatar/avatar-interface.ts new file mode 100644 index 000000000..4af2b87b7 --- /dev/null +++ b/server/models/avatar/avatar-interface.ts | |||
@@ -0,0 +1,16 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | export namespace AvatarMethods {} | ||
4 | |||
5 | export interface AvatarClass {} | ||
6 | |||
7 | export interface AvatarAttributes { | ||
8 | filename: string | ||
9 | } | ||
10 | |||
11 | export interface AvatarInstance extends AvatarClass, AvatarAttributes, Sequelize.Instance<AvatarAttributes> { | ||
12 | createdAt: Date | ||
13 | updatedAt: Date | ||
14 | } | ||
15 | |||
16 | export interface AvatarModel extends AvatarClass, Sequelize.Model<AvatarInstance, AvatarAttributes> {} | ||
diff --git a/server/models/avatar/avatar.ts b/server/models/avatar/avatar.ts new file mode 100644 index 000000000..3d329d888 --- /dev/null +++ b/server/models/avatar/avatar.ts | |||
@@ -0,0 +1,24 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | import { addMethodsToModel } from '../utils' | ||
3 | import { AvatarAttributes, AvatarInstance, AvatarMethods } from './avatar-interface' | ||
4 | |||
5 | let Avatar: Sequelize.Model<AvatarInstance, AvatarAttributes> | ||
6 | |||
7 | export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { | ||
8 | Avatar = sequelize.define<AvatarInstance, AvatarAttributes>('Avatar', | ||
9 | { | ||
10 | filename: { | ||
11 | type: DataTypes.STRING, | ||
12 | allowNull: false | ||
13 | } | ||
14 | }, | ||
15 | {} | ||
16 | ) | ||
17 | |||
18 | const classMethods = [] | ||
19 | addMethodsToModel(Avatar, classMethods) | ||
20 | |||
21 | return Avatar | ||
22 | } | ||
23 | |||
24 | // ------------------------------ Statics ------------------------------ | ||
diff --git a/server/models/avatar/index.ts b/server/models/avatar/index.ts new file mode 100644 index 000000000..877aed1ce --- /dev/null +++ b/server/models/avatar/index.ts | |||
@@ -0,0 +1 @@ | |||
export * from './avatar-interface' | |||
diff --git a/server/models/index.ts b/server/models/index.ts index 65faa5294..fedd97dd1 100644 --- a/server/models/index.ts +++ b/server/models/index.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | export * from './application' | 1 | export * from './application' |
2 | export * from './avatar' | ||
2 | export * from './job' | 3 | export * from './job' |
3 | export * from './oauth' | 4 | export * from './oauth' |
4 | export * from './server' | 5 | export * from './server' |
diff --git a/shared/models/accounts/account.model.ts b/shared/models/accounts/account.model.ts index 338426dc7..d14701317 100644 --- a/shared/models/accounts/account.model.ts +++ b/shared/models/accounts/account.model.ts | |||
@@ -1,5 +1,13 @@ | |||
1 | import { Avatar } from '../avatars/avatar.model' | ||
2 | |||
1 | export interface Account { | 3 | export interface Account { |
2 | id: number | 4 | id: number |
5 | uuid: string | ||
3 | name: string | 6 | name: string |
4 | host: string | 7 | host: string |
8 | followingCount: number | ||
9 | followersCount: number | ||
10 | createdAt: Date | ||
11 | updatedAt: Date | ||
12 | avatar: Avatar | ||
5 | } | 13 | } |
diff --git a/shared/models/avatars/avatar.model.ts b/shared/models/avatars/avatar.model.ts new file mode 100644 index 000000000..301d00929 --- /dev/null +++ b/shared/models/avatars/avatar.model.ts | |||
@@ -0,0 +1,5 @@ | |||
1 | export interface Avatar { | ||
2 | path: string | ||
3 | createdAt: Date | string | ||
4 | updatedAt: Date | string | ||
5 | } | ||
diff --git a/shared/models/users/user.model.ts b/shared/models/users/user.model.ts index a8012734c..4b17881e5 100644 --- a/shared/models/users/user.model.ts +++ b/shared/models/users/user.model.ts | |||
@@ -1,3 +1,4 @@ | |||
1 | import { Account } from '../accounts' | ||
1 | import { VideoChannel } from '../videos/video-channel.model' | 2 | import { VideoChannel } from '../videos/video-channel.model' |
2 | import { UserRole } from './user-role' | 3 | import { UserRole } from './user-role' |
3 | 4 | ||
@@ -8,10 +9,7 @@ export interface User { | |||
8 | displayNSFW: boolean | 9 | displayNSFW: boolean |
9 | role: UserRole | 10 | role: UserRole |
10 | videoQuota: number | 11 | videoQuota: number |
11 | createdAt: Date, | 12 | createdAt: Date |
12 | account: { | 13 | account: Account |
13 | id: number | ||
14 | uuid: string | ||
15 | } | ||
16 | videoChannels?: VideoChannel[] | 14 | videoChannels?: VideoChannel[] |
17 | } | 15 | } |