mirror of
https://iceshrimp.dev/limepotato/jormungandr-bite.git
synced 2025-02-04 13:50:20 -07:00
[mastodon-client] PATCH /v1/accounts/update_credentials
This commit is contained in:
parent
75f6732928
commit
01b5af2686
4 changed files with 64 additions and 16 deletions
|
@ -9,6 +9,7 @@ import { NoteConverter } from "@/server/api/mastodon/converters/note.js";
|
||||||
import { UserHelpers } from "@/server/api/mastodon/helpers/user.js";
|
import { UserHelpers } from "@/server/api/mastodon/helpers/user.js";
|
||||||
import { PaginationHelpers } from "@/server/api/mastodon/helpers/pagination.js";
|
import { PaginationHelpers } from "@/server/api/mastodon/helpers/pagination.js";
|
||||||
import { ListHelpers } from "@/server/api/mastodon/helpers/list.js";
|
import { ListHelpers } from "@/server/api/mastodon/helpers/list.js";
|
||||||
|
import { Files } from "formidable";
|
||||||
|
|
||||||
export function setupEndpointsAccount(router: Router): void {
|
export function setupEndpointsAccount(router: Router): void {
|
||||||
router.get("/v1/accounts/verify_credentials", async (ctx) => {
|
router.get("/v1/accounts/verify_credentials", async (ctx) => {
|
||||||
|
@ -40,7 +41,8 @@ export function setupEndpointsAccount(router: Router): void {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const acct = await UserHelpers.updateCredentials(user, (ctx.request as any).body as any);
|
const files = (ctx.request as any).files as Files | undefined;
|
||||||
|
const acct = await UserHelpers.updateCredentials(user, (ctx.request as any).body as any, files);
|
||||||
ctx.body = convertAccountId(acct)
|
ctx.body = convertAccountId(acct)
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
|
|
|
@ -75,8 +75,8 @@ export function setupEndpointsMedia(router: Router): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
//FIXME: why do we have to cast this to any first?
|
//FIXME: why do we have to cast this to any first?
|
||||||
const files = (ctx.request as any).files as Files;
|
const files = (ctx.request as any).files as Files | undefined;
|
||||||
const file = toSingleLast(files['file']);
|
const file = toSingleLast(files?.file);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
ctx.body = {error: "No image"};
|
ctx.body = {error: "No image"};
|
||||||
ctx.status = 400;
|
ctx.status = 400;
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { File } from "formidable";
|
||||||
|
|
||||||
export class MediaHelpers {
|
export class MediaHelpers {
|
||||||
public static async uploadMedia(user: ILocalUser, file: File, body: any): Promise<Packed<"DriveFile">> {
|
public static async uploadMedia(user: ILocalUser, file: File, body: any): Promise<Packed<"DriveFile">> {
|
||||||
return await addFile({
|
return addFile({
|
||||||
user: user,
|
user: user,
|
||||||
path: file.filepath,
|
path: file.filepath,
|
||||||
name: file.originalFilename !== null && file.originalFilename !== 'file' ? file.originalFilename : undefined,
|
name: file.originalFilename !== null && file.originalFilename !== 'file' ? file.originalFilename : undefined,
|
||||||
|
@ -17,6 +17,15 @@ export class MediaHelpers {
|
||||||
.then(p => DriveFiles.pack(p));
|
.then(p => DriveFiles.pack(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async uploadMediaBasic(user: ILocalUser, file: File): Promise<DriveFile> {
|
||||||
|
return addFile({
|
||||||
|
user: user,
|
||||||
|
path: file.filepath,
|
||||||
|
name: file.originalFilename !== null && file.originalFilename !== 'file' ? file.originalFilename : undefined,
|
||||||
|
sensitive: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
public static async updateMedia(user: ILocalUser, file: DriveFile, body: any): Promise<Packed<"DriveFile">> {
|
public static async updateMedia(user: ILocalUser, file: DriveFile, body: any): Promise<Packed<"DriveFile">> {
|
||||||
await DriveFiles.update(file.id, {
|
await DriveFiles.update(file.id, {
|
||||||
comment: body?.description ?? undefined
|
comment: body?.description ?? undefined
|
||||||
|
|
|
@ -34,6 +34,12 @@ import acceptFollowRequest from "@/services/following/requests/accept.js";
|
||||||
import { rejectFollowRequest } from "@/services/following/reject.js";
|
import { rejectFollowRequest } from "@/services/following/reject.js";
|
||||||
import { Brackets, IsNull } from "typeorm";
|
import { Brackets, IsNull } from "typeorm";
|
||||||
import { IceshrimpVisibility, VisibilityConverter } from "@/server/api/mastodon/converters/visibility.js";
|
import { IceshrimpVisibility, VisibilityConverter } from "@/server/api/mastodon/converters/visibility.js";
|
||||||
|
import { Files } from "formidable";
|
||||||
|
import { toSingleLast } from "@/prelude/array.js";
|
||||||
|
import { MediaHelpers } from "@/server/api/mastodon/helpers/media.js";
|
||||||
|
import { FileConverter } from "@/server/api/mastodon/converters/file.js";
|
||||||
|
import { UserProfile } from "@/models/entities/user-profile.js";
|
||||||
|
import { verifyLink } from "@/services/fetch-rel-me.js";
|
||||||
|
|
||||||
export type AccountCache = {
|
export type AccountCache = {
|
||||||
locks: AsyncLock;
|
locks: AsyncLock;
|
||||||
|
@ -53,6 +59,7 @@ export type updateCredsData = {
|
||||||
locked: boolean;
|
locked: boolean;
|
||||||
bot: boolean;
|
bot: boolean;
|
||||||
discoverable: boolean;
|
discoverable: boolean;
|
||||||
|
fields_attributes?: { name: string, value: string }[];
|
||||||
}
|
}
|
||||||
|
|
||||||
type RelationshipType = 'followers' | 'following';
|
type RelationshipType = 'followers' | 'following';
|
||||||
|
@ -145,17 +152,41 @@ export class UserHelpers {
|
||||||
return this.getUserRelationshipTo(target.id, localUser.id);
|
return this.getUserRelationshipTo(target.id, localUser.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async updateCredentials(user: ILocalUser, formData: updateCredsData): Promise<MastodonEntity.Account> {
|
public static async updateCredentials(user: ILocalUser, formData: updateCredsData, files: Files | undefined): Promise<MastodonEntity.Account> {
|
||||||
//FIXME: Actually implement this
|
const updates: Partial<User> = {};
|
||||||
//FIXME: handle multipart avatar & header image upload
|
const profileUpdates: Partial<UserProfile> = {};
|
||||||
//FIXME: handle field attributes
|
|
||||||
const obj: any = {};
|
|
||||||
|
|
||||||
if (formData.display_name) obj.name = formData.display_name;
|
const avatar = toSingleLast(files?.avatar);
|
||||||
if (formData.note) obj.description = formData.note;
|
const header = toSingleLast(files?.header);
|
||||||
if (formData.locked) obj.isLocked = formData.locked;
|
|
||||||
if (formData.bot) obj.isBot = formData.bot;
|
if (avatar) {
|
||||||
if (formData.discoverable) obj.isExplorable = formData.discoverable;
|
const file = await MediaHelpers.uploadMediaBasic(user, avatar);
|
||||||
|
updates.avatarId = file.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header) {
|
||||||
|
const file = await MediaHelpers.uploadMediaBasic(user, header);
|
||||||
|
updates.bannerId = file.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formData.fields_attributes) {
|
||||||
|
profileUpdates.fields = await Promise.all(formData.fields_attributes.map(async field => {
|
||||||
|
const verified = field.value.startsWith("http") ? await verifyLink(field.value, user.username) : undefined;
|
||||||
|
return {
|
||||||
|
...field,
|
||||||
|
verified
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formData.display_name) updates.name = formData.display_name;
|
||||||
|
if (formData.note) profileUpdates.description = formData.note;
|
||||||
|
if (formData.locked) updates.isLocked = formData.locked;
|
||||||
|
if (formData.bot) updates.isBot = formData.bot;
|
||||||
|
if (formData.discoverable) updates.isExplorable = formData.discoverable;
|
||||||
|
|
||||||
|
if (Object.keys(updates).length > 0) await Users.update(user.id, updates);
|
||||||
|
if (Object.keys(profileUpdates).length > 0) await UserProfiles.update({ userId: user.id }, profileUpdates);
|
||||||
|
|
||||||
return this.verifyCredentials(user);
|
return this.verifyCredentials(user);
|
||||||
}
|
}
|
||||||
|
@ -164,10 +195,16 @@ export class UserHelpers {
|
||||||
const acct = UserConverter.encode(user);
|
const acct = UserConverter.encode(user);
|
||||||
const profile = UserProfiles.findOneByOrFail({userId: user.id});
|
const profile = UserProfiles.findOneByOrFail({userId: user.id});
|
||||||
const privacy = this.getDefaultNoteVisibility(user);
|
const privacy = this.getDefaultNoteVisibility(user);
|
||||||
|
const fields = profile.then(profile => profile.fields.map(field => {
|
||||||
|
return {
|
||||||
|
name: field.name,
|
||||||
|
value: field.value
|
||||||
|
} as MastodonEntity.Field;
|
||||||
|
}));
|
||||||
return acct.then(acct => {
|
return acct.then(acct => {
|
||||||
const source = {
|
const source = {
|
||||||
note: acct.note,
|
note: profile.then(profile => profile.description ?? ''),
|
||||||
fields: acct.fields,
|
fields: fields,
|
||||||
privacy: privacy.then(p => VisibilityConverter.encode(p)),
|
privacy: privacy.then(p => VisibilityConverter.encode(p)),
|
||||||
sensitive: profile.then(p => p.alwaysMarkNsfw),
|
sensitive: profile.then(p => p.alwaysMarkNsfw),
|
||||||
language: profile.then(p => p.lang ?? ''),
|
language: profile.then(p => p.lang ?? ''),
|
||||||
|
|
Loading…
Reference in a new issue