mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 08:28: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_username_size: number;
|
||||
max_note_size: number;
|
||||
max_avatar_size: number;
|
||||
max_header_size: number;
|
||||
max_media_size: number;
|
||||
max_media_attachments: number;
|
||||
max_media_description_size: number;
|
||||
|
|
|
|||
Loading…
Reference in a new issue