mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 16:38:19 +01:00
Add new user patch route
This commit is contained in:
parent
b96fe8116e
commit
0bdf559bdc
40
database/entities/Token.ts
Normal file
40
database/entities/Token.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import {
|
||||||
|
Entity,
|
||||||
|
BaseEntity,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
ManyToOne,
|
||||||
|
} from "typeorm";
|
||||||
|
import { User } from "./User";
|
||||||
|
import { Application } from "./Application";
|
||||||
|
|
||||||
|
export enum TokenType {
|
||||||
|
BEARER = "bearer",
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity({
|
||||||
|
name: "tokens",
|
||||||
|
})
|
||||||
|
export class Token extends BaseEntity {
|
||||||
|
@PrimaryGeneratedColumn("uuid")
|
||||||
|
id!: string;
|
||||||
|
|
||||||
|
@Column("varchar")
|
||||||
|
token_type!: TokenType;
|
||||||
|
|
||||||
|
@Column("varchar")
|
||||||
|
scope!: string;
|
||||||
|
|
||||||
|
@Column("varchar")
|
||||||
|
access_token!: string;
|
||||||
|
|
||||||
|
@CreateDateColumn()
|
||||||
|
created_at!: Date;
|
||||||
|
|
||||||
|
@ManyToOne(() => User, user => user.id)
|
||||||
|
user!: User;
|
||||||
|
|
||||||
|
@ManyToOne(() => Application, application => application.id)
|
||||||
|
application!: Application;
|
||||||
|
}
|
||||||
125
server/api/v1/accounts/update_credentials/index.ts
Normal file
125
server/api/v1/accounts/update_credentials/index.ts
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
import { getUserByToken } from "@auth";
|
||||||
|
import { getConfig } from "@config";
|
||||||
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Patches a user
|
||||||
|
*/
|
||||||
|
export default async (req: Request): Promise<Response> => {
|
||||||
|
// Check if request is a PATCH request
|
||||||
|
if (req.method !== "PATCH")
|
||||||
|
return errorResponse("This method requires a PATCH request", 405);
|
||||||
|
|
||||||
|
// Check auth token
|
||||||
|
const token = req.headers.get("Authorization")?.split(" ")[1] || null;
|
||||||
|
|
||||||
|
if (!token)
|
||||||
|
return errorResponse("This method requires an authenticated user", 422);
|
||||||
|
|
||||||
|
const user = await getUserByToken(token);
|
||||||
|
|
||||||
|
if (!user) return errorResponse("Unauthorized", 401);
|
||||||
|
|
||||||
|
const config = getConfig();
|
||||||
|
const body = await req.formData();
|
||||||
|
|
||||||
|
const display_name = body.get("display_name")?.toString() || null;
|
||||||
|
const note = body.get("note")?.toString() || null;
|
||||||
|
// Avatar is a file element
|
||||||
|
const avatar = (body.get("avatar") as File | null) || null;
|
||||||
|
const header = (body.get("header") as File | null) || null;
|
||||||
|
const locked = body.get("locked")?.toString() || null;
|
||||||
|
const bot = body.get("bot")?.toString() || null;
|
||||||
|
const discoverable = body.get("discoverable")?.toString() || null;
|
||||||
|
// TODO: Implement other options like field or source
|
||||||
|
// const source_privacy = body.get("source[privacy]")?.toString() || null;
|
||||||
|
// const source_sensitive = body.get("source[sensitive]")?.toString() || null;
|
||||||
|
// const source_language = body.get("source[language]")?.toString() || null;
|
||||||
|
|
||||||
|
if (display_name) {
|
||||||
|
// Check if within allowed display name lengths
|
||||||
|
if (
|
||||||
|
display_name.length < 3 ||
|
||||||
|
display_name.length > config.validation.max_displayname_size
|
||||||
|
) {
|
||||||
|
return errorResponse(
|
||||||
|
`Display name must be between 3 and ${config.validation.max_displayname_size} characters`,
|
||||||
|
422
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
user.display_name = display_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (note) {
|
||||||
|
// Check if within allowed note length
|
||||||
|
if (note.length > config.validation.max_note_size) {
|
||||||
|
return errorResponse(
|
||||||
|
`Note must be less than ${config.validation.max_note_size} characters`,
|
||||||
|
422
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
user.bio = note;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (avatar) {
|
||||||
|
// Check if within allowed avatar length (avatar is an image)
|
||||||
|
if (avatar.size > config.validation.max_avatar_size) {
|
||||||
|
return errorResponse(
|
||||||
|
`Avatar must be less than ${config.validation.max_avatar_size} bytes`,
|
||||||
|
422
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Store the file somewhere and then change the user's actual avatar
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header) {
|
||||||
|
// Check if within allowed header length (header is an image)
|
||||||
|
if (header.size > config.validation.max_header_size) {
|
||||||
|
return errorResponse(
|
||||||
|
`Header must be less than ${config.validation.max_avatar_size} bytes`,
|
||||||
|
422
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// TODO: Store the file somewhere and then change the user's actual header
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locked) {
|
||||||
|
// Check if locked is a boolean
|
||||||
|
if (locked !== "true" && locked !== "false") {
|
||||||
|
return errorResponse("Locked must be a boolean", 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add a user value for Locked
|
||||||
|
// user.locked = locked === "true";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot) {
|
||||||
|
// Check if bot is a boolean
|
||||||
|
if (bot !== "true" && bot !== "false") {
|
||||||
|
return errorResponse("Bot must be a boolean", 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add a user value for bot
|
||||||
|
// user.bot = bot === "true";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (discoverable) {
|
||||||
|
// Check if discoverable is a boolean
|
||||||
|
if (discoverable !== "true" && discoverable !== "false") {
|
||||||
|
return errorResponse("Discoverable must be a boolean", 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add a user value for discoverable
|
||||||
|
// user.discoverable = discoverable === "true";
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonResponse(
|
||||||
|
{
|
||||||
|
error: `Not really implemented yet`,
|
||||||
|
},
|
||||||
|
501
|
||||||
|
);
|
||||||
|
};
|
||||||
13
utils/auth.ts
Normal file
13
utils/auth.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { Token } from "~database/entities/Token";
|
||||||
|
|
||||||
|
export const getUserByToken = async (access_token: string | null) => {
|
||||||
|
if (!access_token) return null;
|
||||||
|
|
||||||
|
const token = await Token.findOneBy({
|
||||||
|
access_token,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!token) return null;
|
||||||
|
|
||||||
|
return token.user;
|
||||||
|
};
|
||||||
|
|
@ -17,6 +17,8 @@ export interface ConfigType {
|
||||||
max_bio_size: number;
|
max_bio_size: number;
|
||||||
max_username_size: number;
|
max_username_size: number;
|
||||||
max_note_size: number;
|
max_note_size: number;
|
||||||
|
max_avatar_size: number;
|
||||||
|
max_header_size: number;
|
||||||
max_media_size: number;
|
max_media_size: number;
|
||||||
max_media_attachments: number;
|
max_media_attachments: number;
|
||||||
max_media_description_size: number;
|
max_media_description_size: number;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue