fix endpoints
This commit is contained in:
parent
754347d4f0
commit
2ed21940ab
5 changed files with 187 additions and 53 deletions
|
@ -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[];
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue