fix: Ton of updates for users/teams
This commit is contained in:
parent
f779b3bb54
commit
b96c1a23ec
@ -32,26 +32,42 @@ export async function login({ email, password, isLogin }) {
|
||||
if (users === 0) {
|
||||
await prisma.setting.update({ where: { id }, data: { isRegistrationEnabled: false } });
|
||||
// Create default network & start Coolify Proxy
|
||||
asyncExecShell(`docker network create --attachable coolify`)
|
||||
.then(() => {
|
||||
console.log('Network created');
|
||||
})
|
||||
.catch(() => {
|
||||
console.log('Network already exists.');
|
||||
});
|
||||
|
||||
startCoolifyProxy('/var/run/docker.sock')
|
||||
.then(() => {
|
||||
console.log('Coolify Proxy started.');
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
await asyncExecShell(`docker network create --attachable coolify`);
|
||||
await startCoolifyProxy('/var/run/docker.sock');
|
||||
uid = '0';
|
||||
}
|
||||
|
||||
if (userFound) {
|
||||
if (userFound.type === 'email') {
|
||||
if (userFound.password === 'RESETME') {
|
||||
const hashedPassword = await hashPassword(password);
|
||||
if (userFound.updatedAt < new Date(Date.now() - 1000 * 60 * 10)) {
|
||||
await prisma.user.update({
|
||||
where: { email: userFound.email },
|
||||
data: { password: 'RESETTIMEOUT' }
|
||||
});
|
||||
throw {
|
||||
error: 'Password reset link has expired. Please request a new one.'
|
||||
};
|
||||
} else {
|
||||
await prisma.user.update({
|
||||
where: { email: userFound.email },
|
||||
data: { password: hashedPassword }
|
||||
});
|
||||
return {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Set-Cookie': `teamId=${uid}; HttpOnly; Path=/; Max-Age=15778800;`
|
||||
},
|
||||
body: {
|
||||
userId: userFound.id,
|
||||
teamId: userFound.id,
|
||||
permission: userFound.permission,
|
||||
isAdmin: true
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
const passwordMatch = await bcrypt.compare(password, userFound.password);
|
||||
if (!passwordMatch) {
|
||||
throw {
|
||||
|
@ -434,13 +434,12 @@
|
||||
<div class="flex flex-col space-y-4 py-2">
|
||||
<a
|
||||
sveltekit:prefetch
|
||||
href="/teams"
|
||||
class="icons tooltip-right bg-coolgray-200 hover:text-cyan-500"
|
||||
class:text-cyan-500={$page.url.pathname.startsWith('/teams')}
|
||||
class:bg-coolgray-500={$page.url.pathname.startsWith('/teams')}
|
||||
data-tooltip="Teams"
|
||||
>
|
||||
<svg
|
||||
href="/iam"
|
||||
class="icons tooltip-right bg-coolgray-200 hover:text-fuchsia-500"
|
||||
class:text-fuchsia-500={$page.url.pathname.startsWith('/iam')}
|
||||
class:bg-coolgray-500={$page.url.pathname.startsWith('/iam')}
|
||||
data-tooltip="IAM"
|
||||
><svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-8 w-8"
|
||||
viewBox="0 0 24 24"
|
||||
@ -457,6 +456,7 @@
|
||||
<path d="M21 21v-2a4 4 0 0 0 -3 -3.85" />
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
{#if $session.teamId === '0'}
|
||||
<a
|
||||
sveltekit:prefetch
|
||||
@ -484,6 +484,7 @@
|
||||
</svg>
|
||||
</a>
|
||||
{/if}
|
||||
|
||||
<div
|
||||
class="icons tooltip-right bg-coolgray-200 hover:text-red-500"
|
||||
data-tooltip="Logout"
|
||||
|
@ -47,16 +47,13 @@
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-col">
|
||||
{#if $session.teamId === '0' && ownApplications.length > 0 && otherApplications.length > 0}
|
||||
<div class="text-xl font-bold pb-5 px-1">Current Team</div>
|
||||
{/if}
|
||||
<div class="flex flex-col md:flex-row flex-wrap px-2 justify-center">
|
||||
{#each ownApplications as application}
|
||||
<Application {application} />
|
||||
{/each}
|
||||
</div>
|
||||
{#if otherApplications.length > 0 && $session.teamId === '0'}
|
||||
<div class="text-xl font-bold pb-5 pt-10 px-6">Others</div>
|
||||
<div class="text-xl font-bold pb-5 pt-10 px-6">Other Applications</div>
|
||||
<div class="flex flex-col md:flex-row flex-wrap px-2 justify-center">
|
||||
{#each otherApplications as application}
|
||||
<Application {application} />
|
||||
|
@ -52,9 +52,6 @@
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-col">
|
||||
{#if $session.teamId === '0' && ownDatabases.length > 0 && otherDatabases.length > 0}
|
||||
<div class="text-xl font-bold pb-5 px-6">Current Team</div>
|
||||
{/if}
|
||||
<div class="flex flex-col md:flex-row flex-wrap px-2 justify-center">
|
||||
{#each ownDatabases as database}
|
||||
<a href="/databases/{database.id}" class="no-underline p-2 w-96">
|
||||
@ -82,15 +79,13 @@
|
||||
<div class="font-bold text-center truncate text-red-500 group-hover:text-white">
|
||||
Configuration missing
|
||||
</div>
|
||||
{:else}
|
||||
<div class="text-center truncate">{database.type}</div>
|
||||
{/if}
|
||||
</div>
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
{#if otherDatabases.length > 0 && $session.teamId === '0'}
|
||||
<div class="text-xl font-bold pb-5 pt-10 px-6">Others</div>
|
||||
<div class="text-xl font-bold pb-5 pt-10 px-6">Other Databases</div>
|
||||
<div class="flex flex-col md:flex-row flex-wrap px-2 justify-center">
|
||||
{#each otherDatabases as database}
|
||||
<a href="/databases/{database.id}" class="no-underline p-2 w-96">
|
||||
|
@ -63,9 +63,6 @@
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-col">
|
||||
{#if $session.teamId === '0' && ownDestinations.length > 0 && otherDestinations.length > 0}
|
||||
<div class="text-xl font-bold pb-5 px-6">Current Team</div>
|
||||
{/if}
|
||||
<div class="flex flex-col md:flex-row flex-wrap px-2 justify-center">
|
||||
{#each ownDestinations as destination}
|
||||
<a href="/destinations/{destination.id}" class="no-underline p-2 w-96">
|
||||
@ -81,7 +78,7 @@
|
||||
</div>
|
||||
|
||||
{#if otherDestinations.length > 0 && $session.teamId === '0'}
|
||||
<div class="text-xl font-bold pb-5 pt-10 px-6">Others</div>
|
||||
<div class="text-xl font-bold pb-5 pt-10 px-6">Other Destinations</div>
|
||||
<div class="flex flex-col md:flex-row flex-wrap px-2 justify-center">
|
||||
{#each otherDestinations as destination}
|
||||
<a href="/destinations/{destination.id}" class="no-underline p-2 w-96">
|
||||
|
130
src/routes/iam/index.json.ts
Normal file
130
src/routes/iam/index.json.ts
Normal file
@ -0,0 +1,130 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const get: RequestHandler = async (event) => {
|
||||
const { teamId, userId, status, body } = await getUserDetails(event);
|
||||
if (status === 401) return { status, body };
|
||||
|
||||
try {
|
||||
const account = await db.prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
select: { id: true, email: true, teams: true }
|
||||
});
|
||||
let accounts = [];
|
||||
if (teamId === '0') {
|
||||
accounts = await db.prisma.user.findMany({ select: { id: true, email: true, teams: true } });
|
||||
}
|
||||
|
||||
const teams = await db.prisma.permission.findMany({
|
||||
where: { userId: teamId === '0' ? undefined : userId },
|
||||
include: { team: { include: { _count: { select: { users: true } } } } }
|
||||
});
|
||||
|
||||
const invitations = await db.prisma.teamInvitation.findMany({ where: { uid: userId } });
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
teams,
|
||||
invitations,
|
||||
account,
|
||||
accounts
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { teamId, userId, status, body } = await getUserDetails(event);
|
||||
if (status === 401) return { status, body };
|
||||
if (teamId !== '0')
|
||||
return { status: 401, body: { message: 'You are not authorized to perform this action' } };
|
||||
|
||||
const { id } = await event.request.json();
|
||||
try {
|
||||
const aloneInTeams = await db.prisma.team.findMany({ where: { users: { every: { id } } } });
|
||||
if (aloneInTeams.length > 0) {
|
||||
for (const team of aloneInTeams) {
|
||||
const applications = await db.prisma.application.findMany({
|
||||
where: { teams: { every: { id: team.id } } }
|
||||
});
|
||||
if (applications.length > 0) {
|
||||
for (const application of applications) {
|
||||
await db.prisma.application.update({
|
||||
where: { id: application.id },
|
||||
data: { teams: { connect: { id: '0' } } }
|
||||
});
|
||||
}
|
||||
}
|
||||
const services = await db.prisma.service.findMany({
|
||||
where: { teams: { every: { id: team.id } } }
|
||||
});
|
||||
if (services.length > 0) {
|
||||
for (const service of services) {
|
||||
await db.prisma.service.update({
|
||||
where: { id: service.id },
|
||||
data: { teams: { connect: { id: '0' } } }
|
||||
});
|
||||
}
|
||||
}
|
||||
const databases = await db.prisma.database.findMany({
|
||||
where: { teams: { every: { id: team.id } } }
|
||||
});
|
||||
if (databases.length > 0) {
|
||||
for (const database of databases) {
|
||||
await db.prisma.database.update({
|
||||
where: { id: database.id },
|
||||
data: { teams: { connect: { id: '0' } } }
|
||||
});
|
||||
}
|
||||
}
|
||||
const sources = await db.prisma.gitSource.findMany({
|
||||
where: { teams: { every: { id: team.id } } }
|
||||
});
|
||||
if (sources.length > 0) {
|
||||
for (const source of sources) {
|
||||
await db.prisma.gitSource.update({
|
||||
where: { id: source.id },
|
||||
data: { teams: { connect: { id: '0' } } }
|
||||
});
|
||||
}
|
||||
}
|
||||
const destinations = await db.prisma.destinationDocker.findMany({
|
||||
where: { teams: { every: { id: team.id } } }
|
||||
});
|
||||
if (destinations.length > 0) {
|
||||
for (const destination of destinations) {
|
||||
await db.prisma.destinationDocker.update({
|
||||
where: { id: destination.id },
|
||||
data: { teams: { connect: { id: '0' } } }
|
||||
});
|
||||
}
|
||||
}
|
||||
await db.prisma.teamInvitation.deleteMany({ where: { teamId: team.id } });
|
||||
await db.prisma.permission.deleteMany({ where: { teamId: team.id } });
|
||||
await db.prisma.user.delete({ where: { id } });
|
||||
await db.prisma.team.delete({ where: { id: team.id } });
|
||||
}
|
||||
}
|
||||
|
||||
const notAloneInTeams = await db.prisma.team.findMany({ where: { users: { some: { id } } } });
|
||||
if (notAloneInTeams.length > 0) {
|
||||
for (const team of notAloneInTeams) {
|
||||
await db.prisma.team.update({
|
||||
where: { id: team.id },
|
||||
data: { users: { disconnect: { id } } }
|
||||
});
|
||||
}
|
||||
}
|
||||
return {
|
||||
status: 201
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 500
|
||||
};
|
||||
}
|
||||
};
|
178
src/routes/iam/index.svelte
Normal file
178
src/routes/iam/index.svelte
Normal file
@ -0,0 +1,178 @@
|
||||
<script context="module" lang="ts">
|
||||
import type { Load } from '@sveltejs/kit';
|
||||
export const load: Load = async ({ fetch }) => {
|
||||
const url = `/iam.json`;
|
||||
const res = await fetch(url);
|
||||
|
||||
if (res.ok) {
|
||||
return {
|
||||
props: {
|
||||
...(await res.json())
|
||||
}
|
||||
};
|
||||
}
|
||||
if (res.status === 401) {
|
||||
return {
|
||||
status: 302,
|
||||
redirect: '/'
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: res.status,
|
||||
error: new Error(`Could not load ${url}`)
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { session } from '$app/stores';
|
||||
import { get, post } from '$lib/api';
|
||||
import { errorNotification } from '$lib/form';
|
||||
import { toast } from '@zerodevx/svelte-toast';
|
||||
|
||||
export let account;
|
||||
export let accounts;
|
||||
if (accounts.length === 0) {
|
||||
accounts.push(account);
|
||||
}
|
||||
export let teams;
|
||||
|
||||
const ownTeams = teams.filter((team) => {
|
||||
if (team.team.id === $session.teamId) {
|
||||
return team;
|
||||
}
|
||||
});
|
||||
const otherTeams = teams.filter((team) => {
|
||||
if (team.team.id !== $session.teamId) {
|
||||
return team;
|
||||
}
|
||||
});
|
||||
|
||||
async function resetPassword(id) {
|
||||
const sure = window.confirm('Are you sure you want to reset the password?');
|
||||
if (!sure) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await post(`/iam/password.json`, { id });
|
||||
toast.push('Password reset successfully.');
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
async function deleteUser(id) {
|
||||
const sure = window.confirm('Are you sure you want to delete this user?');
|
||||
if (!sure) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await post(`/iam.json`, { id });
|
||||
toast.push('Account deleted.');
|
||||
const data = await get('/iam.json');
|
||||
accounts = data.accounts;
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex space-x-1 p-6 font-bold">
|
||||
<div class="mr-4 text-2xl tracking-tight">Identity and Access Management System</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class="flex items-center px-6">
|
||||
<div>{account.email}</div>
|
||||
</div> -->
|
||||
<div class="mx-auto max-w-4xl px-6">
|
||||
{#if $session.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-coollabs hover:bg-coollabs-100 disabled:bg-coolgray-200"
|
||||
>Reset Password</button
|
||||
>
|
||||
</form>
|
||||
<form on:submit|preventDefault={() => deleteUser(account.id)}>
|
||||
<button
|
||||
disabled={account.id === $session.userId}
|
||||
class="mx-auto my-4 w-32 bg-coollabs hover:bg-coollabs-100 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-col flex-wrap justify-center px-2 pb-10 md:flex-row">
|
||||
{#each ownTeams as team}
|
||||
<a href="/iam/team/{team.teamId}" class="w-96 p-2 no-underline">
|
||||
<div
|
||||
class="box-selection relative"
|
||||
class:hover:bg-cyan-600={team.team?.id !== '0'}
|
||||
class:hover:bg-red-500={team.team?.id === '0'}
|
||||
>
|
||||
<div class="truncate text-center text-xl font-bold">
|
||||
{team.team.name}
|
||||
</div>
|
||||
<div class="truncate text-center font-bold">
|
||||
{team.team?.id === '0' ? 'root team' : ''}
|
||||
</div>
|
||||
|
||||
<div class="mt-1 text-center">{team.team._count.users} member(s)</div>
|
||||
</div>
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
{#if $session.teamId === '0' && otherTeams.length > 0}
|
||||
<div class="pb-5 pt-10 text-xl font-bold">Other Teams</div>
|
||||
{/if}
|
||||
<div class="flex flex-col flex-wrap justify-center px-2 md:flex-row">
|
||||
{#each otherTeams as team}
|
||||
<a href="/iam/team/{team.teamId}" class="w-96 p-2 no-underline">
|
||||
<div
|
||||
class="box-selection relative"
|
||||
class:hover:bg-cyan-600={team.team?.id !== '0'}
|
||||
class:hover:bg-red-500={team.team?.id === '0'}
|
||||
>
|
||||
<div class="truncate text-center text-xl font-bold">
|
||||
{team.team.name}
|
||||
</div>
|
||||
<div class="truncate text-center font-bold">
|
||||
{team.team?.id === '0' ? 'root team' : ''}
|
||||
</div>
|
||||
|
||||
<div class="mt-1 text-center">{team.team._count.users} member(s)</div>
|
||||
</div>
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
22
src/routes/iam/password.json.ts
Normal file
22
src/routes/iam/password.json.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const post: RequestHandler = async (event) => {
|
||||
const { teamId, userId, status, body } = await getUserDetails(event);
|
||||
if (status === 401) return { status, body };
|
||||
|
||||
const { id } = await event.request.json();
|
||||
try {
|
||||
await db.prisma.user.update({ where: { id }, data: { password: 'RESETME' } });
|
||||
return {
|
||||
status: 201
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return {
|
||||
status: 500
|
||||
};
|
||||
}
|
||||
};
|
@ -1,14 +1,14 @@
|
||||
<script context="module" lang="ts">
|
||||
import type { Load } from '@sveltejs/kit';
|
||||
export const load: Load = async ({ fetch, params }) => {
|
||||
const url = `/teams/${params.id}.json`;
|
||||
const url = `/iam/team/${params.id}.json`;
|
||||
const res = await fetch(url);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (!data.permissions || Object.entries(data.permissions).length === 0) {
|
||||
return {
|
||||
status: 302,
|
||||
redirect: '/teams'
|
||||
redirect: '/iam'
|
||||
};
|
||||
}
|
||||
return {
|
||||
@ -20,7 +20,7 @@
|
||||
|
||||
return {
|
||||
status: 302,
|
||||
redirect: '/teams'
|
||||
redirect: '/iam'
|
||||
};
|
||||
};
|
||||
</script>
|
@ -1,7 +1,7 @@
|
||||
<script context="module" lang="ts">
|
||||
import type { Load } from '@sveltejs/kit';
|
||||
export const load: Load = async ({ fetch, params }) => {
|
||||
const url = `/teams/${params.id}.json`;
|
||||
const url = `/iam/team/${params.id}.json`;
|
||||
const res = await fetch(url);
|
||||
|
||||
if (res.ok) {
|
||||
@ -44,7 +44,7 @@
|
||||
|
||||
async function sendInvitation() {
|
||||
try {
|
||||
await post(`/teams/${id}/invitation/invite.json`, {
|
||||
await post(`/iam/team/${id}/invitation/invite.json`, {
|
||||
teamId: team.id,
|
||||
teamName: invitation.teamName,
|
||||
email: invitation.email.toLowerCase(),
|
||||
@ -57,7 +57,7 @@
|
||||
}
|
||||
async function revokeInvitation(id: string) {
|
||||
try {
|
||||
await post(`/teams/${id}/invitation/revoke.json`, { id });
|
||||
await post(`/iam/team/${id}/invitation/revoke.json`, { id });
|
||||
return window.location.reload();
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
@ -65,7 +65,7 @@
|
||||
}
|
||||
async function removeFromTeam(uid: string) {
|
||||
try {
|
||||
await post(`/teams/${id}/remove/user.json`, { teamId: team.id, uid });
|
||||
await post(`/iam/team/${id}/remove/user.json`, { teamId: team.id, uid });
|
||||
return window.location.reload();
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
@ -77,7 +77,7 @@
|
||||
newPermission = 'admin';
|
||||
}
|
||||
try {
|
||||
await post(`/teams/${id}/permission/change.json`, { userId, newPermission, permissionId });
|
||||
await post(`/iam/team/${id}/permission/change.json`, { userId, newPermission, permissionId });
|
||||
return window.location.reload();
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
@ -85,7 +85,7 @@
|
||||
}
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
await post(`/teams/${id}.json`, { ...team });
|
||||
await post(`/iam/team/${id}.json`, { ...team });
|
||||
return window.location.reload();
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
@ -57,9 +57,6 @@
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-col">
|
||||
{#if $session.teamId === '0' && ownServices.length > 0 && otherServices.length > 0}
|
||||
<div class="text-xl font-bold pb-5 px-6">Current Team</div>
|
||||
{/if}
|
||||
<div class="flex flex-col md:flex-row flex-wrap px-2 justify-center">
|
||||
{#each ownServices as service}
|
||||
<a href="/services/{service.id}" class="no-underline p-2 w-96">
|
||||
@ -97,15 +94,13 @@
|
||||
<div class="font-bold text-center truncate text-red-500 group-hover:text-white">
|
||||
Configuration missing
|
||||
</div>
|
||||
{:else}
|
||||
<div class="text-center truncate">{service.type}</div>
|
||||
{/if}
|
||||
</div>
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
{#if otherServices.length > 0 && $session.teamId === '0'}
|
||||
<div class="text-xl font-bold pb-5 pt-10 px-6">Others</div>
|
||||
<div class="text-xl font-bold pb-5 pt-10 px-6">Other Services</div>
|
||||
<div class="flex flex-col md:flex-row flex-wrap px-2 justify-center">
|
||||
{#each otherServices as service}
|
||||
<a href="/services/{service.id}" class="no-underline p-2 w-96">
|
||||
|
@ -61,9 +61,6 @@
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-col">
|
||||
{#if $session.teamId === '0' && ownSources.length > 0 && otherSources.length > 0}
|
||||
<div class="text-xl font-bold pb-5 px-6">Current Team</div>
|
||||
{/if}
|
||||
<div class="flex flex-col md:flex-row flex-wrap px-2 justify-center">
|
||||
{#each ownSources as source}
|
||||
<a href="/sources/{source.id}" class="no-underline p-2 w-96">
|
||||
@ -90,7 +87,7 @@
|
||||
</div>
|
||||
|
||||
{#if otherSources.length > 0 && $session.teamId === '0'}
|
||||
<div class="text-xl font-bold pb-5 pt-10 px-6">Others</div>
|
||||
<div class="text-xl font-bold pb-5 pt-10 px-6">Other Srouces</div>
|
||||
<div class="flex flex-col md:flex-row flex-wrap px-2 justify-center">
|
||||
{#each otherSources as source}
|
||||
<a href="/sources/{source.id}" class="no-underline p-2 w-96">
|
||||
|
@ -1,27 +0,0 @@
|
||||
import { getUserDetails } from '$lib/common';
|
||||
import * as db from '$lib/database';
|
||||
import { ErrorHandler } from '$lib/database';
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
|
||||
export const get: RequestHandler = async (event) => {
|
||||
const { teamId, userId, status, body } = await getUserDetails(event, false);
|
||||
if (status === 401) return { status, body };
|
||||
|
||||
try {
|
||||
const teams = await db.prisma.permission.findMany({
|
||||
where: { userId: teamId === '0' ? undefined : userId },
|
||||
include: { team: { include: { _count: { select: { users: true } } } } }
|
||||
});
|
||||
|
||||
const invitations = await db.prisma.teamInvitation.findMany({ where: { uid: userId } });
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
teams,
|
||||
invitations
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
return ErrorHandler(error);
|
||||
}
|
||||
};
|
@ -1,146 +0,0 @@
|
||||
<script context="module" lang="ts">
|
||||
import type { Load } from '@sveltejs/kit';
|
||||
export const load: Load = async ({ fetch }) => {
|
||||
const url = `/teams.json`;
|
||||
const res = await fetch(url);
|
||||
|
||||
if (res.ok) {
|
||||
return {
|
||||
props: {
|
||||
...(await res.json())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: res.status,
|
||||
error: new Error(`Could not load ${url}`)
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { errorNotification } from '$lib/form';
|
||||
import { session } from '$app/stores';
|
||||
import { post } from '$lib/api';
|
||||
|
||||
export let teams;
|
||||
export let invitations;
|
||||
|
||||
async function acceptInvitation(id, teamId) {
|
||||
try {
|
||||
await post(`/teams/${teamId}/invitation/accept.json`, { id });
|
||||
return window.location.reload();
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
async function revokeInvitation(id, teamId) {
|
||||
try {
|
||||
await post(`/teams/${teamId}/invitation/revoke.json`, { id });
|
||||
return window.location.reload();
|
||||
} catch ({ error }) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
const ownTeams = teams.filter((team) => {
|
||||
if (team.team.id === $session.teamId) {
|
||||
return team;
|
||||
}
|
||||
});
|
||||
const otherTeams = teams.filter((team) => {
|
||||
if (team.team.id !== $session.teamId) {
|
||||
return team;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="flex space-x-1 p-6 font-bold">
|
||||
<div class="mr-4 text-2xl tracking-tight">Teams</div>
|
||||
{#if $session.isAdmin}
|
||||
<a href="/new/team" class="add-icon bg-cyan-600 hover:bg-cyan-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
|
||||
>
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if invitations.length > 0}
|
||||
<div class="mx-auto max-w-2xl pb-10">
|
||||
<div class="flex space-x-1 p-6 font-bold">
|
||||
<div class="title">Pending invitations</div>
|
||||
</div>
|
||||
<div class="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="flex flex-wrap justify-center">
|
||||
<div class="flex flex-col">
|
||||
<div class=" px-6 pb-5 text-xl font-bold">Current Team</div>
|
||||
<div class="flex flex-col flex-wrap justify-center px-2 md:flex-row">
|
||||
{#each ownTeams as team}
|
||||
<a href="/teams/{team.teamId}" class="w-96 p-2 no-underline">
|
||||
<div
|
||||
class="box-selection relative"
|
||||
class:hover:bg-cyan-600={team.team?.id !== '0'}
|
||||
class:hover:bg-red-500={team.team?.id === '0'}
|
||||
>
|
||||
<div class="truncate text-center text-xl font-bold">
|
||||
{team.team.name}
|
||||
{team.team?.id === '0' ? '(admin team)' : ''}
|
||||
</div>
|
||||
|
||||
<div class="mt-1 text-center">{team.team._count.users} member(s)</div>
|
||||
</div>
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="px-6 pb-5 pt-10 text-xl font-bold">Others</div>
|
||||
<div class="flex flex-col flex-wrap justify-center px-2 md:flex-row">
|
||||
{#each otherTeams as team}
|
||||
<a href="/teams/{team.teamId}" class="w-96 p-2 no-underline">
|
||||
<div
|
||||
class="box-selection relative"
|
||||
class:hover:bg-cyan-600={team.team?.id !== '0'}
|
||||
class:hover:bg-red-500={team.team?.id === '0'}
|
||||
>
|
||||
<div class="truncate text-center text-xl font-bold">
|
||||
{team.team.name}
|
||||
{team.team?.id === '0' ? '(admin team)' : ''}
|
||||
</div>
|
||||
|
||||
<div class="mt-1 text-center">{team.team._count.users} member(s)</div>
|
||||
</div>
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
Loading…
x
Reference in New Issue
Block a user