Basic services; Version 0.2.0
This commit is contained in:
parent
cae53fab61
commit
72d7b22891
11 changed files with 483 additions and 11 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "tether",
|
||||
"version": "0.1.0",
|
||||
"version": "0.2.0",
|
||||
"description": "Communication server using the Nexlink protocol",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
Warnings:
|
||||
|
||||
- Made the column `name` on table `Channel` required. This step will fail if there are existing NULL values in that column.
|
||||
- Made the column `name` on table `Role` required. This step will fail if there are existing NULL values in that column.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "Channel" ALTER COLUMN "name" SET NOT NULL;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "Role" ALTER COLUMN "name" SET NOT NULL;
|
||||
|
|
@ -23,7 +23,7 @@ model Community {
|
|||
|
||||
model Channel {
|
||||
id String @id @unique @default(uuid())
|
||||
name String?
|
||||
name String
|
||||
community Community? @relation(fields: [communityId], references: [id])
|
||||
communityId String?
|
||||
creationDate DateTime @default(now())
|
||||
|
|
@ -31,7 +31,7 @@ model Channel {
|
|||
|
||||
model Role {
|
||||
id String @id @unique @default(uuid())
|
||||
name String?
|
||||
name String
|
||||
community Community @relation(fields: [communityId], references: [id])
|
||||
communityId String
|
||||
users User[] @relation(name: "UsersRolesToUsers")
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ interface IGetSessionsResponseError {
|
|||
}
|
||||
|
||||
interface IGetSessionsResponseSuccess {
|
||||
id: string;
|
||||
sessions: IGetSessionsResponseSession[];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ const getSessions = async (request: FastifyRequest, reply: FastifyReply) => {
|
|||
}
|
||||
|
||||
return {
|
||||
id: id,
|
||||
sessions: sessions.map((session) => ({
|
||||
id: session.id,
|
||||
userId: session.userId,
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue