fix endpoints

This commit is contained in:
Namekuji 2023-08-19 07:34:36 -04:00
parent 754347d4f0
commit 2ed21940ab
No known key found for this signature in database
GPG key ID: 1D62332C07FBA532
5 changed files with 187 additions and 53 deletions

View file

@ -10,6 +10,16 @@ import renderEmoji from "./emoji.js";
import renderMention from "./mention.js";
import renderHashtag from "./hashtag.js";
import renderDocument from "./document.js";
import {
type ScyllaPoll,
type ScyllaNote,
parseScyllaNote,
prepared,
scyllaClient,
parseScyllaPollVote,
} from "@/db/scylla.js";
import { userByIdCache } from "@/services/user-cache.js";
import { EmojiCache } from "@/misc/populate-emojis.js";
export default async function renderNote(
note: Note,
@ -25,15 +35,32 @@ export default async function renderNote(
};
let inReplyTo;
let inReplyToNote: Note | null;
let inReplyToNote: Note | null = null;
if (note.replyId) {
if (scyllaClient) {
const result = await scyllaClient.execute(
prepared.note.select.byId,
[note.replyId],
{ prepare: true },
);
if (result.rowLength > 0) {
inReplyToNote = parseScyllaNote(result.first());
}
} else {
inReplyToNote = await Notes.findOneBy({ id: note.replyId });
}
if (inReplyToNote != null) {
const inReplyToUser = await Users.findOneBy({ id: inReplyToNote.userId });
if (inReplyToNote) {
const inReplyToUser = await userByIdCache.fetchMaybe(
inReplyToNote.userId,
() =>
Users.findOneBy({ id: (inReplyToNote as Note).userId }).then(
(user) => user ?? undefined,
),
);
if (inReplyToUser != null) {
if (!inReplyToUser) {
if (inReplyToNote.uri) {
inReplyTo = inReplyToNote.uri;
} else {
@ -52,7 +79,19 @@ export default async function renderNote(
let quote;
if (note.renoteId) {
const renote = await Notes.findOneBy({ id: note.renoteId });
let renote: Note | null = null;
if (scyllaClient) {
const result = await scyllaClient.execute(
prepared.note.select.byId,
[note.renoteId],
{ prepare: true },
);
if (result.rowLength > 0) {
renote = parseScyllaNote(result.first());
}
} else {
renote = await Notes.findOneBy({ id: note.renoteId });
}
if (renote) {
quote = renote.uri ? renote.uri : `${config.url}/notes/${renote.id}`;
@ -94,11 +133,15 @@ export default async function renderNote(
const files = await getPromisedFiles(note.fileIds);
const text = note.text ?? "";
let poll: Poll | null = null;
let poll: Poll | ScyllaPoll | null = null;
if (note.hasPoll) {
if (scyllaClient) {
poll = (note as ScyllaNote).poll;
} else {
poll = await Polls.findOneBy({ noteId: note.id });
}
}
let apText = text;
@ -119,6 +162,39 @@ export default async function renderNote(
const tag = [...hashtagTags, ...mentionTags, ...apemojis];
let choices: {
type: "Note";
name: string;
replies: { type: "Collection"; totalItems: number };
}[] = [];
if (poll) {
if (scyllaClient) {
const votes = await scyllaClient
.execute(prepared.poll.select, [note.id], { prepare: true })
.then((result) => result.rows.map(parseScyllaPollVote));
choices = Object.entries((poll as ScyllaPoll).choices).map(
([index, text]) => ({
type: "Note",
name: text,
replies: {
type: "Collection",
totalItems: votes.filter((vote) => vote.choice.has(parseInt(index)))
.length,
},
}),
);
} else {
choices = (poll as Poll).choices.map((text, i) => ({
type: "Note",
name: text,
replies: {
type: "Collection",
totalItems: (poll as Poll).votes[i],
},
}));
}
}
const asPoll = poll
? {
type: "Question",
@ -129,14 +205,7 @@ export default async function renderNote(
),
[poll.expiresAt && poll.expiresAt < new Date() ? "closed" : "endTime"]:
poll.expiresAt,
[poll.multiple ? "anyOf" : "oneOf"]: poll.choices.map((text, i) => ({
type: "Note",
name: text,
replies: {
type: "Collection",
totalItems: poll!.votes[i],
},
})),
[poll.multiple ? "anyOf" : "oneOf"]: choices,
}
: {};
@ -177,12 +246,14 @@ export async function getEmojis(names: string[]): Promise<Emoji[]> {
const emojis = await Promise.all(
names.map((name) =>
EmojiCache.fetch(`${name} null`, () =>
Emojis.findOneBy({
name,
host: IsNull(),
}),
),
),
);
return emojis.filter((emoji) => emoji != null) as Emoji[];
return emojis.filter((emoji) => !!emoji) as Emoji[];
}

View file

@ -1,6 +1,12 @@
import { Notes } from "@/models/index.js";
import define from "../define.js";
import { makePaginationQuery } from "../common/make-pagination-query.js";
import {
type ScyllaNote,
scyllaClient,
execPaginationQuery,
FeedType,
} from "@/db/scylla.js";
export const meta = {
tags: ["notes"],
@ -35,6 +41,43 @@ export const paramDef = {
} as const;
export default define(meta, paramDef, async (ps) => {
if (scyllaClient) {
const filter = async (notes: ScyllaNote[]) => {
let filtered = notes.filter((note) => !note.localOnly);
if (ps.reply === undefined) {
filtered = filtered.filter(
(note) => !!note.replyId === (ps.reply as boolean),
);
}
if (ps.renote === undefined) {
filtered = filtered.filter(
(note) => !!note.renoteId === (ps.renote as boolean),
);
}
if (ps.withFiles === undefined) {
filtered = filtered.filter((note) =>
ps.withFiles ? note.files.length > 0 : note.files.length === 0,
);
}
if (ps.poll === undefined) {
filtered = filtered.filter(
(note) => note.hasPoll === (ps.poll as boolean),
);
}
return filtered;
};
const foundNotes = (await execPaginationQuery(
ps.local ? "global" : "local",
ps,
{
note: filter,
},
)) as ScyllaNote[];
return await Notes.packMany(foundNotes);
}
const query = makePaginationQuery(
Notes.createQueryBuilder("note"),
ps.sinceId,

View file

@ -116,28 +116,6 @@ export default define(meta, paramDef, async (ps, user) => {
);
}
publishNoteStream(scyllaNote.id, "pollVoted", {
choice: ps.choice,
userId: user.id,
});
createNotification(scyllaNote.userId, "pollVote", {
notifierId: user.id,
noteId: scyllaNote.id,
choice: ps.choice,
});
NoteWatchings.findBy({
noteId: scyllaNote.id,
userId: Not(user.id),
}).then((watchers) => {
for (const watcher of watchers) {
createNotification(watcher.userId, "pollVote", {
notifierId: user.id,
noteId: scyllaNote.id,
choice: ps.choice,
});
}
});
return;
}

View file

@ -35,6 +35,9 @@ import { urlPreviewHandler } from "./url-preview.js";
import { manifestHandler } from "./manifest.js";
import packFeed from "./feed.js";
import { MINUTE, DAY } from "@/const.js";
import { parseScyllaNote, prepared, scyllaClient } from "@/db/scylla.js";
import { userByIdCache } from "@/services/user-cache.js";
import type { Note } from "@/models/entities/note.js";
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
@ -465,10 +468,25 @@ router.get("/users/:user", async (ctx) => {
// Note
router.get("/notes/:note", async (ctx, next) => {
const note = await Notes.findOneBy({
let note: Note | null = null;
if (scyllaClient) {
const result = await scyllaClient.execute(
prepared.note.select.byId,
[ctx.params.note],
{ prepare: true },
);
if (result.rowLength > 0) {
const candidate = parseScyllaNote(result.first());
if (["public", "home"].includes(candidate.visibility)) {
note = candidate;
}
}
} else {
note = await Notes.findOneBy({
id: ctx.params.note,
visibility: In(["public", "home"]),
});
}
try {
if (note) {
@ -483,7 +501,9 @@ router.get("/notes/:note", async (ctx, next) => {
note: _note,
profile,
avatarUrl: await Users.getAvatarUrl(
await Users.findOneByOrFail({ id: note.userId }),
await userByIdCache.fetch(note.userId, () =>
Users.findOneByOrFail({ id: (note as Note).userId }),
),
),
// TODO: Let locale changeable by instance setting
summary: getNoteSummary(_note),
@ -503,10 +523,25 @@ router.get("/notes/:note", async (ctx, next) => {
});
router.get("/posts/:note", async (ctx, next) => {
const note = await Notes.findOneBy({
let note: Note | null = null;
if (scyllaClient) {
const result = await scyllaClient.execute(
prepared.note.select.byId,
[ctx.params.note],
{ prepare: true },
);
if (result.rowLength > 0) {
const candidate = parseScyllaNote(result.first());
if (["public", "home"].includes(candidate.visibility)) {
note = candidate;
}
}
} else {
note = await Notes.findOneBy({
id: ctx.params.note,
visibility: In(["public", "home"]),
});
}
if (note) {
const _note = await Notes.pack(note);
@ -517,7 +552,9 @@ router.get("/posts/:note", async (ctx, next) => {
note: _note,
profile,
avatarUrl: await Users.getAvatarUrl(
await Users.findOneByOrFail({ id: note.userId }),
await userByIdCache.fetch(note.userId, () =>
Users.findOneByOrFail({ id: (note as Note).userId }),
),
),
// TODO: Let locale changeable by instance setting
summary: getNoteSummary(_note),

View file

@ -140,6 +140,11 @@ export async function createNotification(
// Fire "new notification" event if not yet read after two seconds
setTimeout(async () => {
if (scyllaClient) {
pushNotification(notifieeId, "notification", packed);
return;
}
const fresh = await Notifications.findOneBy({ id: notification.id });
if (!fresh) return;
// We execute this before, because the server side "read" check doesnt work well with push notifications, the app and service worker will decide themself