aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/account/account-settings/account-settings.component.html10
-rw-r--r--client/src/app/account/account-settings/account-settings.component.scss21
-rw-r--r--client/src/app/account/account-settings/account-settings.component.ts4
-rw-r--r--client/src/app/core/auth/auth.service.ts36
-rw-r--r--client/src/app/menu/menu.component.html2
-rw-r--r--client/src/app/menu/menu.component.scss8
-rw-r--r--client/src/app/menu/menu.component.ts4
-rw-r--r--client/src/app/shared/users/user.model.ts25
-rw-r--r--client/src/assets/default-avatar.pngbin0 -> 1674 bytes
-rw-r--r--client/src/sass/_mixins.scss5
-rw-r--r--config/default.yaml1
-rw-r--r--config/production.yaml.example1
-rw-r--r--config/test-1.yaml1
-rw-r--r--config/test-2.yaml1
-rw-r--r--config/test-3.yaml1
-rw-r--r--config/test-4.yaml1
-rw-r--r--config/test-5.yaml1
-rw-r--r--config/test-6.yaml1
-rw-r--r--server/initializers/constants.ts7
-rw-r--r--server/initializers/database.ts2
-rw-r--r--server/initializers/migrations/0115-account-avatar.ts31
-rw-r--r--server/models/account/account-interface.ts3
-rw-r--r--server/models/account/account.ts27
-rw-r--r--server/models/account/user.ts5
-rw-r--r--server/models/avatar/avatar-interface.ts16
-rw-r--r--server/models/avatar/avatar.ts24
-rw-r--r--server/models/avatar/index.ts1
-rw-r--r--server/models/index.ts1
-rw-r--r--shared/models/accounts/account.model.ts8
-rw-r--r--shared/models/avatars/avatar.model.ts5
-rw-r--r--shared/models/users/user.model.ts8
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 @@
1import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'
1import { Injectable } from '@angular/core' 2import { Injectable } from '@angular/core'
2import { Router } from '@angular/router' 3import { Router } from '@angular/router'
3import { Observable } from 'rxjs/Observable' 4
4import { Subject } from 'rxjs/Subject' 5import { NotificationsService } from 'angular2-notifications'
5import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http' 6import 'rxjs/add/observable/throw'
6import { ReplaySubject } from 'rxjs/ReplaySubject'
7import 'rxjs/add/operator/do' 7import 'rxjs/add/operator/do'
8import 'rxjs/add/operator/map' 8import 'rxjs/add/operator/map'
9import 'rxjs/add/operator/mergeMap' 9import 'rxjs/add/operator/mergeMap'
10import 'rxjs/add/observable/throw' 10import { Observable } from 'rxjs/Observable'
11 11import { ReplaySubject } from 'rxjs/ReplaySubject'
12import { NotificationsService } from 'angular2-notifications' 12import { Subject } from 'rxjs/Subject'
13 13import { OAuthClientLocal, User as UserServerModel, UserRefreshToken, UserRole, VideoChannel } from '../../../../../shared'
14import { AuthStatus } from './auth-status.model' 14import { Account } from '../../../../../shared/models/accounts'
15import { AuthUser } from './auth-user.model' 15import { UserLogin } from '../../../../../shared/models/users/user-login.model'
16import {
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)
24import { RestExtractor } from '../../shared/rest' 17import { RestExtractor } from '../../shared/rest'
25import { UserLogin } from '../../../../../shared/models/users/user-login.model'
26import { UserConstructorHash } from '../../shared/users/user.model' 18import { UserConstructorHash } from '../../shared/users/user.model'
27 19
20import { AuthStatus } from './auth-status.model'
21import { AuthUser } from './auth-user.model'
22
28interface UserLoginWithUsername extends UserLogin { 23interface 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 @@
1import { 1import { hasUserRight, User as UserServerModel, UserRight, UserRole, VideoChannel } from '../../../../../shared'
2 User as UserServerModel, 2import { Account } from '../../../../../shared/models/accounts'
3 UserRole,
4 VideoChannel,
5 UserRight,
6 hasUserRight
7} from '../../../../../shared'
8 3
9export type UserConstructorHash = { 4export 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}
23export class User implements UserServerModel { 15export 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
18storage: 18storage:
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
19storage: 19storage:
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
12storage: 12storage:
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
12storage: 12storage:
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
12storage: 12storage:
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
12storage: 12storage:
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
12storage: 12storage:
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
12storage: 12storage:
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
17const LAST_MIGRATION_VERSION = 110 17const 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 = {
105CONFIG.WEBSERVER.URL = CONFIG.WEBSERVER.SCHEME + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT 106CONFIG.WEBSERVER.URL = CONFIG.WEBSERVER.SCHEME + '://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT
106CONFIG.WEBSERVER.HOST = CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT 107CONFIG.WEBSERVER.HOST = CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.WEBSERVER.PORT
107 108
109const AVATARS_DIR = {
110 ACCOUNT: join(CONFIG.STORAGE.AVATARS_DIR, 'account')
111}
108// --------------------------------------------------------------------------- 112// ---------------------------------------------------------------------------
109 113
110const CONSTRAINTS_FIELDS = { 114const 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'
2import { flattenDepth } from 'lodash' 2import { flattenDepth } from 'lodash'
3require('pg').defaults.parseInt8 = true // Avoid BIGINT to be converted to string 3require('pg').defaults.parseInt8 = true // Avoid BIGINT to be converted to string
4import * as Sequelize from 'sequelize' 4import * as Sequelize from 'sequelize'
5import { AvatarModel } from '../models/avatar'
5 6
6import { CONFIG } from './constants' 7import { 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 @@
1import * as Sequelize from 'sequelize'
2import { PeerTubeDatabase } from '../database'
3
4async 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
24function down (options) {
25 throw new Error('Not implemented.')
26}
27
28export {
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 @@
1import * as Bluebird from 'bluebird' 1import * as Bluebird from 'bluebird'
2import * as Sequelize from 'sequelize' 2import * as Sequelize from 'sequelize'
3import { Account as FormattedAccount, ActivityPubActor } from '../../../shared' 3import { Account as FormattedAccount, ActivityPubActor } from '../../../shared'
4import { AvatarInstance } from '../avatar'
4import { ServerInstance } from '../server/server-interface' 5import { ServerInstance } from '../server/server-interface'
5import { VideoChannelInstance } from '../video/video-channel-interface' 6import { 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
56export interface AccountInstance extends AccountClass, AccountAttributes, Sequelize.Instance<AccountAttributes> { 58export 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
73export interface AccountModel extends AccountClass, Sequelize.Model<AccountInstance, AccountAttributes> {} 76export 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 @@
1import { join } from 'path'
1import * as Sequelize from 'sequelize' 2import * as Sequelize from 'sequelize'
3import { Avatar } from '../../../shared/models/avatars/avatar.model'
2import { 4import {
3 activityPubContextify, 5 activityPubContextify,
4 isAccountFollowersCountValid, 6 isAccountFollowersCountValid,
@@ -8,8 +10,10 @@ import {
8 isUserUsernameValid 10 isUserUsernameValid
9} from '../../helpers' 11} from '../../helpers'
10import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 12import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
13import { AVATARS_DIR } from '../../initializers'
11import { CONFIG, CONSTRAINTS_FIELDS } from '../../initializers/constants' 14import { CONFIG, CONSTRAINTS_FIELDS } from '../../initializers/constants'
12import { sendDeleteAccount } from '../../lib/activitypub/send/send-delete' 15import { sendDeleteAccount } from '../../lib/activitypub/send/send-delete'
16import { AvatarModel } from '../avatar'
13import { addMethodsToModel } from '../utils' 17import { addMethodsToModel } from '../utils'
14import { AccountAttributes, AccountInstance, AccountMethods } from './account-interface' 18import { 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
257function afterDestroy (account: AccountInstance) { 269function afterDestroy (account: AccountInstance) {
@@ -265,6 +277,15 @@ function afterDestroy (account: AccountInstance) {
265toFormattedJSON = function (this: AccountInstance) { 277toFormattedJSON = 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 @@
1import * as Sequelize from 'sequelize'
2
3export namespace AvatarMethods {}
4
5export interface AvatarClass {}
6
7export interface AvatarAttributes {
8 filename: string
9}
10
11export interface AvatarInstance extends AvatarClass, AvatarAttributes, Sequelize.Instance<AvatarAttributes> {
12 createdAt: Date
13 updatedAt: Date
14}
15
16export 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 @@
1import * as Sequelize from 'sequelize'
2import { addMethodsToModel } from '../utils'
3import { AvatarAttributes, AvatarInstance, AvatarMethods } from './avatar-interface'
4
5let Avatar: Sequelize.Model<AvatarInstance, AvatarAttributes>
6
7export 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 @@
1export * from './application' 1export * from './application'
2export * from './avatar'
2export * from './job' 3export * from './job'
3export * from './oauth' 4export * from './oauth'
4export * from './server' 5export * 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 @@
1import { Avatar } from '../avatars/avatar.model'
2
1export interface Account { 3export 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 @@
1export 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 @@
1import { Account } from '../accounts'
1import { VideoChannel } from '../videos/video-channel.model' 2import { VideoChannel } from '../videos/video-channel.model'
2import { UserRole } from './user-role' 3import { 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}