* New Version: 3.0.0
This commit is contained in:
Andras Bacsai
2022-07-06 11:02:36 +02:00
committed by GitHub
parent 9137e8bc32
commit 87ba4560ad
491 changed files with 16824 additions and 20459 deletions

View File

@@ -0,0 +1,242 @@
<script context="module" lang="ts">
import type { Load } from '@sveltejs/kit';
export const load: Load = async () => {
try {
const response = await get(`/iam`);
return {
props: {
...response
}
};
} catch (error: any) {
return {
status: 500,
error: new Error(error)
};
}
};
</script>
<script lang="ts">
export let account: any;
export let accounts: any;
export let invitations: any;
export let ownTeams: any;
export let allTeams: any;
import { page } from '$app/stores';
import { del, get, post } from '$lib/api';
import { toast } from '@zerodevx/svelte-toast';
import { errorNotification } from '$lib/common';
import { appSession } from '$lib/store';
import { goto } from '$app/navigation';
import Cookies from 'js-cookie';
if (accounts.length === 0) {
accounts.push(account);
}
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 });
toast.push('Password reset successfully. Please relogin to reset it.');
} catch (error) {
return errorNotification(error);
}
}
async function deleteUser(id: any) {
const sure = window.confirm('Are you sure you want to delete this user?');
if (!sure) {
return;
}
try {
await del(`/iam/user/remove`, { uid: id });
toast.push('Account deleted.');
const data = await get('/iam');
accounts = data.accounts;
} catch (error) {
return errorNotification(error);
}
}
async function acceptInvitation(id: any, teamId: any) {
try {
await post(`/iam/team/${teamId}/invitation/accept`, { id });
return window.location.reload();
} catch (error) {
return errorNotification(error);
}
}
async function revokeInvitation(id: any, teamId: any) {
try {
await post(`/iam/team/${teamId}/invitation/revoke`, { id });
return window.location.reload();
} catch (error) {
return errorNotification(error);
}
}
async function switchTeam(selectedTeamId: any) {
try {
const payload = await get(`/user?teamId=${selectedTeamId}`);
if (payload.token) {
Cookies.set('token', payload.token, {
path: '/'
});
$appSession.teamId = payload.teamId;
$appSession.userId = payload.userId;
$appSession.permission = payload.permission;
$appSession.isAdmin = payload.isAdmin;
return window.location.reload();
}
} catch (error) {
console.error(error);
return errorNotification(error);
}
}
async function newTeam() {
const { id } = await post('/iam/new', {});
return await goto(`/iam/team/${id}`, { replaceState: true });
}
</script>
<div class="flex space-x-1 p-6 font-bold">
<div class="mr-4 text-2xl tracking-tight">Identity and Access Management</div>
<div on:click={newTeam} class="add-icon cursor-pointer bg-fuchsia-600 hover:bg-fuchsia-500">
<svg
class="w-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
><path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
/></svg
>
</div>
</div>
{#if invitations.length > 0}
<div class="mx-auto max-w-4xl px-6 py-4">
<div class="title font-bold">Pending invitations</div>
<div class="pt-10 text-center">
{#each invitations as invitation}
<div class="flex justify-center space-x-2">
<div>
Invited to <span class="font-bold text-pink-600">{invitation.teamName}</span> with
<span class="font-bold text-rose-600">{invitation.permission}</span> permission.
</div>
<button
class="hover:bg-green-500"
on:click={() => acceptInvitation(invitation.id, invitation.teamId)}>Accept</button
>
<button
class="hover:bg-red-600"
on:click={() => revokeInvitation(invitation.id, invitation.teamId)}>Delete</button
>
</div>
{/each}
</div>
</div>
{/if}
<div class="mx-auto max-w-4xl px-6 py-4">
{#if $appSession.teamId === '0' && accounts.length > 0}
<div class="title font-bold">Accounts</div>
{:else}
<div class="title font-bold">Account</div>
{/if}
<div class="flex items-center justify-center pt-10">
<table class="mx-2 text-left">
<thead class="mb-2">
<tr>
{#if accounts.length > 1}
<th class="px-2">Email</th>
<th>Actions</th>
{/if}
</tr>
</thead>
<tbody>
{#each accounts as account}
<tr>
<td class="px-2">{account.email}</td>
<td class="flex space-x-2">
<form on:submit|preventDefault={() => resetPassword(account.id)}>
<button
class="mx-auto my-4 w-32 bg-fuchsia-600 hover:bg-fuchsia-500 disabled:bg-coolgray-200"
>Reset Password</button
>
</form>
<form on:submit|preventDefault={() => deleteUser(account.id)}>
<button
disabled={account.id === $appSession.userId}
class="mx-auto my-4 w-32 bg-red-600 hover:bg-red-500 disabled:bg-coolgray-200"
type="submit">Delete User</button
>
</form>
</td>
</tr>
{/each}
</tbody>
</table>
</div>
</div>
<div class="mx-auto max-w-4xl px-6">
<div class="title font-bold">Teams</div>
<div class="flex items-center justify-center pt-10">
<div class="flex flex-col">
<div class="flex flex-row flex-wrap justify-center px-2 pb-10 md:flex-row">
{#each ownTeams as team}
<a href="/iam/team/{team.id}" class="w-96 p-2 no-underline">
<div class="box-selection relative">
<div>
<div class="truncate text-center text-xl font-bold">
{team.name}
</div>
<div class="mt-1 text-center text-xs">
{team.permissions?.length} member(s)
</div>
</div>
<div class="flex items-center justify-center pt-3">
<button
on:click|preventDefault={() => switchTeam(team.id)}
class:bg-fuchsia-600={$appSession.teamId !== team.id}
class:hover:bg-fuchsia-500={$appSession.teamId !== team.id}
class:bg-transparent={$appSession.teamId === team.id}
disabled={$appSession.teamId === team.id}
>{$appSession.teamId === team.id ? 'Current Team' : 'Switch Team'}</button
>
</div>
</div>
</a>
{/each}
</div>
{#if $appSession.teamId === '0' && allTeams.length > 0}
<div class="pb-5 pt-10 text-xl font-bold">Other Teams</div>
<div class="flex flex-row flex-wrap justify-center px-2 md:flex-row">
{#each allTeams as team}
<a href="/iam/team/{team.id}" class="w-96 p-2 no-underline">
<div
class="box-selection relative"
class:hover:bg-fuchsia-600={team.id !== '0'}
class:hover:bg-red-500={team.id === '0'}
>
<div class="truncate text-center text-xl font-bold">
{team.name}
</div>
<div class="mt-1 text-center">{team.permissions?.length} member(s)</div>
</div>
</a>
{/each}
</div>
{/if}
</div>
</div>
</div>

View File

@@ -0,0 +1,63 @@
<script context="module" lang="ts">
import { del, get } from '$lib/api';
import type { Load } from '@sveltejs/kit';
export const load: Load = async ({ params, url }) => {
try {
const response = await get(`/iam/team/${params.id}`);
if (!response.permissions || Object.entries(response.permissions).length === 0) {
return {
status: 302,
redirect: '/iam'
};
}
return {
props: {
...response
},
stuff: {
...response
}
};
} catch (error) {
return handlerNotFoundLoad(error, url);
}
};
</script>
<script lang="ts">
import { page } from '$app/stores';
import { errorNotification, handlerNotFoundLoad } from '$lib/common';
import { appSession } from '$lib/store';
import { t } from '$lib/translations';
import DeleteIcon from '$lib/components/DeleteIcon.svelte';
import { goto } from '$app/navigation';
const { id } = $page.params;
async function deleteTeam() {
const sure = confirm('Are you sure you want to delete this team?');
if (sure) {
try {
await del(`/iam/team/${id}`, { id });
return await goto('/iam', { replaceState: true });
} catch (error) {
return errorNotification(error);
}
}
}
</script>
{#if id !== 'new'}
<nav class="nav-side">
<button
on:click={deleteTeam}
title={$t('source.delete_git_source')}
type="submit"
disabled={!$appSession.isAdmin}
class:hover:text-red-500={$appSession.isAdmin}
class="icons tooltip-bottom bg-transparent text-sm"
data-tooltip={$appSession.isAdmin
? 'Delete Team'
: $t('destination.permission_denied_delete_destination')}><DeleteIcon /></button
>
</nav>
{/if}
<slot />

View File

@@ -0,0 +1,210 @@
<script context="module" lang="ts">
import type { Load } from '@sveltejs/kit';
export const load: Load = async ({ fetch, params, stuff }) => {
return {
props: { ...stuff }
};
};
</script>
<script lang="ts">
export let permissions: any;
export let team: any;
export let invitations: any[];
import { page } from '$app/stores';
import Explainer from '$lib/components/Explainer.svelte';
import { post } from '$lib/api';
import { t } from '$lib/translations';
import { errorNotification } from '$lib/common';
import { appSession } from '$lib/store';
const { id } = $page.params;
let invitation: any = {
teamName: team.name,
email: null,
permission: 'read'
};
function isAdmin(permission: string) {
if (permission === 'admin' || permission === 'owner') {
return true;
}
return false;
}
async function sendInvitation() {
try {
await post(`/iam/team/${id}/invitation/invite`, {
teamId: team.id,
teamName: invitation.teamName,
email: invitation.email.toLowerCase(),
permission: invitation.permission
});
return window.location.reload();
} catch (error) {
return errorNotification(error);
}
}
async function revokeInvitation(id: string) {
try {
await post(`/iam/team/${id}/invitation/revoke`, { id });
return window.location.reload();
} catch (error) {
return errorNotification(error);
}
}
async function removeFromTeam(uid: string) {
try {
await post(`/iam/team/${id}/user/remove`, { teamId: team.id, uid });
return window.location.reload();
} catch (error) {
return errorNotification(error);
}
}
async function changePermission(userId: string, permissionId: string, currentPermission: string) {
let newPermission = 'read';
if (currentPermission === 'read') {
newPermission = 'admin';
}
try {
await post(`/iam/team/${id}/permission`, { userId, newPermission, permissionId });
return window.location.reload();
} catch (error) {
return errorNotification(error);
}
}
async function handleSubmit() {
try {
await post(`/iam/team/${id}`, { ...team });
return window.location.reload();
} catch (error) {
return errorNotification(error);
}
}
</script>
<div class="flex space-x-1 p-6 px-6 text-2xl font-bold">
<div class="tracking-tight">{$t('index.team')}</div>
<span class="arrow-right-applications px-1 text-fuchsia-500">></span>
<span class="pr-2">{team.name}</span>
</div>
<div class="mx-auto max-w-4xl px-6">
<form on:submit|preventDefault={handleSubmit} class=" py-4">
<div class="flex space-x-1 pb-5">
<div class="title font-bold">{$t('index.settings')}</div>
<button class="bg-fuchsia-600 hover:bg-fuchsia-500" type="submit">{$t('forms.save')}</button>
</div>
<div class="grid grid-flow-row gap-2 px-10">
<div class="mt-2 grid grid-cols-2">
<div class="flex-col">
<label for="name" class="text-base font-bold text-stone-100">{$t('forms.name')}</label>
{#if team.id === '0'}
<Explainer customClass="w-full" text={$t('team.root_team_explainer')} />
{/if}
</div>
<input id="name" name="name" placeholder="name" bind:value={team.name} />
</div>
</div>
</form>
<div class="flex space-x-1 py-5 pt-10 font-bold">
<div class="title">{$t('team.members')}</div>
</div>
<div class="px-4 sm:px-6">
<table class="w-full border-separate text-left">
<thead>
<tr class="h-8 border-b border-coolgray-400">
<th scope="col">{$t('forms.email')}</th>
<th scope="col">{$t('team.permission')}</th>
<th scope="col" class="text-center">{$t('forms.action')}</th>
</tr>
</thead>
{#each permissions as permission}
<tr class="text-xs">
<td class="py-4"
>{permission.user.email}
<span class="font-bold"
>{permission.user.id === $appSession.userId ? $t('team.you') : ''}</span
></td
>
<td class="py-4">{permission.permission}</td>
{#if $appSession.isAdmin && permission.user.id !== $appSession.userId && permission.permission !== 'owner'}
<td class="flex flex-col items-center justify-center space-y-2 py-4 text-center">
<button
class="w-52 bg-red-600 hover:bg-red-500"
on:click={() => removeFromTeam(permission.user.id)}>{$t('forms.remove')}</button
>
<button
class="w-52"
on:click={() =>
changePermission(permission.user.id, permission.id, permission.permission)}
>{$t('team.promote_to', {
grade: permission.permission === 'admin' ? 'read' : 'admin'
})}</button
>
</td>
{:else}
<td class="text-center py-4 flex-col space-y-2">
{$t('forms.no_actions_available')}
</td>
{/if}
</tr>
{/each}
{#each invitations as invitation}
<tr class="text-xs">
<td class="py-4 font-bold text-yellow-500">{invitation.email} </td>
<td class="py-4 font-bold text-yellow-500">{invitation.permission}</td>
{#if isAdmin(team.permissions[0].permission)}
<td class="flex-col space-y-2 py-4 text-center">
<button
class="w-52 bg-red-600 hover:bg-red-500"
on:click={() => revokeInvitation(invitation.id)}
>{$t('team.revoke_invitation')}</button
>
</td>
{:else}
<td class="text-center py-4 flex-col space-y-2">{$t('team.pending_invitation')}</td>
{/if}
</tr>
{/each}
</table>
</div>
{#if $appSession.isAdmin}
<form on:submit|preventDefault={sendInvitation} class="py-5 pt-10">
<div class="flex space-x-1">
<div class="flex space-x-1">
<div class="title font-bold">{$t('team.invite_new_member')}</div>
<button class="bg-fuchsia-600 hover:bg-fuchsia-500" type="submit"
>{$t('team.send_invitation')}</button
>
</div>
</div>
<Explainer text={$t('team.invite_only_register_explainer')} />
<div class="flex-col space-y-2 px-4 pt-5 sm:px-6">
<div class="flex space-x-0">
<input
bind:value={invitation.email}
placeholder={$t('forms.email')}
class="mr-2 w-full"
required
/>
<div class="flex-1" />
<button
on:click={() => (invitation.permission = 'read')}
class="rounded-none rounded-l border border-dashed border-transparent"
type="button"
class:border-coolgray-300={invitation.permission !== 'read'}
class:bg-fuchsia-500={invitation.permission === 'read'}>{$t('team.read')}</button
>
<button
on:click={() => (invitation.permission = 'admin')}
class="rounded-none rounded-r border border-dashed border-transparent"
type="button"
class:border-coolgray-300={invitation.permission !== 'admin'}
class:bg-red-500={invitation.permission === 'admin'}>{$t('team.admin')}</button
>
</div>
</div>
</form>
{/if}
</div>