aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/helpers/custom-validators/activitypub/actor.ts39
-rw-r--r--server/initializers/constants.ts2
-rw-r--r--server/initializers/migrations/0645-actor-remote-creation-date.ts26
-rw-r--r--server/lib/activitypub/actor.ts4
-rw-r--r--server/models/account/account.ts2
-rw-r--r--server/models/activitypub/actor.ts15
-rw-r--r--server/models/video/video-channel.ts2
-rw-r--r--server/tests/api/users/users-multiple-servers.ts28
-rw-r--r--server/types/models/account/actor.ts2
9 files changed, 78 insertions, 42 deletions
diff --git a/server/helpers/custom-validators/activitypub/actor.ts b/server/helpers/custom-validators/activitypub/actor.ts
index 877345157..675a7b663 100644
--- a/server/helpers/custom-validators/activitypub/actor.ts
+++ b/server/helpers/custom-validators/activitypub/actor.ts
@@ -1,6 +1,6 @@
1import validator from 'validator' 1import validator from 'validator'
2import { CONSTRAINTS_FIELDS } from '../../../initializers/constants' 2import { CONSTRAINTS_FIELDS } from '../../../initializers/constants'
3import { exists, isArray } from '../misc' 3import { exists, isArray, isDateValid } from '../misc'
4import { isActivityPubUrlValid, isBaseActivityValid, setValidAttributedTo } from './misc' 4import { isActivityPubUrlValid, isBaseActivityValid, setValidAttributedTo } from './misc'
5import { isHostValid } from '../servers' 5import { isHostValid } from '../servers'
6import { peertubeTruncate } from '@server/helpers/core-utils' 6import { peertubeTruncate } from '@server/helpers/core-utils'
@@ -47,7 +47,21 @@ function isActorPrivateKeyValid (privateKey: string) {
47 validator.isLength(privateKey, CONSTRAINTS_FIELDS.ACTORS.PRIVATE_KEY) 47 validator.isLength(privateKey, CONSTRAINTS_FIELDS.ACTORS.PRIVATE_KEY)
48} 48}
49 49
50function isActorObjectValid (actor: any) { 50function isActorFollowingCountValid (value: string) {
51 return exists(value) && validator.isInt('' + value, { min: 0 })
52}
53
54function isActorFollowersCountValid (value: string) {
55 return exists(value) && validator.isInt('' + value, { min: 0 })
56}
57
58function isActorDeleteActivityValid (activity: any) {
59 return isBaseActivityValid(activity, 'Delete')
60}
61
62function sanitizeAndCheckActorObject (actor: any) {
63 normalizeActor(actor)
64
51 return exists(actor) && 65 return exists(actor) &&
52 isActivityPubUrlValid(actor.id) && 66 isActivityPubUrlValid(actor.id) &&
53 isActorTypeValid(actor.type) && 67 isActorTypeValid(actor.type) &&
@@ -68,24 +82,6 @@ function isActorObjectValid (actor: any) {
68 (actor.type !== 'Group' || actor.attributedTo.length !== 0) 82 (actor.type !== 'Group' || actor.attributedTo.length !== 0)
69} 83}
70 84
71function isActorFollowingCountValid (value: string) {
72 return exists(value) && validator.isInt('' + value, { min: 0 })
73}
74
75function isActorFollowersCountValid (value: string) {
76 return exists(value) && validator.isInt('' + value, { min: 0 })
77}
78
79function isActorDeleteActivityValid (activity: any) {
80 return isBaseActivityValid(activity, 'Delete')
81}
82
83function sanitizeAndCheckActorObject (object: any) {
84 normalizeActor(object)
85
86 return isActorObjectValid(object)
87}
88
89function normalizeActor (actor: any) { 85function normalizeActor (actor: any) {
90 if (!actor) return 86 if (!actor) return
91 87
@@ -95,6 +91,8 @@ function normalizeActor (actor: any) {
95 actor.url = actor.url.href || actor.url.url 91 actor.url = actor.url.href || actor.url.url
96 } 92 }
97 93
94 if (!isDateValid(actor.published)) actor.published = undefined
95
98 if (actor.summary && typeof actor.summary === 'string') { 96 if (actor.summary && typeof actor.summary === 'string') {
99 actor.summary = peertubeTruncate(actor.summary, { length: CONSTRAINTS_FIELDS.USERS.DESCRIPTION.max }) 97 actor.summary = peertubeTruncate(actor.summary, { length: CONSTRAINTS_FIELDS.USERS.DESCRIPTION.max })
100 98
@@ -135,7 +133,6 @@ export {
135 isActorPublicKeyValid, 133 isActorPublicKeyValid,
136 isActorPreferredUsernameValid, 134 isActorPreferredUsernameValid,
137 isActorPrivateKeyValid, 135 isActorPrivateKeyValid,
138 isActorObjectValid,
139 isActorFollowingCountValid, 136 isActorFollowingCountValid,
140 isActorFollowersCountValid, 137 isActorFollowersCountValid,
141 isActorDeleteActivityValid, 138 isActorDeleteActivityValid,
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index d390fd95e..f807a1e58 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -24,7 +24,7 @@ import { CONFIG, registerConfigChangedHandler } from './config'
24 24
25// --------------------------------------------------------------------------- 25// ---------------------------------------------------------------------------
26 26
27const LAST_MIGRATION_VERSION = 640 27const LAST_MIGRATION_VERSION = 645
28 28
29// --------------------------------------------------------------------------- 29// ---------------------------------------------------------------------------
30 30
diff --git a/server/initializers/migrations/0645-actor-remote-creation-date.ts b/server/initializers/migrations/0645-actor-remote-creation-date.ts
new file mode 100644
index 000000000..38b3b881c
--- /dev/null
+++ b/server/initializers/migrations/0645-actor-remote-creation-date.ts
@@ -0,0 +1,26 @@
1import * as Sequelize from 'sequelize'
2
3async function up (utils: {
4 transaction: Sequelize.Transaction
5 queryInterface: Sequelize.QueryInterface
6 sequelize: Sequelize.Sequelize
7 db: any
8}): Promise<void> {
9 {
10 const data = {
11 type: Sequelize.DATE,
12 defaultValue: null,
13 allowNull: true
14 }
15 await utils.queryInterface.addColumn('actor', 'remoteCreatedAt', data)
16 }
17}
18
19function down (options) {
20 throw new Error('Not implemented.')
21}
22
23export {
24 up,
25 down
26}
diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts
index eec951d4e..34d53bd52 100644
--- a/server/lib/activitypub/actor.ts
+++ b/server/lib/activitypub/actor.ts
@@ -154,6 +154,8 @@ async function updateActorInstance (actorInstance: ActorModel, attributes: Activ
154 const followersCount = await fetchActorTotalItems(attributes.followers) 154 const followersCount = await fetchActorTotalItems(attributes.followers)
155 const followingCount = await fetchActorTotalItems(attributes.following) 155 const followingCount = await fetchActorTotalItems(attributes.following)
156 156
157 logger.info('coucou', { attributes })
158
157 actorInstance.type = attributes.type 159 actorInstance.type = attributes.type
158 actorInstance.preferredUsername = attributes.preferredUsername 160 actorInstance.preferredUsername = attributes.preferredUsername
159 actorInstance.url = attributes.id 161 actorInstance.url = attributes.id
@@ -165,6 +167,8 @@ async function updateActorInstance (actorInstance: ActorModel, attributes: Activ
165 actorInstance.followersUrl = attributes.followers 167 actorInstance.followersUrl = attributes.followers
166 actorInstance.followingUrl = attributes.following 168 actorInstance.followingUrl = attributes.following
167 169
170 if (attributes.published) actorInstance.remoteCreatedAt = new Date(attributes.published)
171
168 if (attributes.endpoints?.sharedInbox) { 172 if (attributes.endpoints?.sharedInbox) {
169 actorInstance.sharedInboxUrl = attributes.endpoints.sharedInbox 173 actorInstance.sharedInboxUrl = attributes.endpoints.sharedInbox
170 } 174 }
diff --git a/server/models/account/account.ts b/server/models/account/account.ts
index 312451abe..44be0fd3c 100644
--- a/server/models/account/account.ts
+++ b/server/models/account/account.ts
@@ -411,8 +411,6 @@ export class AccountModel extends Model {
411 id: this.id, 411 id: this.id,
412 displayName: this.getDisplayName(), 412 displayName: this.getDisplayName(),
413 description: this.description, 413 description: this.description,
414 createdAt: this.createdAt,
415 updatedAt: this.updatedAt,
416 userId: this.userId ? this.userId : undefined 414 userId: this.userId ? this.userId : undefined
417 } 415 }
418 416
diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts
index 19f3f7e04..396a52337 100644
--- a/server/models/activitypub/actor.ts
+++ b/server/models/activitypub/actor.ts
@@ -69,9 +69,7 @@ export const unusedActorAttributesForAPI = [
69 'outboxUrl', 69 'outboxUrl',
70 'sharedInboxUrl', 70 'sharedInboxUrl',
71 'followersUrl', 71 'followersUrl',
72 'followingUrl', 72 'followingUrl'
73 'createdAt',
74 'updatedAt'
75] 73]
76 74
77@DefaultScope(() => ({ 75@DefaultScope(() => ({
@@ -222,6 +220,10 @@ export class ActorModel extends Model {
222 @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.URL.max)) 220 @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.URL.max))
223 followingUrl: string 221 followingUrl: string
224 222
223 @AllowNull(true)
224 @Column
225 remoteCreatedAt: Date
226
225 @CreatedAt 227 @CreatedAt
226 createdAt: Date 228 createdAt: Date
227 229
@@ -555,7 +557,7 @@ export class ActorModel extends Model {
555 followingCount: this.followingCount, 557 followingCount: this.followingCount,
556 followersCount: this.followersCount, 558 followersCount: this.followersCount,
557 banner, 559 banner,
558 createdAt: this.createdAt, 560 createdAt: this.getCreatedAt(),
559 updatedAt: this.updatedAt 561 updatedAt: this.updatedAt
560 }) 562 })
561 } 563 }
@@ -608,6 +610,7 @@ export class ActorModel extends Model {
608 owner: this.url, 610 owner: this.url,
609 publicKeyPem: this.publicKey 611 publicKeyPem: this.publicKey
610 }, 612 },
613 published: this.getCreatedAt().toISOString(),
611 icon, 614 icon,
612 image 615 image
613 } 616 }
@@ -690,4 +693,8 @@ export class ActorModel extends Model {
690 693
691 return isOutdated(this, ACTIVITY_PUB.ACTOR_REFRESH_INTERVAL) 694 return isOutdated(this, ACTIVITY_PUB.ACTOR_REFRESH_INTERVAL)
692 } 695 }
696
697 getCreatedAt (this: MActorAPChannel | MActorAPAccount | MActorFormattable) {
698 return this.remoteCreatedAt || this.createdAt
699 }
693} 700}
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts
index b7ffbd3b1..b627595c9 100644
--- a/server/models/video/video-channel.ts
+++ b/server/models/video/video-channel.ts
@@ -653,8 +653,6 @@ ON "Account->Actor"."serverId" = "Account->Actor->Server"."id"`
653 description: this.description, 653 description: this.description,
654 support: this.support, 654 support: this.support,
655 isLocal: this.Actor.isOwned(), 655 isLocal: this.Actor.isOwned(),
656 createdAt: this.createdAt,
657 updatedAt: this.updatedAt,
658 ownerAccount: undefined, 656 ownerAccount: undefined,
659 videosCount, 657 videosCount,
660 viewsPerDay 658 viewsPerDay
diff --git a/server/tests/api/users/users-multiple-servers.ts b/server/tests/api/users/users-multiple-servers.ts
index dcd03879b..f60c66e4b 100644
--- a/server/tests/api/users/users-multiple-servers.ts
+++ b/server/tests/api/users/users-multiple-servers.ts
@@ -130,26 +130,32 @@ describe('Test users with multiple servers', function () {
130 }) 130 })
131 131
132 it('Should have updated my profile on other servers too', async function () { 132 it('Should have updated my profile on other servers too', async function () {
133 let createdAt: string | Date
134
133 for (const server of servers) { 135 for (const server of servers) {
134 const resAccounts = await getAccountsList(server.url, '-createdAt') 136 const resAccounts = await getAccountsList(server.url, '-createdAt')
135 137
136 const rootServer1List = resAccounts.body.data.find(a => a.name === 'root' && a.host === 'localhost:' + servers[0].port) as Account 138 const resList = resAccounts.body.data.find(a => a.name === 'root' && a.host === 'localhost:' + servers[0].port) as Account
137 expect(rootServer1List).not.to.be.undefined 139 expect(resList).not.to.be.undefined
140
141 const resAccount = await getAccount(server.url, resList.name + '@' + resList.host)
142 const account = resAccount.body as Account
143
144 if (!createdAt) createdAt = account.createdAt
138 145
139 const resAccount = await getAccount(server.url, rootServer1List.name + '@' + rootServer1List.host) 146 expect(account.name).to.equal('root')
140 const rootServer1Get = resAccount.body as Account 147 expect(account.host).to.equal('localhost:' + servers[0].port)
141 expect(rootServer1Get.name).to.equal('root') 148 expect(account.displayName).to.equal('my super display name')
142 expect(rootServer1Get.host).to.equal('localhost:' + servers[0].port) 149 expect(account.description).to.equal('my super description updated')
143 expect(rootServer1Get.displayName).to.equal('my super display name') 150 expect(createdAt).to.equal(account.createdAt)
144 expect(rootServer1Get.description).to.equal('my super description updated')
145 151
146 if (server.serverNumber === 1) { 152 if (server.serverNumber === 1) {
147 expect(rootServer1Get.userId).to.be.a('number') 153 expect(account.userId).to.be.a('number')
148 } else { 154 } else {
149 expect(rootServer1Get.userId).to.be.undefined 155 expect(account.userId).to.be.undefined
150 } 156 }
151 157
152 await testImage(server.url, 'avatar2-resized', rootServer1Get.avatar.path, '.png') 158 await testImage(server.url, 'avatar2-resized', account.avatar.path, '.png')
153 } 159 }
154 }) 160 })
155 161
diff --git a/server/types/models/account/actor.ts b/server/types/models/account/actor.ts
index 8f3f30074..0b620872e 100644
--- a/server/types/models/account/actor.ts
+++ b/server/types/models/account/actor.ts
@@ -150,7 +150,7 @@ export type MActorSummaryFormattable =
150 150
151export type MActorFormattable = 151export type MActorFormattable =
152 MActorSummaryFormattable & 152 MActorSummaryFormattable &
153 Pick<MActor, 'id' | 'followingCount' | 'followersCount' | 'createdAt' | 'updatedAt' | 'bannerId' | 'avatarId'> & 153 Pick<MActor, 'id' | 'followingCount' | 'followersCount' | 'createdAt' | 'updatedAt' | 'remoteCreatedAt' | 'bannerId' | 'avatarId'> &
154 Use<'Server', MServerHost & Partial<Pick<MServer, 'redundancyAllowed'>>> & 154 Use<'Server', MServerHost & Partial<Pick<MServer, 'redundancyAllowed'>>> &
155 UseOpt<'Banner', MActorImageFormattable> 155 UseOpt<'Banner', MActorImageFormattable>
156 156