fix(api): 🐛 Fix statuses not saving the user's applicationId

This commit is contained in:
Jesse Wierzbinski 2024-04-15 16:09:16 -10:00
parent a5a5d57c81
commit 06bcbbe451
No known key found for this signature in database
6 changed files with 62 additions and 7 deletions

View file

@ -40,7 +40,7 @@ import { LogLevel } from "~packages/log-manager";
import type { Note } from "~types/lysand/Object"; import type { Note } from "~types/lysand/Object";
import type { Attachment as APIAttachment } from "~types/mastodon/attachment"; import type { Attachment as APIAttachment } from "~types/mastodon/attachment";
import type { Status as APIStatus } from "~types/mastodon/status"; import type { Status as APIStatus } from "~types/mastodon/status";
import { applicationToAPI } from "./Application"; import { applicationToAPI, type Application } from "./Application";
import { import {
attachmentFromLysand, attachmentFromLysand,
attachmentToAPI, attachmentToAPI,
@ -923,6 +923,7 @@ export const createNewStatus = async (
media_attachments?: string[], media_attachments?: string[],
inReplyTo?: StatusWithRelations, inReplyTo?: StatusWithRelations,
quoting?: StatusWithRelations, quoting?: StatusWithRelations,
application?: Application,
): Promise<StatusWithRelations | null> => { ): Promise<StatusWithRelations | null> => {
const htmlContent = await contentToHtml(content, mentions); const htmlContent = await contentToHtml(content, mentions);
@ -956,6 +957,7 @@ export const createNewStatus = async (
uri: uri || null, uri: uri || null,
inReplyToPostId: inReplyTo?.id, inReplyToPostId: inReplyTo?.id,
quotingPostId: quoting?.id, quotingPostId: quoting?.id,
applicationId: application?.id ?? null,
updatedAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
}) })
.returning() .returning()

View file

@ -7,10 +7,12 @@ import { htmlToText } from "html-to-text";
import type * as Lysand from "lysand-types"; import type * as Lysand from "lysand-types";
import { db } from "~drizzle/db"; import { db } from "~drizzle/db";
import { import {
application,
emojiToUser, emojiToUser,
instance, instance,
notification, notification,
relationship, relationship,
token,
user, user,
} from "~drizzle/schema"; } from "~drizzle/schema";
import { LogLevel } from "~packages/log-manager"; import { LogLevel } from "~packages/log-manager";
@ -25,6 +27,8 @@ import {
import { objectToInboxRequest } from "./Federation"; import { objectToInboxRequest } from "./Federation";
import { addInstanceIfNotExists } from "./Instance"; import { addInstanceIfNotExists } from "./Instance";
import { createNewRelationship } from "./Relationship"; import { createNewRelationship } from "./Relationship";
import type { Token } from "./Token";
import type { Application } from "./Application";
export type User = InferSelectModel<typeof user> & { export type User = InferSelectModel<typeof user> & {
endpoints?: Partial<{ endpoints?: Partial<{
@ -123,6 +127,7 @@ export const userExtrasTemplate = (name: string) => ({
export interface AuthData { export interface AuthData {
user: UserWithRelations | null; user: UserWithRelations | null;
token: string; token: string;
application: Application | null;
} }
/** /**
@ -153,7 +158,10 @@ export const getFromRequest = async (req: Request): Promise<AuthData> => {
// Check auth token // Check auth token
const token = req.headers.get("Authorization")?.split(" ")[1] || ""; const token = req.headers.get("Authorization")?.split(" ")[1] || "";
return { user: await retrieveUserFromToken(token), token }; const { user, application } =
await retrieveUserAndApplicationFromToken(token);
return { user, token, application };
}; };
export const followRequestUser = async ( export const followRequestUser = async (
@ -652,9 +660,7 @@ export const retrieveUserFromToken = async (
): Promise<UserWithRelations | null> => { ): Promise<UserWithRelations | null> => {
if (!access_token) return null; if (!access_token) return null;
const token = await db.query.token.findFirst({ const token = await retrieveToken(access_token);
where: (tokens, { eq }) => eq(tokens.accessToken, access_token),
});
if (!token || !token.userId) return null; if (!token || !token.userId) return null;
@ -665,6 +671,47 @@ export const retrieveUserFromToken = async (
return user; return user;
}; };
export const retrieveUserAndApplicationFromToken = async (
access_token: string,
): Promise<{
user: UserWithRelations | null;
application: Application | null;
}> => {
if (!access_token) return { user: null, application: null };
const output = (
await db
.select({
token: token,
application: application,
})
.from(token)
.leftJoin(application, eq(token.applicationId, application.id))
.where(eq(token.accessToken, access_token))
.limit(1)
)[0];
if (!output?.token.userId) return { user: null, application: null };
const user = await findFirstUser({
where: (user, { eq }) => eq(user.id, output.token.userId ?? ""),
});
return { user, application: output.application ?? null };
};
export const retrieveToken = async (
access_token: string,
): Promise<Token | null> => {
if (!access_token) return null;
return (
(await db.query.token.findFirst({
where: (tokens, { eq }) => eq(tokens.accessToken, access_token),
})) ?? null
);
};
/** /**
* Gets the relationship to another user. * Gets the relationship to another user.
* @param other The other user to get the relationship to. * @param other The other user to get the relationship to.

View file

@ -6,6 +6,7 @@ import { LogLevel, type LogManager, type MultiLogManager } from "log-manager";
import { RequestParser } from "request-parser"; import { RequestParser } from "request-parser";
import type { ZodType, z } from "zod"; import type { ZodType, z } from "zod";
import { fromZodError } from "zod-validation-error"; import { fromZodError } from "zod-validation-error";
import type { Application } from "~database/entities/Application";
import { import {
type AuthData, type AuthData,
type UserWithRelations, type UserWithRelations,
@ -31,6 +32,7 @@ export type RouteHandler<
token: RouteMeta["auth"]["required"] extends true token: RouteMeta["auth"]["required"] extends true
? string ? string
: string | null; : string | null;
application: Application | null;
}; };
parsedRequest: z.infer<ZodSchema>; parsedRequest: z.infer<ZodSchema>;
configManager: { configManager: {
@ -140,6 +142,7 @@ export const processRoute = async (
auth: { auth: {
token: auth?.token ?? null, token: auth?.token ?? null,
user: auth?.user ?? null, user: auth?.user ?? null,
application: auth?.application ?? null,
}, },
parsedRequest: parsingResult parsedRequest: parsingResult
? (parsingResult.data as z.infer<typeof route.schema>) ? (parsingResult.data as z.infer<typeof route.schema>)

View file

@ -35,7 +35,7 @@ export default apiRoute<typeof meta, typeof schema>(
return errorResponse("Invalid ID, must be of type UUIDv7", 404); return errorResponse("Invalid ID, must be of type UUIDv7", 404);
} }
const { user } = extraData.auth; const { user, application } = extraData.auth;
if (!user) return errorResponse("Unauthorized", 401); if (!user) return errorResponse("Unauthorized", 401);
@ -70,6 +70,7 @@ export default apiRoute<typeof meta, typeof schema>(
visibility, visibility,
sensitive: false, sensitive: false,
updatedAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
applicationId: application?.id ?? null,
}) })
.returning() .returning()
)[0]; )[0];

View file

@ -66,7 +66,7 @@ export const schema = z.object({
*/ */
export default apiRoute<typeof meta, typeof schema>( export default apiRoute<typeof meta, typeof schema>(
async (req, matchedRoute, extraData) => { async (req, matchedRoute, extraData) => {
const { user } = extraData.auth; const { user, application } = extraData.auth;
if (!user) return errorResponse("Unauthorized", 401); if (!user) return errorResponse("Unauthorized", 401);
@ -187,6 +187,7 @@ export default apiRoute<typeof meta, typeof schema>(
media_ids, media_ids,
replyStatus ?? undefined, replyStatus ?? undefined,
quote ?? undefined, quote ?? undefined,
application ?? undefined,
); );
if (!newStatus) { if (!newStatus) {

View file

@ -95,6 +95,7 @@ export const getTestStatuses = async (
sensitive: false, sensitive: false,
updatedAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
visibility: "public", visibility: "public",
applicationId: null,
...partial, ...partial,
}) })
.returning() .returning()