import validator from 'validator'
import { CONSTRAINTS_FIELDS } from '../../../initializers/constants'
-import { exists, isArray } from '../misc'
+import { exists, isArray, isDateValid } from '../misc'
import { isActivityPubUrlValid, isBaseActivityValid, setValidAttributedTo } from './misc'
import { isHostValid } from '../servers'
import { peertubeTruncate } from '@server/helpers/core-utils'
validator.isLength(privateKey, CONSTRAINTS_FIELDS.ACTORS.PRIVATE_KEY)
}
-function isActorObjectValid (actor: any) {
+function isActorFollowingCountValid (value: string) {
+ return exists(value) && validator.isInt('' + value, { min: 0 })
+}
+
+function isActorFollowersCountValid (value: string) {
+ return exists(value) && validator.isInt('' + value, { min: 0 })
+}
+
+function isActorDeleteActivityValid (activity: any) {
+ return isBaseActivityValid(activity, 'Delete')
+}
+
+function sanitizeAndCheckActorObject (actor: any) {
+ normalizeActor(actor)
+
return exists(actor) &&
isActivityPubUrlValid(actor.id) &&
isActorTypeValid(actor.type) &&
(actor.type !== 'Group' || actor.attributedTo.length !== 0)
}
-function isActorFollowingCountValid (value: string) {
- return exists(value) && validator.isInt('' + value, { min: 0 })
-}
-
-function isActorFollowersCountValid (value: string) {
- return exists(value) && validator.isInt('' + value, { min: 0 })
-}
-
-function isActorDeleteActivityValid (activity: any) {
- return isBaseActivityValid(activity, 'Delete')
-}
-
-function sanitizeAndCheckActorObject (object: any) {
- normalizeActor(object)
-
- return isActorObjectValid(object)
-}
-
function normalizeActor (actor: any) {
if (!actor) return
actor.url = actor.url.href || actor.url.url
}
+ if (!isDateValid(actor.published)) actor.published = undefined
+
if (actor.summary && typeof actor.summary === 'string') {
actor.summary = peertubeTruncate(actor.summary, { length: CONSTRAINTS_FIELDS.USERS.DESCRIPTION.max })
isActorPublicKeyValid,
isActorPreferredUsernameValid,
isActorPrivateKeyValid,
- isActorObjectValid,
isActorFollowingCountValid,
isActorFollowersCountValid,
isActorDeleteActivityValid,
// ---------------------------------------------------------------------------
-const LAST_MIGRATION_VERSION = 640
+const LAST_MIGRATION_VERSION = 645
// ---------------------------------------------------------------------------
--- /dev/null
+import * as Sequelize from 'sequelize'
+
+async function up (utils: {
+ transaction: Sequelize.Transaction
+ queryInterface: Sequelize.QueryInterface
+ sequelize: Sequelize.Sequelize
+ db: any
+}): Promise<void> {
+ {
+ const data = {
+ type: Sequelize.DATE,
+ defaultValue: null,
+ allowNull: true
+ }
+ await utils.queryInterface.addColumn('actor', 'remoteCreatedAt', data)
+ }
+}
+
+function down (options) {
+ throw new Error('Not implemented.')
+}
+
+export {
+ up,
+ down
+}
const followersCount = await fetchActorTotalItems(attributes.followers)
const followingCount = await fetchActorTotalItems(attributes.following)
+ logger.info('coucou', { attributes })
+
actorInstance.type = attributes.type
actorInstance.preferredUsername = attributes.preferredUsername
actorInstance.url = attributes.id
actorInstance.followersUrl = attributes.followers
actorInstance.followingUrl = attributes.following
+ if (attributes.published) actorInstance.remoteCreatedAt = new Date(attributes.published)
+
if (attributes.endpoints?.sharedInbox) {
actorInstance.sharedInboxUrl = attributes.endpoints.sharedInbox
}
id: this.id,
displayName: this.getDisplayName(),
description: this.description,
- createdAt: this.createdAt,
- updatedAt: this.updatedAt,
userId: this.userId ? this.userId : undefined
}
'outboxUrl',
'sharedInboxUrl',
'followersUrl',
- 'followingUrl',
- 'createdAt',
- 'updatedAt'
+ 'followingUrl'
]
@DefaultScope(() => ({
@Column(DataType.STRING(CONSTRAINTS_FIELDS.ACTORS.URL.max))
followingUrl: string
+ @AllowNull(true)
+ @Column
+ remoteCreatedAt: Date
+
@CreatedAt
createdAt: Date
followingCount: this.followingCount,
followersCount: this.followersCount,
banner,
- createdAt: this.createdAt,
+ createdAt: this.getCreatedAt(),
updatedAt: this.updatedAt
})
}
owner: this.url,
publicKeyPem: this.publicKey
},
+ published: this.getCreatedAt().toISOString(),
icon,
image
}
return isOutdated(this, ACTIVITY_PUB.ACTOR_REFRESH_INTERVAL)
}
+
+ getCreatedAt (this: MActorAPChannel | MActorAPAccount | MActorFormattable) {
+ return this.remoteCreatedAt || this.createdAt
+ }
}
description: this.description,
support: this.support,
isLocal: this.Actor.isOwned(),
- createdAt: this.createdAt,
- updatedAt: this.updatedAt,
ownerAccount: undefined,
videosCount,
viewsPerDay
})
it('Should have updated my profile on other servers too', async function () {
+ let createdAt: string | Date
+
for (const server of servers) {
const resAccounts = await getAccountsList(server.url, '-createdAt')
- const rootServer1List = resAccounts.body.data.find(a => a.name === 'root' && a.host === 'localhost:' + servers[0].port) as Account
- expect(rootServer1List).not.to.be.undefined
+ const resList = resAccounts.body.data.find(a => a.name === 'root' && a.host === 'localhost:' + servers[0].port) as Account
+ expect(resList).not.to.be.undefined
+
+ const resAccount = await getAccount(server.url, resList.name + '@' + resList.host)
+ const account = resAccount.body as Account
+
+ if (!createdAt) createdAt = account.createdAt
- const resAccount = await getAccount(server.url, rootServer1List.name + '@' + rootServer1List.host)
- const rootServer1Get = resAccount.body as Account
- expect(rootServer1Get.name).to.equal('root')
- expect(rootServer1Get.host).to.equal('localhost:' + servers[0].port)
- expect(rootServer1Get.displayName).to.equal('my super display name')
- expect(rootServer1Get.description).to.equal('my super description updated')
+ expect(account.name).to.equal('root')
+ expect(account.host).to.equal('localhost:' + servers[0].port)
+ expect(account.displayName).to.equal('my super display name')
+ expect(account.description).to.equal('my super description updated')
+ expect(createdAt).to.equal(account.createdAt)
if (server.serverNumber === 1) {
- expect(rootServer1Get.userId).to.be.a('number')
+ expect(account.userId).to.be.a('number')
} else {
- expect(rootServer1Get.userId).to.be.undefined
+ expect(account.userId).to.be.undefined
}
- await testImage(server.url, 'avatar2-resized', rootServer1Get.avatar.path, '.png')
+ await testImage(server.url, 'avatar2-resized', account.avatar.path, '.png')
}
})
export type MActorFormattable =
MActorSummaryFormattable &
- Pick<MActor, 'id' | 'followingCount' | 'followersCount' | 'createdAt' | 'updatedAt' | 'bannerId' | 'avatarId'> &
+ Pick<MActor, 'id' | 'followingCount' | 'followersCount' | 'createdAt' | 'updatedAt' | 'remoteCreatedAt' | 'bannerId' | 'avatarId'> &
Use<'Server', MServerHost & Partial<Pick<MServer, 'redundancyAllowed'>>> &
UseOpt<'Banner', MActorImageFormattable>
icon?: ActivityIconObject
image?: ActivityIconObject
+
+ published?: string
}