Skip to content

Commit

Permalink
Merge branch 'develop' into blurhash-transition
Browse files Browse the repository at this point in the history
  • Loading branch information
tamaina committed Apr 12, 2023
2 parents 20739d2 + 6ea057f commit 704e0c5
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 28 deletions.
4 changes: 2 additions & 2 deletions packages/backend/src/core/CustomEmojiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ export class CustomEmojiService {
toRedisConverter: (value) => JSON.stringify(Array.from(value.values())),
fromRedisConverter: (value) => {
if (!Array.isArray(JSON.parse(value))) return undefined; // 古いバージョンの壊れたキャッシュが残っていることがある(そのうち消す)
return new Map(JSON.parse(value).map((x) => [x.name, {
return new Map(JSON.parse(value).map((x: Emoji) => [x.name, {
...x,
updatedAt: new Date(x.updatedAt),
updatedAt: x.updatedAt && new Date(x.updatedAt),
}]));
},
});
Expand Down
6 changes: 5 additions & 1 deletion packages/backend/src/core/UserFollowingService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { bindThis } from '@/decorators.js';
import { UserBlockingService } from '@/core/UserBlockingService.js';
import { MetaService } from '@/core/MetaService.js';
import { CacheService } from '@/core/CacheService.js';
import type { Config } from '@/config.js';
import Logger from '../logger.js';

const logger = new Logger('following/create');
Expand All @@ -44,6 +45,9 @@ export class UserFollowingService implements OnModuleInit {
constructor(
private moduleRef: ModuleRef,

@Inject(DI.config)
private config: Config,

@Inject(DI.usersRepository)
private usersRepository: UsersRepository,

Expand Down Expand Up @@ -411,7 +415,7 @@ export class UserFollowingService implements OnModuleInit {
}

if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
const content = this.apRendererService.addContext(this.apRendererService.renderFollow(follower, followee));
const content = this.apRendererService.addContext(this.apRendererService.renderFollow(follower, followee, requestId ?? `${this.config.url}/follows/${followRequest.id}`));
this.queueService.deliver(follower, content, followee.inbox, false);
}
}
Expand Down
88 changes: 63 additions & 25 deletions packages/backend/src/server/ActivityPubServerService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Brackets, In, IsNull, LessThan, Not } from 'typeorm';
import accepts from 'accepts';
import vary from 'vary';
import { DI } from '@/di-symbols.js';
import type { FollowingsRepository, NotesRepository, EmojisRepository, NoteReactionsRepository, UserProfilesRepository, UserNotePiningsRepository, UsersRepository } from '@/models/index.js';
import type { FollowingsRepository, NotesRepository, EmojisRepository, NoteReactionsRepository, UserProfilesRepository, UserNotePiningsRepository, UsersRepository, FollowRequestsRepository } from '@/models/index.js';
import * as url from '@/misc/prelude/url.js';
import type { Config } from '@/config.js';
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
Expand Down Expand Up @@ -54,6 +54,9 @@ export class ActivityPubServerService {
@Inject(DI.followingsRepository)
private followingsRepository: FollowingsRepository,

@Inject(DI.followRequestsRepository)
private followRequestsRepository: FollowRequestsRepository,

private utilityService: UtilityService,
private userEntityService: UserEntityService,
private apRendererService: ApRendererService,
Expand Down Expand Up @@ -205,22 +208,22 @@ export class ActivityPubServerService {
reply.code(400);
return;
}

const page = request.query.page === 'true';

const user = await this.usersRepository.findOneBy({
id: userId,
host: IsNull(),
});

if (user == null) {
reply.code(404);
return;
}

//#region Check ff visibility
const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id });

if (profile.ffVisibility === 'private') {
reply.code(403);
reply.header('Cache-Control', 'public, max-age=30');
Expand All @@ -231,31 +234,31 @@ export class ActivityPubServerService {
return;
}
//#endregion

const limit = 10;
const partOf = `${this.config.url}/users/${userId}/following`;

if (page) {
const query = {
followerId: user.id,
} as FindOptionsWhere<Following>;

// カーソルが指定されている場合
if (cursor) {
query.id = LessThan(cursor);
}

// Get followings
const followings = await this.followingsRepository.find({
where: query,
take: limit + 1,
order: { id: -1 },
});

// 「次のページ」があるかどうか
const inStock = followings.length === limit + 1;
if (inStock) followings.pop();

const renderedFollowees = await Promise.all(followings.map(following => this.apRendererService.renderFollowUser(following.followeeId)));
const rendered = this.apRendererService.renderOrderedCollectionPage(
`${partOf}?${url.query({
Expand All @@ -269,7 +272,7 @@ export class ActivityPubServerService {
cursor: followings[followings.length - 1].id,
})}` : undefined,
);

this.setResponseType(request, reply);
return (this.apRendererService.addContext(rendered));
} else {
Expand Down Expand Up @@ -330,33 +333,33 @@ export class ActivityPubServerService {
reply.code(400);
return;
}

const untilId = request.query.until_id;
if (untilId != null && typeof untilId !== 'string') {
reply.code(400);
return;
}

const page = request.query.page === 'true';

if (countIf(x => x != null, [sinceId, untilId]) > 1) {
reply.code(400);
return;
}

const user = await this.usersRepository.findOneBy({
id: userId,
host: IsNull(),
});

if (user == null) {
reply.code(404);
return;
}

const limit = 20;
const partOf = `${this.config.url}/users/${userId}/outbox`;

if (page) {
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), sinceId, untilId)
.andWhere('note.userId = :userId', { userId: user.id })
Expand All @@ -365,11 +368,11 @@ export class ActivityPubServerService {
.orWhere('note.visibility = \'home\'');
}))
.andWhere('note.localOnly = FALSE');

const notes = await query.take(limit).getMany();

if (sinceId) notes.reverse();

const activities = await Promise.all(notes.map(note => this.packActivity(note)));
const rendered = this.apRendererService.renderOrderedCollectionPage(
`${partOf}?${url.query({
Expand All @@ -387,7 +390,7 @@ export class ActivityPubServerService {
until_id: notes[notes.length - 1].id,
})}` : undefined,
);

this.setResponseType(request, reply);
return (this.apRendererService.addContext(rendered));
} else {
Expand Down Expand Up @@ -457,7 +460,7 @@ export class ActivityPubServerService {
// note
fastify.get<{ Params: { note: string; } }>('/notes/:note', { constraints: { apOrHtml: 'ap' } }, async (request, reply) => {
vary(reply.raw, 'Accept');

const note = await this.notesRepository.findOneBy({
id: request.params.note,
visibility: In(['public', 'home']),
Expand Down Expand Up @@ -639,6 +642,41 @@ export class ActivityPubServerService {
return (this.apRendererService.addContext(this.apRendererService.renderFollow(follower, followee)));
});

// follow
fastify.get<{ Params: { followRequestId: string ; } }>('/follows/:followRequestId', async (request, reply) => {
// This may be used before the follow is completed, so we do not
// check if the following exists and only check if the follow request exists.

const followRequest = await this.followRequestsRepository.findOneBy({
id: request.params.followRequestId,
});

if (followRequest == null) {
reply.code(404);
return;
}

const [follower, followee] = await Promise.all([
this.usersRepository.findOneBy({
id: followRequest.followerId,
host: IsNull(),
}),
this.usersRepository.findOneBy({
id: followRequest.followeeId,
host: Not(IsNull()),
}),
]);

if (follower == null || followee == null) {
reply.code(404);
return;
}

reply.header('Cache-Control', 'public, max-age=180');
this.setResponseType(request, reply);
return (this.apRendererService.addContext(this.apRendererService.renderFollow(follower, followee)));
});

done();
}
}

1 comment on commit 704e0c5

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chromatic will skip testing but you may still have to review the changes on Chromatic.

Please sign in to comment.