Basic services; Version 0.2.0

This commit is contained in:
Aslan 2025-12-27 01:51:42 +01:00
parent cae53fab61
commit 72d7b22891
11 changed files with 483 additions and 11 deletions

View file

@ -7,12 +7,30 @@ import type {
IPatchCommunityRequest,
IPatchCommunityResponseError,
IPatchCommunityResponseSuccess,
IGetMembersParams,
IGetMembersResponseError,
IGetMembersResponseSuccess,
IGetChannelsParams,
IGetChannelsResponseError,
IGetChannelsResponseSuccess,
IGetRolesParams,
IGetRolesResponseError,
IGetRolesResponseSuccess,
IPostCreateInviteParams,
IPostCreateInviteRequest,
IPostCreateInviteResponseError,
IPostCreateInviteResponseSuccess,
} from "./types.js";
import {
createInviteAuth,
getCommunityById,
getCommunityChannelsByIdAuth,
getCommunityMembersByIdAuth,
getCommunityRolesByIdAuth,
updateCommunityByIdAuth,
} from "../../services/community/community.js";
import { API_ERROR } from "../errors.js";
import type { ICreateInvite } from "../../services/community/types.js";
const getCommunity = async (request: FastifyRequest, reply: FastifyReply) => {
const { id } = request.params as IGetCommunityParams;
@ -66,4 +84,147 @@ const patchCommunity = async (request: FastifyRequest, reply: FastifyReply) => {
} as IPatchCommunityResponseSuccess;
};
export { getCommunity, patchCommunity };
const getMembers = async (request: FastifyRequest, reply: FastifyReply) => {
const { id } = request.params as IGetMembersParams;
const authHeader = request.headers["authorization"];
const community = await getCommunityById(id);
const members = await getCommunityMembersByIdAuth(id, authHeader);
if (!members || !community) {
reply.status(404);
return {
id: id,
error: API_ERROR.NOT_FOUND,
} as IGetMembersResponseError;
}
if (members === API_ERROR.ACCESS_DENIED) {
reply.status(403);
return {
id: id,
error: API_ERROR.ACCESS_DENIED,
} as IGetMembersResponseError;
}
return {
id: community.id,
name: community.name,
members: members.map((member) => ({
id: member.id,
username: member.username,
})),
} as IGetMembersResponseSuccess;
};
const getChannels = async (request: FastifyRequest, reply: FastifyReply) => {
const { id } = request.params as IGetChannelsParams;
const authHeader = request.headers["authorization"];
const community = await getCommunityById(id);
const channels = await getCommunityChannelsByIdAuth(id, authHeader);
if (!channels || !community) {
reply.status(404);
return {
id: id,
error: API_ERROR.NOT_FOUND,
} as IGetChannelsResponseError;
}
if (channels === API_ERROR.ACCESS_DENIED) {
reply.status(403);
return {
id: id,
error: API_ERROR.ACCESS_DENIED,
} as IGetChannelsResponseError;
}
return {
id: community.id,
name: community.name,
channels: channels.map((channel) => ({
id: channel.id,
name: channel.name,
})),
} as IGetChannelsResponseSuccess;
};
const getRoles = async (request: FastifyRequest, reply: FastifyReply) => {
const { id } = request.params as IGetRolesParams;
const authHeader = request.headers["authorization"];
const community = await getCommunityById(id);
const roles = await getCommunityRolesByIdAuth(id, authHeader);
if (!roles || !community) {
reply.status(404);
return {
id: id,
error: API_ERROR.NOT_FOUND,
} as IGetRolesResponseError;
}
if (roles === API_ERROR.ACCESS_DENIED) {
reply.status(403);
return {
id: id,
error: API_ERROR.ACCESS_DENIED,
} as IGetRolesResponseError;
}
return {
id: community.id,
name: community.name,
roles: roles.map((role) => ({
id: role.id,
name: role.name,
})),
} as IGetRolesResponseSuccess;
};
const postCreateInvite = async (
request: FastifyRequest,
reply: FastifyReply,
) => {
const { id } = request.params as IPostCreateInviteParams;
const { creatorId, totalInvites, expirationDate } =
request.body as IPostCreateInviteRequest;
const authHeader = request.headers["authorization"];
const createInviteData = {
creatorId: creatorId,
} as ICreateInvite;
if (totalInvites) {
createInviteData.totalInvites = totalInvites;
createInviteData.remainingInvites = totalInvites;
}
if (expirationDate) {
createInviteData.expirationDate = new Date(expirationDate);
}
const community = await getCommunityById(id);
const invite = await createInviteAuth(id, createInviteData, authHeader);
if (!invite || !community) {
reply.status(404);
return {
id: id,
error: API_ERROR.NOT_FOUND,
} as IPostCreateInviteResponseError;
}
if (invite === API_ERROR.ACCESS_DENIED) {
reply.status(403);
return {
id: id,
error: API_ERROR.ACCESS_DENIED,
} as IPostCreateInviteResponseError;
}
return {
id: community.id,
inviteId: invite.id,
} as IPostCreateInviteResponseSuccess;
};
export {
getCommunity,
patchCommunity,
getMembers,
getChannels,
getRoles,
postCreateInvite,
};

View file

@ -4,6 +4,10 @@ import * as controller from "./community.js";
const communityRoutes = async (fastify: FastifyInstance) => {
fastify.get(`/:id`, controller.getCommunity);
fastify.patch(`/:id`, controller.patchCommunity);
fastify.get(`/:id/members`, controller.getMembers);
fastify.get(`/:id/channels`, controller.getChannels);
fastify.get(`/:id/roles`, controller.getRoles);
fastify.post(`/:id/invite`, controller.postCreateInvite);
};
export { communityRoutes };

View file

@ -34,6 +34,86 @@ interface IPatchCommunityResponseSuccess {
description: string;
}
interface IGetMembersParams {
id: string;
}
interface IGetMembersResponseError {
id: string;
error: string;
}
interface IGetMembersResponseSuccess {
id: string;
name: string;
members: IGetMembersResponseMembers[];
}
interface IGetMembersResponseMembers {
id: string;
username: string;
}
interface IGetChannelsParams {
id: string;
}
interface IGetChannelsResponseError {
id: string;
error: string;
}
interface IGetChannelsResponseSuccess {
id: string;
name: string;
channels: IGetChannelsResponseChannels[];
}
interface IGetChannelsResponseChannels {
id: string;
name: string;
}
interface IGetRolesParams {
id: string;
}
interface IGetRolesResponseError {
id: string;
error: string;
}
interface IGetRolesResponseSuccess {
id: string;
name: string;
roles: IGetRolesResponseRoles[];
}
interface IGetRolesResponseRoles {
id: string;
name: string;
}
interface IPostCreateInviteParams {
id: string;
}
interface IPostCreateInviteRequest {
creatorId: string;
totalInvites?: number;
expirationDate?: number;
}
interface IPostCreateInviteResponseError {
id: string;
error: string;
}
interface IPostCreateInviteResponseSuccess {
id: string;
inviteId: string;
}
export {
type IGetCommunityParams,
type IGetCommunityResponseError,
@ -42,4 +122,20 @@ export {
type IPatchCommunityRequest,
type IPatchCommunityResponseError,
type IPatchCommunityResponseSuccess,
type IGetMembersParams,
type IGetMembersResponseError,
type IGetMembersResponseSuccess,
type IGetMembersResponseMembers,
type IGetChannelsParams,
type IGetChannelsResponseError,
type IGetChannelsResponseSuccess,
type IGetChannelsResponseChannels,
type IGetRolesParams,
type IGetRolesResponseError,
type IGetRolesResponseSuccess,
type IGetRolesResponseRoles,
type IPostCreateInviteParams,
type IPostCreateInviteRequest,
type IPostCreateInviteResponseError,
type IPostCreateInviteResponseSuccess,
};

View file

@ -47,6 +47,7 @@ interface IGetSessionsResponseError {
}
interface IGetSessionsResponseSuccess {
id: string;
sessions: IGetSessionsResponseSession[];
}

View file

@ -98,6 +98,7 @@ const getSessions = async (request: FastifyRequest, reply: FastifyReply) => {
}
return {
id: id,
sessions: sessions.map((session) => ({
id: session.id,
userId: session.userId,

View file

@ -1,13 +1,14 @@
enum PERMISSION {
COMMUNITY_MANAGE = "COMMUNITY_MANAGE",
CHANNELS_MANAGE = "CHANNELS_MANAGE",
CHANNELS_READ = "CHANNELS_READ",
CHANNELS_MANAGE = "CHANNELS_MANAGE",
ROLES_READ = "ROLES_READ",
INVITES_CREATE = "INVITES_CREATE",
INVITES_DELETE = "INVITES_DELETE",
ROLES_MANAGE = "ROLES_MANAGE",
USERS_KICK = "USERS_KICK",
USERS_BAN = "USERS_BAN",
MEMBERS_READ = "MEMBERS_READ",
MEMBERS_KICK = "MEMBERS_KICK",
MEMBERS_BAN = "MEMBERS_BAN",
}
export { PERMISSION };

View file

@ -1,9 +1,16 @@
import { API_ERROR } from "../../controllers/errors.js";
import type { Community } from "../../generated/prisma/client.js";
import type { Community, Invite, User } 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";
import { getInviteById } from "../invite/invite.js";
import type {
ICommunityChannel,
ICommunityMember,
ICommunityRole,
ICreateInvite,
IUpdateCommunity,
} from "./types.js";
const getCommunityById = async (id: string): Promise<Community | null> => {
return await getDB().community.findUnique({
@ -49,4 +56,165 @@ const updateCommunityByIdAuth = async (
return await updateCommunityById(id, update);
};
export { getCommunityById, updateCommunityById, updateCommunityByIdAuth };
const getCommunityMembersById = async (
id: string,
): Promise<ICommunityMember[]> => {
return await getDB().user.findMany({
where: {
communities: {
some: {
id: id,
},
},
},
select: {
id: true,
username: true,
},
});
};
const getCommunityMembersByIdAuth = async (
id: string,
authHeader: string | undefined,
): Promise<ICommunityMember[] | API_ERROR.ACCESS_DENIED> => {
const authUser = await getUserFromAuth(authHeader);
const community = await getCommunityById(id);
if (
!(await isUserAllowed(
authUser,
{
community: community,
},
community,
[PERMISSION.MEMBERS_READ],
))
) {
return API_ERROR.ACCESS_DENIED;
}
return await getCommunityMembersById(id);
};
const getCommunityChannelsById = async (
id: string,
): Promise<ICommunityChannel[]> => {
return await getDB().channel.findMany({
where: {
communityId: id,
},
select: {
id: true,
name: true,
},
});
};
const getCommunityChannelsByIdAuth = async (
id: string,
authHeader: string | undefined,
): Promise<ICommunityChannel[] | API_ERROR.ACCESS_DENIED> => {
const authUser = await getUserFromAuth(authHeader);
const community = await getCommunityById(id);
if (
!(await isUserAllowed(
authUser,
{
community: community,
},
community,
[PERMISSION.CHANNELS_READ],
))
) {
return API_ERROR.ACCESS_DENIED;
}
return await getCommunityChannelsById(id);
};
const getCommunityRolesById = async (id: string): Promise<ICommunityRole[]> => {
return await getDB().role.findMany({
where: {
communityId: id,
},
select: {
id: true,
name: true,
},
});
};
const getCommunityRolesByIdAuth = async (
id: string,
authHeader: string | undefined,
): Promise<ICommunityRole[] | API_ERROR.ACCESS_DENIED> => {
const authUser = await getUserFromAuth(authHeader);
const community = await getCommunityById(id);
if (
!(await isUserAllowed(
authUser,
{
community: community,
},
community,
[PERMISSION.ROLES_READ],
))
) {
return API_ERROR.ACCESS_DENIED;
}
return await getCommunityRolesById(id);
};
const createInvite = async (
id: string,
createInviteData: ICreateInvite,
): Promise<Invite> => {
return await getDB().invite.create({
data: {
...createInviteData,
communityId: id,
},
});
};
const createInviteAuth = async (
id: string,
createInviteData: ICreateInvite,
authHeader: string | undefined,
): Promise<Invite | API_ERROR.ACCESS_DENIED> => {
const authUser = await getUserFromAuth(authHeader);
const community = await getCommunityById(id);
if (
!(await isUserAllowed(
authUser,
{
community: community,
},
community,
[PERMISSION.INVITES_CREATE],
))
) {
return API_ERROR.ACCESS_DENIED;
}
return await createInvite(id, createInviteData);
};
export {
getCommunityById,
updateCommunityById,
updateCommunityByIdAuth,
getCommunityMembersById,
getCommunityMembersByIdAuth,
getCommunityChannelsById,
getCommunityChannelsByIdAuth,
getCommunityRolesById,
getCommunityRolesByIdAuth,
createInvite,
createInviteAuth,
};

View file

@ -3,4 +3,32 @@ interface IUpdateCommunity {
description?: string;
}
export { type IUpdateCommunity };
interface ICommunityMember {
id: string;
username: string;
}
interface ICommunityChannel {
id: string;
name: string;
}
interface ICommunityRole {
id: string;
name: string;
}
interface ICreateInvite {
creatorId: string;
totalInvites?: number;
remainingInvites?: number;
expirationDate?: Date;
}
export {
type IUpdateCommunity,
type ICommunityMember,
type ICommunityChannel,
type ICommunityRole,
type ICreateInvite,
};