From ed02c1ae36dce4b20380829d19b51d22246f5d5f Mon Sep 17 00:00:00 2001 From: Andras Bacsai Date: Mon, 3 Oct 2022 11:31:50 +0200 Subject: [PATCH] ui: iam & settings update --- apps/api/src/routes/api/v1/iam/handlers.ts | 43 ++- apps/api/src/routes/api/v1/iam/index.ts | 9 +- apps/api/src/routes/api/v1/iam/types.ts | 8 + apps/ui/src/lib/locales/en.json | 2 +- apps/ui/src/lib/locales/fr.json | 2 +- apps/ui/src/routes/__layout.svelte | 1 + apps/ui/src/routes/iam/_Account.svelte | 64 ++++ apps/ui/src/routes/iam/_Menu.svelte | 63 ++-- apps/ui/src/routes/iam/__layout.svelte | 14 +- apps/ui/src/routes/iam/index.svelte | 293 ++---------------- apps/ui/src/routes/iam/pending.svelte | 4 + .../src/routes/iam/team/[id]/__layout.svelte | 84 ----- apps/ui/src/routes/iam/teams.svelte | 108 +++++++ .../src/routes/iam/teams/[id]/__layout.svelte | 31 ++ .../iam/{team => teams}/[id]/index.svelte | 180 ++++++++--- apps/ui/src/routes/index.svelte | 244 +++++++-------- apps/ui/src/routes/settings/_Menu.svelte | 6 +- apps/ui/src/routes/settings/ssh.svelte | 15 +- 18 files changed, 597 insertions(+), 574 deletions(-) create mode 100644 apps/ui/src/routes/iam/_Account.svelte delete mode 100644 apps/ui/src/routes/iam/team/[id]/__layout.svelte create mode 100644 apps/ui/src/routes/iam/teams.svelte create mode 100644 apps/ui/src/routes/iam/teams/[id]/__layout.svelte rename apps/ui/src/routes/iam/{team => teams}/[id]/index.svelte (53%) diff --git a/apps/api/src/routes/api/v1/iam/handlers.ts b/apps/api/src/routes/api/v1/iam/handlers.ts index b423b16bb..d61b173f2 100644 --- a/apps/api/src/routes/api/v1/iam/handlers.ts +++ b/apps/api/src/routes/api/v1/iam/handlers.ts @@ -5,9 +5,10 @@ import { decrypt, errorHandler, prisma, uniqueName } from '../../../../lib/commo import { day } from '../../../../lib/dayjs'; import type { OnlyId } from '../../../../types'; -import type { BodyId, InviteToTeam, SaveTeam, SetPermission } from './types'; +import type { BodyId, DeleteUserFromTeam, InviteToTeam, SaveTeam, SetPermission } from './types'; -export async function listTeams(request: FastifyRequest) { + +export async function listAccounts(request: FastifyRequest) { try { const userId = request.user.userId; const teamId = request.user.teamId; @@ -15,10 +16,24 @@ export async function listTeams(request: FastifyRequest) { where: { id: userId }, select: { id: true, email: true, teams: true } }); - let accounts = []; - let allTeams = []; + let accounts = await prisma.user.findMany({ where: { teams: { some: { id: teamId } } }, select: { id: true, email: true, teams: true } }); if (teamId === '0') { accounts = await prisma.user.findMany({ select: { id: true, email: true, teams: true } }); + } + return { + account, + accounts + }; + } catch ({ status, message }) { + return errorHandler({ status, message }) + } +} +export async function listTeams(request: FastifyRequest) { + try { + const userId = request.user.userId; + const teamId = request.user.teamId; + let allTeams = []; + if (teamId === '0') { allTeams = await prisma.team.findMany({ where: { users: { none: { id: userId } } }, include: { permissions: true } @@ -28,18 +43,30 @@ export async function listTeams(request: FastifyRequest) { where: { users: { some: { id: userId } } }, include: { permissions: true } }); - const invitations = await prisma.teamInvitation.findMany({ where: { uid: userId } }); return { ownTeams, allTeams, - invitations, - account, - accounts }; } catch ({ status, message }) { return errorHandler({ status, message }) } } +export async function removeUserFromTeam(request: FastifyRequest, reply: FastifyReply) { + try { + const { uid } = request.body; + const { id } = request.params; + const userId = request.user.userId; + const foundUser = await prisma.team.findMany({ where: { id, users: { some: { id: userId } } } }); + if (foundUser.length === 0) { + return errorHandler({ status: 404, message: 'Team not found' }); + } + await prisma.team.update({ where: { id }, data: { users: { disconnect: { id: uid } } } }); + await prisma.permission.deleteMany({ where: { teamId: id, userId: uid } }) + return reply.code(201).send() + } catch ({ status, message }) { + return errorHandler({ status, message }) + } +} export async function deleteTeam(request: FastifyRequest, reply: FastifyReply) { try { const userId = request.user.userId; diff --git a/apps/api/src/routes/api/v1/iam/index.ts b/apps/api/src/routes/api/v1/iam/index.ts index 539586b2d..42b0dec69 100644 --- a/apps/api/src/routes/api/v1/iam/index.ts +++ b/apps/api/src/routes/api/v1/iam/index.ts @@ -1,19 +1,22 @@ import { FastifyPluginAsync } from 'fastify'; -import { acceptInvitation, changePassword, deleteTeam, getTeam, inviteToTeam, listTeams, newTeam, removeUser, revokeInvitation, saveTeam, setPermission } from './handlers'; +import { acceptInvitation, changePassword, deleteTeam, getTeam, inviteToTeam, listAccounts, listTeams, newTeam, removeUser, removeUserFromTeam, revokeInvitation, saveTeam, setPermission } from './handlers'; import type { OnlyId } from '../../../../types'; -import type { BodyId, InviteToTeam, SaveTeam, SetPermission } from './types'; +import type { BodyId, DeleteUserFromTeam, InviteToTeam, SaveTeam, SetPermission } from './types'; const root: FastifyPluginAsync = async (fastify): Promise => { fastify.addHook('onRequest', async (request) => { return await request.jwtVerify() }) - fastify.get('/', async (request) => await listTeams(request)); + + fastify.get('/', async (request) => await listAccounts(request)); fastify.post('/new', async (request, reply) => await newTeam(request, reply)); + fastify.get('/teams', async (request) => await listTeams(request)); fastify.get('/team/:id', async (request, reply) => await getTeam(request, reply)); fastify.post('/team/:id', async (request, reply) => await saveTeam(request, reply)); fastify.delete('/team/:id', async (request, reply) => await deleteTeam(request, reply)); + fastify.post('/team/:id/user/remove', async (request, reply) => await removeUserFromTeam(request, reply)); fastify.post('/team/:id/invitation/invite', async (request, reply) => await inviteToTeam(request, reply)) fastify.post('/team/:id/invitation/accept', async (request) => await acceptInvitation(request)); diff --git a/apps/api/src/routes/api/v1/iam/types.ts b/apps/api/src/routes/api/v1/iam/types.ts index 26a896b8e..959d3dac5 100644 --- a/apps/api/src/routes/api/v1/iam/types.ts +++ b/apps/api/src/routes/api/v1/iam/types.ts @@ -5,6 +5,14 @@ export interface SaveTeam extends OnlyId { name: string } } +export interface DeleteUserFromTeam { + Body: { + uid: string + }, + Params: { + id: string + } +} export interface InviteToTeam { Body: { email: string, diff --git a/apps/ui/src/lib/locales/en.json b/apps/ui/src/lib/locales/en.json index d98d5a88b..5abe6ee55 100644 --- a/apps/ui/src/lib/locales/en.json +++ b/apps/ui/src/lib/locales/en.json @@ -328,7 +328,7 @@ "members": "Members", "root_team_explainer": "This is the root team. That means members of this group can manage instance wide settings and have all the priviliges in Coolify (imagine like root user on Linux).", "permission": "Permission", - "you": "(You)", + "you": "You", "promote_to": "Promote to {{grade}}", "revoke_invitation": "Revoke invitation", "pending_invitation": "Pending invitation", diff --git a/apps/ui/src/lib/locales/fr.json b/apps/ui/src/lib/locales/fr.json index 9dc5aa8c4..8fa7fca16 100644 --- a/apps/ui/src/lib/locales/fr.json +++ b/apps/ui/src/lib/locales/fr.json @@ -318,6 +318,6 @@ "root": "(suprême)", "root_team_explainer": "Il s'agit de l'équipe suprême. \nCela signifie que les membres de ce groupe peuvent gérer les paramètres à l'échelle de l'instance et avoir tous les privilèges dans Coolify (imaginez comme un utilisateur root sous Linux).", "send_invitation": "Envoyer une invitation", - "you": "(Toi)" + "you": "Toi" } } diff --git a/apps/ui/src/routes/__layout.svelte b/apps/ui/src/routes/__layout.svelte index 22a11c765..248ac22ec 100644 --- a/apps/ui/src/routes/__layout.svelte +++ b/apps/ui/src/routes/__layout.svelte @@ -352,6 +352,7 @@
  • + export let account: any; + export let accounts: any = []; + import { del, get, post } from '$lib/api'; + import { errorNotification } from '$lib/common'; + import { addToast, appSession } from '$lib/store'; + async function resetPassword(id: any) { + const sure = window.confirm('Are you sure you want to reset the password?'); + if (!sure) { + return; + } + try { + await post(`/iam/user/password`, { id }); + return addToast({ + message: 'Password reset successfully. Please relogin to reset it.', + type: 'success' + }); + } catch (error) { + return errorNotification(error); + } + } + async function deleteAccount(id: any) { + if (id === $appSession.userId || account.id === '0') return; + const sure = window.confirm('Are you sure you want to delete this user?'); + if (!sure) { + return; + } + try { + await del(`/iam/user/remove`, { id }); + addToast({ + message: 'Account deleted.', + type: 'success' + }); + const data = await get('/iam'); + accounts = data.accounts; + } catch (error) { + return errorNotification(error); + } + } + + +
    + +
    +
    + +
    +
    + +
    +
    +
    diff --git a/apps/ui/src/routes/iam/_Menu.svelte b/apps/ui/src/routes/iam/_Menu.svelte index 23b3709f6..ad60ef343 100644 --- a/apps/ui/src/routes/iam/_Menu.svelte +++ b/apps/ui/src/routes/iam/_Menu.svelte @@ -4,29 +4,32 @@