Add more services and auth

This commit is contained in:
Aslan 2025-12-27 00:18:15 +01:00
parent d17f37749d
commit cae53fab61
13 changed files with 331 additions and 57 deletions

View file

@ -1,20 +1,29 @@
import { type FastifyReply, type FastifyRequest } from "fastify";
import type {
ICommunityParams,
ICommunityResponseError,
ICommunityResponseSuccess,
IGetCommunityParams,
IGetCommunityResponseError,
IGetCommunityResponseSuccess,
IPatchCommunityParams,
IPatchCommunityRequest,
IPatchCommunityResponseError,
IPatchCommunityResponseSuccess,
} from "./types.js";
import { getCommunityById } from "../../services/community/community.js";
import {
getCommunityById,
updateCommunityByIdAuth,
} from "../../services/community/community.js";
import { API_ERROR } from "../errors.js";
const getCommunity = async (request: FastifyRequest, _reply: FastifyReply) => {
const { id } = request.params as ICommunityParams;
const getCommunity = async (request: FastifyRequest, reply: FastifyReply) => {
const { id } = request.params as IGetCommunityParams;
const community = await getCommunityById(id);
if (!community) {
reply.status(404);
return {
id: id,
error: "community does not exist",
} as ICommunityResponseError;
error: API_ERROR.NOT_FOUND,
} as IGetCommunityResponseError;
}
return {
@ -22,7 +31,39 @@ const getCommunity = async (request: FastifyRequest, _reply: FastifyReply) => {
name: community.name,
description: community.description,
creationDate: community.creationDate.getTime(),
} as ICommunityResponseSuccess;
} as IGetCommunityResponseSuccess;
};
export { getCommunity };
const patchCommunity = async (request: FastifyRequest, reply: FastifyReply) => {
const { id } = request.params as IPatchCommunityParams;
const patchCommunityRequest = request.body as IPatchCommunityRequest;
const authHeader = request.headers["authorization"];
const community = await updateCommunityByIdAuth(
id,
patchCommunityRequest,
authHeader,
);
if (!community) {
reply.status(404);
return {
id: id,
error: API_ERROR.NOT_FOUND,
} as IPatchCommunityResponseError;
}
if (community === API_ERROR.ACCESS_DENIED) {
reply.status(403);
return {
id: id,
error: API_ERROR.ACCESS_DENIED,
} as IPatchCommunityResponseError;
}
return {
id: community.id,
name: community.name,
description: community.description,
} as IPatchCommunityResponseSuccess;
};
export { getCommunity, patchCommunity };

View file

@ -3,6 +3,7 @@ import * as controller from "./community.js";
const communityRoutes = async (fastify: FastifyInstance) => {
fastify.get(`/:id`, controller.getCommunity);
fastify.patch(`/:id`, controller.patchCommunity);
};
export { communityRoutes };

View file

@ -1,21 +1,45 @@
interface ICommunityParams {
interface IGetCommunityParams {
id: string;
}
interface ICommunityResponseError {
interface IGetCommunityResponseError {
id: string;
error: string;
}
interface ICommunityResponseSuccess {
interface IGetCommunityResponseSuccess {
id: string;
name: string;
description: string;
creationDate: number;
}
interface IPatchCommunityParams {
id: string;
}
interface IPatchCommunityRequest {
name?: string;
description?: string;
}
interface IPatchCommunityResponseError {
id: string;
error: string;
}
interface IPatchCommunityResponseSuccess {
id: string;
name: string;
description: string;
}
export {
type ICommunityParams,
type ICommunityResponseError,
type ICommunityResponseSuccess,
type IGetCommunityParams,
type IGetCommunityResponseError,
type IGetCommunityResponseSuccess,
type IPatchCommunityParams,
type IPatchCommunityRequest,
type IPatchCommunityResponseError,
type IPatchCommunityResponseSuccess,
};

View file

@ -3,6 +3,7 @@ import * as controller from "./user.js";
const userRoutes = async (fastify: FastifyInstance) => {
fastify.get(`/:id`, controller.getUser);
fastify.patch(`/:id`, controller.patchUser);
fastify.get(`/:id/sessions`, controller.getSessions);
};

View file

@ -1,13 +1,13 @@
interface IUserParams {
interface IGetUserParams {
id: string;
}
interface IUserResponseError {
interface IGetUserResponseError {
id: string;
error: string;
}
interface IUserResponseSuccess {
interface IGetUserResponseSuccess {
id: string;
username: string;
email: string;
@ -17,25 +17,54 @@ interface IUserResponseSuccess {
lastLogin: number;
}
interface ISessionsResponseError {
interface IPatchUserParams {
id: string;
}
interface IPatchUserRequest {
email?: string;
description?: string;
}
interface IPatchUserResponseError {
id: string;
error: string;
}
interface ISessionsResponseSuccess {
sessions: ISessionsResponseSession[];
interface IPatchUserResponseSuccess {
id: string;
email: string;
description: string;
}
interface ISessionsResponseSession {
interface IGetSessionsParams {
id: string;
}
interface IGetSessionsResponseError {
id: string;
error: string;
}
interface IGetSessionsResponseSuccess {
sessions: IGetSessionsResponseSession[];
}
interface IGetSessionsResponseSession {
id: string;
userId: string;
}
export {
type IUserParams,
type IUserResponseError,
type IUserResponseSuccess,
type ISessionsResponseError,
type ISessionsResponseSuccess,
type ISessionsResponseSession,
type IGetUserParams,
type IGetUserResponseError,
type IGetUserResponseSuccess,
type IPatchUserParams,
type IPatchUserRequest,
type IPatchUserResponseError,
type IPatchUserResponseSuccess,
type IGetSessionsParams,
type IGetSessionsResponseError,
type IGetSessionsResponseSuccess,
type IGetSessionsResponseSession,
};

View file

@ -1,22 +1,41 @@
import { type FastifyReply, type FastifyRequest } from "fastify";
import type {
IUserParams,
IUserResponseError,
IUserResponseSuccess,
ISessionsResponseError,
ISessionsResponseSuccess,
IGetUserParams,
IGetUserResponseError,
IGetUserResponseSuccess,
IPatchUserParams,
IPatchUserRequest,
IPatchUserResponseError,
IPatchUserResponseSuccess,
IGetSessionsParams,
IGetSessionsResponseError,
IGetSessionsResponseSuccess,
} from "./types.js";
import { getUserById, getUserSessionsById } from "../../services/user/user.js";
import {
getUserByIdAuth,
updateUserByIdAuth,
getUserSessionsByIdAuth,
} from "../../services/user/user.js";
import { API_ERROR } from "../errors.js";
const getUser = async (request: FastifyRequest, _reply: FastifyReply) => {
const { id } = request.params as IUserParams;
const getUser = async (request: FastifyRequest, reply: FastifyReply) => {
const { id } = request.params as IGetUserParams;
const authHeader = request.headers["authorization"];
const user = await getUserById(id);
const user = await getUserByIdAuth(id, authHeader);
if (!user) {
reply.status(404);
return {
id: id,
error: "user does not exist",
} as IUserResponseError;
error: API_ERROR.NOT_FOUND,
} as IGetUserResponseError;
}
if (user === API_ERROR.ACCESS_DENIED) {
reply.status(403);
return {
id: id,
error: API_ERROR.ACCESS_DENIED,
} as IGetUserResponseError;
}
return {
@ -27,19 +46,55 @@ const getUser = async (request: FastifyRequest, _reply: FastifyReply) => {
admin: user.admin,
registerDate: user.registerDate.getTime(),
lastLogin: user.lastLogin?.getTime() ?? 0,
} as IUserResponseSuccess;
} as IGetUserResponseSuccess;
};
const getSessions = async (request: FastifyRequest, _reply: FastifyReply) => {
const { id } = request.params as IUserParams;
const patchUser = async (request: FastifyRequest, reply: FastifyReply) => {
const { id } = request.params as IPatchUserParams;
const patchUserRequest = request.body as IPatchUserRequest;
const authHeader = request.headers["authorization"];
const sessions = await getUserSessionsById(id, authHeader);
if (!sessions) {
const user = await updateUserByIdAuth(id, patchUserRequest, authHeader);
if (!user) {
reply.status(404);
return {
id: id,
error: "user does not exist or you have no access",
} as ISessionsResponseError;
error: API_ERROR.NOT_FOUND,
} as IPatchUserResponseError;
}
if (user === API_ERROR.ACCESS_DENIED) {
reply.status(403);
return {
id: id,
error: API_ERROR.ACCESS_DENIED,
} as IPatchUserResponseError;
}
return {
id: user.id,
email: user.email,
description: user.description,
} as IPatchUserResponseSuccess;
};
const getSessions = async (request: FastifyRequest, reply: FastifyReply) => {
const { id } = request.params as IGetSessionsParams;
const authHeader = request.headers["authorization"];
const sessions = await getUserSessionsByIdAuth(id, authHeader);
if (!sessions) {
reply.status(404);
return {
id: id,
error: API_ERROR.NOT_FOUND,
} as IGetSessionsResponseError;
}
if (sessions === API_ERROR.ACCESS_DENIED) {
reply.status(403);
return {
id: id,
error: API_ERROR.ACCESS_DENIED,
} as IGetSessionsResponseError;
}
return {
@ -47,7 +102,7 @@ const getSessions = async (request: FastifyRequest, _reply: FastifyReply) => {
id: session.id,
userId: session.userId,
})),
} as ISessionsResponseSuccess;
} as IGetSessionsResponseSuccess;
};
export { getUser, getSessions };
export { getUser, patchUser, getSessions };

View file

@ -1,5 +1,9 @@
import { API_ERROR } from "../../controllers/errors.js";
import type { Community } from "../../generated/prisma/client.js";
import { getDB } from "../../store/store.js";
import { getUserFromAuth, isUserAllowed } from "../auth/helpers.js";
import { PERMISSION } from "../auth/permission.js";
import type { IUpdateCommunity } from "./types.js";
const getCommunityById = async (id: string): Promise<Community | null> => {
return await getDB().community.findUnique({
@ -7,4 +11,42 @@ const getCommunityById = async (id: string): Promise<Community | null> => {
});
};
export { getCommunityById };
const updateCommunityById = async (
id: string,
update: IUpdateCommunity,
): Promise<Community | null> => {
return await getDB().community.update({
where: {
id: id,
},
data: {
...update,
},
});
};
const updateCommunityByIdAuth = async (
id: string,
update: IUpdateCommunity,
authHeader: string | undefined,
): Promise<Community | null | API_ERROR.ACCESS_DENIED> => {
const authUser = await getUserFromAuth(authHeader);
const community = await getCommunityById(id);
if (
!(await isUserAllowed(
authUser,
{
community: community,
},
community,
[PERMISSION.COMMUNITY_MANAGE],
))
) {
return API_ERROR.ACCESS_DENIED;
}
return await updateCommunityById(id, update);
};
export { getCommunityById, updateCommunityById, updateCommunityByIdAuth };

View file

@ -1 +1,2 @@
export * from "./community.js";
export * from "./types.js";

View file

@ -0,0 +1,6 @@
interface IUpdateCommunity {
name?: string;
description?: string;
}
export { type IUpdateCommunity };

View file

@ -38,7 +38,7 @@ const deleteSessionByIdAuth = async (
authHeader: string | undefined,
): Promise<Session | null | API_ERROR.ACCESS_DENIED> => {
const authUser = await getUserFromAuth(authHeader);
const session = await deleteSessionById(id);
const session = await getSessionById(id);
if (
!(await isUserOwnerOrAdmin(authUser, {
@ -48,7 +48,7 @@ const deleteSessionByIdAuth = async (
return API_ERROR.ACCESS_DENIED;
}
return session;
return await deleteSessionById(id);
};
export {

View file

@ -1 +1,2 @@
export * from "./user.js";
export * from "./types.js";

View file

@ -0,0 +1,6 @@
interface IUpdateUser {
email?: string;
description?: string;
}
export { type IUpdateUser };

View file

@ -1,6 +1,8 @@
import type { User, Session } from "../../generated/prisma/client.js";
import { getUserFromAuth, isUserOwnerOrAdmin } from "../auth/helpers.js";
import { getDB } from "../../store/store.js";
import { API_ERROR } from "../../controllers/errors.js";
import type { IUpdateUser } from "./types.js";
const getUserById = async (id: string): Promise<User | null> => {
return await getDB().user.findUnique({
@ -8,19 +10,58 @@ const getUserById = async (id: string): Promise<User | null> => {
});
};
const getUserSessionsById = async (
const getUserByIdAuth = async (
id: string,
authHeader: string | undefined,
): Promise<Session[] | null> => {
): Promise<User | null | API_ERROR.ACCESS_DENIED> => {
const authUser = await getUserFromAuth(authHeader);
const user = await getUserById(id);
if (
!(await isUserOwnerOrAdmin(authUser, {
user: await getUserById(id),
user: user,
}))
) {
return null;
return API_ERROR.ACCESS_DENIED;
}
return user;
};
const updateUserById = async (
id: string,
update: IUpdateUser,
): Promise<User | null> => {
return await getDB().user.update({
where: {
id: id,
},
data: {
...update,
},
});
};
const updateUserByIdAuth = async (
id: string,
update: IUpdateUser,
authHeader: string | undefined,
): Promise<User | null | API_ERROR.ACCESS_DENIED> => {
const authUser = await getUserFromAuth(authHeader);
const user = await getUserById(id);
if (
!(await isUserOwnerOrAdmin(authUser, {
user: user,
}))
) {
return API_ERROR.ACCESS_DENIED;
}
return await updateUserById(id, update);
};
const getUserSessionsById = async (id: string): Promise<Session[] | null> => {
return await getDB().session.findMany({
where: {
userId: id,
@ -28,4 +69,30 @@ const getUserSessionsById = async (
});
};
export { getUserById, getUserSessionsById };
const getUserSessionsByIdAuth = async (
id: string,
authHeader: string | undefined,
): Promise<Session[] | null | API_ERROR.ACCESS_DENIED> => {
const authUser = await getUserFromAuth(authHeader);
const user = await getUserById(id);
const sessions = await getUserSessionsById(id);
if (
!(await isUserOwnerOrAdmin(authUser, {
user: user,
}))
) {
return API_ERROR.ACCESS_DENIED;
}
return sessions;
};
export {
getUserById,
getUserByIdAuth,
updateUserById,
updateUserByIdAuth,
getUserSessionsById,
getUserSessionsByIdAuth,
};