9 Commits

Author SHA1 Message Date
8eb5eb3161 Merge branch 'docmost:main' into Merged-Downstream 2024-09-17 10:08:55 +10:00
fb27282886 feat: delete space and edit space slug (#307)
* feat: make space slug editable

* feat: delete space

* client
2024-09-16 17:43:40 +01:00
9e8a3681d6 Merge branch 'Table-of-Contents' into Merged-Downstream 2024-09-16 09:59:40 +10:00
38f66eaab5 Merge branch 'SMTP-IgnoreTLS' into Merged-Downstream 2024-09-16 09:59:34 +10:00
6ad469a115 Minimum viable NTLM auth implementation
Added env variable "VITE_NTLM_AUTH", if true, login page will attempt NTLM auth challenge instead of showing login page.

If challenge is successful and an authenticate message is received, it will check for the existence of the user using the provided mail attribute, and create an account with a random, complex password, and then authenticate as the user.
2024-09-16 08:32:33 +10:00
dea9f4c063 remove unnecessary log 2024-09-13 22:37:38 +01:00
0b6730c06f fix page export failure when title contains non-ASCII characters (#309) 2024-09-13 17:40:24 +01:00
9d0331d04f Create Auth/NTLM Endpoint
- Adds NTLM challenge negotiation
- Checks NTLM auth user & domain against AD / LDAP and returns info
- Adds relevant .env entries
2024-09-13 08:37:25 +10:00
0bfd3b6771 Implement nodemailer ignore tls property 2024-09-11 11:23:38 +10:00
34 changed files with 1009 additions and 182 deletions

View File

@ -7,6 +7,18 @@ APP_SECRET=REPLACE_WITH_LONG_SECRET
JWT_TOKEN_EXPIRES_IN=30d
# Use NTLM for user authentication, exposes to browser
VITE_NTLM_AUTH=false
# LDAP settings for NTLM authentication
LDAP_BASEDN=
LDAP_DOMAINSUFFIX=
LDAP_USERNAME=
LDAP_PASSWORD=
# User object attributes for docmost
LDAP_NAMEATTRIBUTE=
LDAP_MAILATTRIBUTE=
DATABASE_URL="postgresql://postgres:password@localhost:5432/docmost?schema=public"
REDIS_URL=redis://127.0.0.1:6379
@ -32,6 +44,7 @@ SMTP_PORT=587
SMTP_USERNAME=
SMTP_PASSWORD=
SMTP_SECURE=false
SMTP_IGNORETLS=false
# Postmark driver config
POSTMARK_TOKEN=

View File

@ -5,14 +5,15 @@ import {
Badge,
Table,
ScrollArea,
} from "@mantine/core";
import { Link } from "react-router-dom";
import PageListSkeleton from "@/components/ui/page-list-skeleton.tsx";
import { buildPageUrl } from "@/features/page/page.utils.ts";
import { formattedDate } from "@/lib/time.ts";
import { useRecentChangesQuery } from "@/features/page/queries/page-query.ts";
import { IconFileDescription } from "@tabler/icons-react";
import { getSpaceUrl } from "@/lib/config.ts";
ActionIcon,
} from '@mantine/core';
import { Link } from 'react-router-dom';
import PageListSkeleton from '@/components/ui/page-list-skeleton.tsx';
import { buildPageUrl } from '@/features/page/page.utils.ts';
import { formattedDate } from '@/lib/time.ts';
import { useRecentChangesQuery } from '@/features/page/queries/page-query.ts';
import { IconFileDescription } from '@tabler/icons-react';
import { getSpaceUrl } from '@/lib/config.ts';
interface Props {
spaceId?: string;
@ -40,10 +41,14 @@ export default function RecentChanges({ spaceId }: Props) {
to={buildPageUrl(page?.space.slug, page.slugId, page.title)}
>
<Group wrap="nowrap">
{page.icon || <IconFileDescription size={18} />}
{page.icon || (
<ActionIcon variant='transparent' color='gray' size={18}>
<IconFileDescription size={18} />
</ActionIcon>
)}
<Text fw={500} size="md" lineClamp={1}>
{page.title || "Untitled"}
{page.title || 'Untitled'}
</Text>
</Group>
</UnstyledButton>
@ -55,7 +60,7 @@ export default function RecentChanges({ spaceId }: Props) {
variant="light"
component={Link}
to={getSpaceUrl(page?.space.slug)}
style={{ cursor: "pointer" }}
style={{ cursor: 'pointer' }}
>
{page?.space.name}
</Badge>

View File

@ -1,5 +1,5 @@
import { useState } from "react";
import { login, setupWorkspace } from "@/features/auth/services/auth-service";
import { login, ntlmLogin, setupWorkspace } from "@/features/auth/services/auth-service";
import { useNavigate } from "react-router-dom";
import { useAtom } from "jotai";
import { authTokensAtom } from "@/features/auth/atoms/auth-tokens-atom";
@ -38,6 +38,25 @@ export default function useAuth() {
}
};
const handleNtlmSignIn = async () => {
setIsLoading(true);
try {
const res = await ntlmLogin();
setIsLoading(false);
setAuthToken(res.tokens);
navigate(APP_ROUTE.HOME);
} catch (err) {
console.log(err);
setIsLoading(false);
notifications.show({
message: err.response?.data.message,
color: "red",
});
}
};
const handleInvitationSignUp = async (data: IAcceptInvite) => {
setIsLoading(true);
@ -107,6 +126,7 @@ export default function useAuth() {
return {
signIn: handleSignIn,
ntlmSignIn: handleNtlmSignIn,
invitationSignup: handleInvitationSignUp,
setupWorkspace: handleSetupWorkspace,
isAuthenticated: handleIsAuthenticated,

View File

@ -6,12 +6,19 @@ import {
ISetupWorkspace,
ITokenResponse,
} from "@/features/auth/types/auth.types";
import axios from "axios";
export async function login(data: ILogin): Promise<ITokenResponse> {
const req = await api.post<ITokenResponse>("/auth/login", data);
return req.data;
}
export async function ntlmLogin(): Promise<ITokenResponse> {
// Use separate axios instance to avoid passing app auth headers to allow for NTLM authentication challenge
const req = await axios.post<ITokenResponse>("/api/auth/ntlm");
return req.data;
}
/*
export async function register(data: IRegister): Promise<ITokenResponse> {
const req = await api.post<ITokenResponse>("/auth/register", data);

View File

@ -3,8 +3,8 @@ import {
useQuery,
useQueryClient,
UseQueryResult,
} from "@tanstack/react-query";
import { IGroup } from "@/features/group/types/group.types";
} from '@tanstack/react-query';
import { IGroup } from '@/features/group/types/group.types';
import {
addGroupMember,
createGroup,
@ -14,22 +14,22 @@ import {
getGroups,
removeGroupMember,
updateGroup,
} from "@/features/group/services/group-service";
import { notifications } from "@mantine/notifications";
import { QueryParams } from "@/lib/types.ts";
} from '@/features/group/services/group-service';
import { notifications } from '@mantine/notifications';
import { QueryParams } from '@/lib/types.ts';
export function useGetGroupsQuery(
params?: QueryParams,
params?: QueryParams
): UseQueryResult<any, Error> {
return useQuery({
queryKey: ["groups", params],
queryKey: ['groups', params],
queryFn: () => getGroups(params),
});
}
export function useGroupQuery(groupId: string): UseQueryResult<IGroup, Error> {
return useQuery({
queryKey: ["groups", groupId],
queryKey: ['groups', groupId],
queryFn: () => getGroupById(groupId),
enabled: !!groupId,
});
@ -37,7 +37,7 @@ export function useGroupQuery(groupId: string): UseQueryResult<IGroup, Error> {
export function useGroupMembersQuery(groupId: string) {
return useQuery({
queryKey: ["groupMembers", groupId],
queryKey: ['groupMembers', groupId],
queryFn: () => getGroupMembers(groupId),
enabled: !!groupId,
});
@ -47,10 +47,10 @@ export function useCreateGroupMutation() {
return useMutation<IGroup, Error, Partial<IGroup>>({
mutationFn: (data) => createGroup(data),
onSuccess: () => {
notifications.show({ message: "Group created successfully" });
notifications.show({ message: 'Group created successfully' });
},
onError: () => {
notifications.show({ message: "Failed to create group", color: "red" });
notifications.show({ message: 'Failed to create group', color: 'red' });
},
});
}
@ -61,14 +61,14 @@ export function useUpdateGroupMutation() {
return useMutation<IGroup, Error, Partial<IGroup>>({
mutationFn: (data) => updateGroup(data),
onSuccess: (data, variables) => {
notifications.show({ message: "Group updated successfully" });
notifications.show({ message: 'Group updated successfully' });
queryClient.invalidateQueries({
queryKey: ["group", variables.groupId],
queryKey: ['group', variables.groupId],
});
},
onError: (error) => {
const errorMessage = error["response"]?.data?.message;
notifications.show({ message: errorMessage, color: "red" });
const errorMessage = error['response']?.data?.message;
notifications.show({ message: errorMessage, color: 'red' });
},
});
}
@ -79,17 +79,19 @@ export function useDeleteGroupMutation() {
return useMutation({
mutationFn: (groupId: string) => deleteGroup({ groupId }),
onSuccess: (data, variables) => {
notifications.show({ message: "Group deleted successfully" });
notifications.show({ message: 'Group deleted successfully' });
const groups = queryClient.getQueryData(["groups"]) as any;
const groups = queryClient.getQueryData(['groups']) as any;
if (groups) {
groups.items?.filter((group: IGroup) => group.id !== variables);
queryClient.setQueryData(["groups"], groups);
groups.items = groups.items?.filter(
(group: IGroup) => group.id !== variables
);
queryClient.setQueryData(['groups'], groups);
}
},
onError: (error) => {
const errorMessage = error["response"]?.data?.message;
notifications.show({ message: errorMessage, color: "red" });
const errorMessage = error['response']?.data?.message;
notifications.show({ message: errorMessage, color: 'red' });
},
});
}
@ -100,15 +102,15 @@ export function useAddGroupMemberMutation() {
return useMutation<void, Error, { groupId: string; userIds: string[] }>({
mutationFn: (data) => addGroupMember(data),
onSuccess: (data, variables) => {
notifications.show({ message: "Added successfully" });
notifications.show({ message: 'Added successfully' });
queryClient.invalidateQueries({
queryKey: ["groupMembers", variables.groupId],
queryKey: ['groupMembers', variables.groupId],
});
},
onError: () => {
notifications.show({
message: "Failed to add group members",
color: "red",
message: 'Failed to add group members',
color: 'red',
});
},
});
@ -127,14 +129,14 @@ export function useRemoveGroupMemberMutation() {
>({
mutationFn: (data) => removeGroupMember(data),
onSuccess: (data, variables) => {
notifications.show({ message: "Removed successfully" });
notifications.show({ message: 'Removed successfully' });
queryClient.invalidateQueries({
queryKey: ["groupMembers", variables.groupId],
queryKey: ['groupMembers', variables.groupId],
});
},
onError: (error) => {
const errorMessage = error["response"]?.data?.message;
notifications.show({ message: errorMessage, color: "red" });
const errorMessage = error['response']?.data?.message;
notifications.show({ message: errorMessage, color: 'red' });
},
});
}

View File

@ -64,7 +64,7 @@ export async function exportPage(data: IExportPageParams): Promise<void> {
.split("filename=")[1]
.replace(/"/g, "");
saveAs(req.data, fileName);
saveAs(req.data, decodeURIComponent(fileName));
}
export async function importPage(file: File, spaceId: string) {
@ -81,14 +81,17 @@ export async function importPage(file: File, spaceId: string) {
return req.data;
}
export async function uploadFile(file: File, pageId: string, attachmentId?: string): Promise<IAttachment> {
export async function uploadFile(
file: File,
pageId: string,
attachmentId?: string,
): Promise<IAttachment> {
const formData = new FormData();
if(attachmentId){
if (attachmentId) {
formData.append("attachmentId", attachmentId);
}
formData.append("pageId", pageId);
formData.append("file", file);
const req = await api.post<IAttachment>("/files/upload", formData, {
headers: {

View File

@ -0,0 +1,86 @@
import { Button, Divider, Group, Modal, Text, TextInput } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import { useDeleteSpaceMutation } from '../queries/space-query';
import { useField } from '@mantine/form';
import { ISpace } from '../types/space.types';
import { useNavigate } from 'react-router-dom';
import APP_ROUTE from '@/lib/app-route';
interface DeleteSpaceModalProps {
space: ISpace;
}
export default function DeleteSpaceModal({ space }: DeleteSpaceModalProps) {
const [opened, { open, close }] = useDisclosure(false);
const deleteSpaceMutation = useDeleteSpaceMutation();
const navigate = useNavigate();
const confirmNameField = useField({
initialValue: '',
validateOnChange: true,
validate: (value) =>
value.trim().toLowerCase() === space.name.trim().toLocaleLowerCase()
? null
: 'Names do not match',
});
const handleDelete = async () => {
if (
confirmNameField.getValue().trim().toLowerCase() !==
space.name.trim().toLowerCase()
) {
confirmNameField.validate();
return;
}
try {
// pass slug too so we can clear the local cache
await deleteSpaceMutation.mutateAsync({ id: space.id, slug: space.slug });
navigate(APP_ROUTE.HOME);
} catch (error) {
console.error('Failed to delete space', error);
}
};
return (
<>
<Button onClick={open} variant="light" color="red">
Delete
</Button>
<Modal
opened={opened}
onClose={close}
title="Are you sure you want to delete this space?"
>
<Divider size="xs" mb="xs" />
<Text>
All pages, comments, attachments and permissions in this space will be
deleted irreversibly.
</Text>
<Text mt="sm">
Type the space name{' '}
<Text span fw={500}>
'{space.name}'
</Text>{' '}
to confirm your action.
</Text>
<TextInput
{...confirmNameField.getInputProps()}
variant="filled"
placeholder="Confirm space name"
py="sm"
data-autofocus
/>
<Group justify="flex-end" mt="md">
<Button onClick={close} variant="default">
Cancel
</Button>
<Button onClick={handleDelete} color="red">
Confirm
</Button>
</Group>
</Modal>
</>
);
}

View File

@ -8,6 +8,14 @@ import { ISpace } from "@/features/space/types/space.types.ts";
const formSchema = z.object({
name: z.string().min(2).max(50),
description: z.string().max(250),
slug: z
.string()
.min(2)
.max(50)
.regex(
/^[a-zA-Z0-9]+$/,
"Space slug must be alphanumeric. No special characters",
),
});
type FormValues = z.infer<typeof formSchema>;
@ -23,12 +31,14 @@ export function EditSpaceForm({ space, readOnly }: EditSpaceFormProps) {
initialValues: {
name: space?.name,
description: space?.description || "",
slug: space.slug,
},
});
const handleSubmit = async (values: {
name?: string;
description?: string;
slug?: string;
}) => {
const spaceData: Partial<ISpace> = {
spaceId: space.id,
@ -40,6 +50,10 @@ export function EditSpaceForm({ space, readOnly }: EditSpaceFormProps) {
spaceData.description = values.description;
}
if (form.isDirty("slug")) {
spaceData.slug = values.slug;
}
await updateSpaceMutation.mutateAsync(spaceData);
form.resetDirty();
};
@ -62,8 +76,8 @@ export function EditSpaceForm({ space, readOnly }: EditSpaceFormProps) {
id="slug"
label="Slug"
variant="filled"
readOnly
value={space.slug}
readOnly={readOnly}
{...form.getInputProps("slug")}
/>
<Textarea

View File

@ -1,7 +1,8 @@
import React from "react";
import { useSpaceQuery } from "@/features/space/queries/space-query.ts";
import { EditSpaceForm } from "@/features/space/components/edit-space-form.tsx";
import { Text } from "@mantine/core";
import React from 'react';
import { useSpaceQuery } from '@/features/space/queries/space-query.ts';
import { EditSpaceForm } from '@/features/space/components/edit-space-form.tsx';
import { Divider, Group, Text } from '@mantine/core';
import DeleteSpaceModal from './delete-space-modal';
interface SpaceDetailsProps {
spaceId: string;
@ -18,6 +19,23 @@ export default function SpaceDetails({ spaceId, readOnly }: SpaceDetailsProps) {
Details
</Text>
<EditSpaceForm space={space} readOnly={readOnly} />
{!readOnly && (
<>
<Divider my="lg" />
<Group justify="space-between" wrap="nowrap" gap="xl">
<div>
<Text size="md">Delete space</Text>
<Text size="sm" c="dimmed">
Delete this space with all its pages and data.
</Text>
</div>
<DeleteSpaceModal space={space} />
</Group>
</>
)}
</div>
)}
</>

View File

@ -3,14 +3,14 @@ import {
useQuery,
useQueryClient,
UseQueryResult,
} from "@tanstack/react-query";
} from '@tanstack/react-query';
import {
IAddSpaceMember,
IChangeSpaceMemberRole,
IRemoveSpaceMember,
ISpace,
ISpaceMember,
} from "@/features/space/types/space.types";
} from '@/features/space/types/space.types';
import {
addSpaceMember,
changeMemberRole,
@ -20,23 +20,24 @@ import {
removeSpaceMember,
createSpace,
updateSpace,
} from "@/features/space/services/space-service.ts";
import { notifications } from "@mantine/notifications";
import { IPagination } from "@/lib/types.ts";
deleteSpace,
} from '@/features/space/services/space-service.ts';
import { notifications } from '@mantine/notifications';
import { IPagination } from '@/lib/types.ts';
export function useGetSpacesQuery(): UseQueryResult<
IPagination<ISpace>,
Error
> {
return useQuery({
queryKey: ["spaces"],
queryKey: ['spaces'],
queryFn: () => getSpaces(),
});
}
export function useSpaceQuery(spaceId: string): UseQueryResult<ISpace, Error> {
return useQuery({
queryKey: ["spaces", spaceId],
queryKey: ['spaces', spaceId],
queryFn: () => getSpaceById(spaceId),
enabled: !!spaceId,
staleTime: 5 * 60 * 1000,
@ -50,22 +51,22 @@ export function useCreateSpaceMutation() {
mutationFn: (data) => createSpace(data),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ["spaces"],
queryKey: ['spaces'],
});
notifications.show({ message: "Space created successfully" });
notifications.show({ message: 'Space created successfully' });
},
onError: (error) => {
const errorMessage = error["response"]?.data?.message;
notifications.show({ message: errorMessage, color: "red" });
const errorMessage = error['response']?.data?.message;
notifications.show({ message: errorMessage, color: 'red' });
},
});
}
export function useGetSpaceBySlugQuery(
spaceId: string,
spaceId: string
): UseQueryResult<ISpace, Error> {
return useQuery({
queryKey: ["spaces", spaceId],
queryKey: ['spaces', spaceId],
queryFn: () => getSpaceById(spaceId),
enabled: !!spaceId,
staleTime: 5 * 60 * 1000,
@ -78,34 +79,64 @@ export function useUpdateSpaceMutation() {
return useMutation<ISpace, Error, Partial<ISpace>>({
mutationFn: (data) => updateSpace(data),
onSuccess: (data, variables) => {
notifications.show({ message: "Space updated successfully" });
notifications.show({ message: 'Space updated successfully' });
const space = queryClient.getQueryData([
"space",
'space',
variables.spaceId,
]) as ISpace;
if (space) {
const updatedSpace = { ...space, ...data };
queryClient.setQueryData(["space", variables.spaceId], updatedSpace);
queryClient.setQueryData(["space", data.slug], updatedSpace);
queryClient.setQueryData(['space', variables.spaceId], updatedSpace);
queryClient.setQueryData(['space', data.slug], updatedSpace);
}
queryClient.invalidateQueries({
queryKey: ["spaces"],
queryKey: ['spaces'],
});
},
onError: (error) => {
const errorMessage = error["response"]?.data?.message;
notifications.show({ message: errorMessage, color: "red" });
const errorMessage = error['response']?.data?.message;
notifications.show({ message: errorMessage, color: 'red' });
},
});
}
export function useDeleteSpaceMutation() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data: Partial<ISpace>) => deleteSpace(data.id),
onSuccess: (data, variables) => {
notifications.show({ message: 'Space deleted successfully' });
if (variables.slug) {
queryClient.removeQueries({
queryKey: ['spaces', variables.slug],
exact: true,
});
}
const spaces = queryClient.getQueryData(['spaces']) as any;
if (spaces) {
spaces.items = spaces.items?.filter(
(space: ISpace) => space.id !== variables.id
);
queryClient.setQueryData(['spaces'], spaces);
}
},
onError: (error) => {
const errorMessage = error['response']?.data?.message;
notifications.show({ message: errorMessage, color: 'red' });
},
});
}
export function useSpaceMembersQuery(
spaceId: string,
spaceId: string
): UseQueryResult<IPagination<ISpaceMember>, Error> {
return useQuery({
queryKey: ["spaceMembers", spaceId],
queryKey: ['spaceMembers', spaceId],
queryFn: () => getSpaceMembers(spaceId),
enabled: !!spaceId,
});
@ -117,14 +148,14 @@ export function useAddSpaceMemberMutation() {
return useMutation<void, Error, IAddSpaceMember>({
mutationFn: (data) => addSpaceMember(data),
onSuccess: (data, variables) => {
notifications.show({ message: "Members added successfully" });
notifications.show({ message: 'Members added successfully' });
queryClient.invalidateQueries({
queryKey: ["spaceMembers", variables.spaceId],
queryKey: ['spaceMembers', variables.spaceId],
});
},
onError: (error) => {
const errorMessage = error["response"]?.data?.message;
notifications.show({ message: errorMessage, color: "red" });
const errorMessage = error['response']?.data?.message;
notifications.show({ message: errorMessage, color: 'red' });
},
});
}
@ -135,14 +166,14 @@ export function useRemoveSpaceMemberMutation() {
return useMutation<void, Error, IRemoveSpaceMember>({
mutationFn: (data) => removeSpaceMember(data),
onSuccess: (data, variables) => {
notifications.show({ message: "Removed successfully" });
notifications.show({ message: 'Removed successfully' });
queryClient.refetchQueries({
queryKey: ["spaceMembers", variables.spaceId],
queryKey: ['spaceMembers', variables.spaceId],
});
},
onError: (error) => {
const errorMessage = error["response"]?.data?.message;
notifications.show({ message: errorMessage, color: "red" });
const errorMessage = error['response']?.data?.message;
notifications.show({ message: errorMessage, color: 'red' });
},
});
}
@ -153,15 +184,15 @@ export function useChangeSpaceMemberRoleMutation() {
return useMutation<void, Error, IChangeSpaceMemberRole>({
mutationFn: (data) => changeMemberRole(data),
onSuccess: (data, variables) => {
notifications.show({ message: "Member role updated successfully" });
notifications.show({ message: 'Member role updated successfully' });
// due to pagination levels, change in cache instead
queryClient.refetchQueries({
queryKey: ["spaceMembers", variables.spaceId],
queryKey: ['spaceMembers', variables.spaceId],
});
},
onError: (error) => {
const errorMessage = error["response"]?.data?.message;
notifications.show({ message: errorMessage, color: "red" });
const errorMessage = error['response']?.data?.message;
notifications.show({ message: errorMessage, color: 'red' });
},
});
}

View File

@ -1,52 +1,56 @@
import api from "@/lib/api-client";
import api from '@/lib/api-client';
import {
IAddSpaceMember,
IChangeSpaceMemberRole,
IRemoveSpaceMember,
ISpace,
} from "@/features/space/types/space.types";
import { IPagination } from "@/lib/types.ts";
import { IUser } from "@/features/user/types/user.types.ts";
} from '@/features/space/types/space.types';
import { IPagination } from '@/lib/types.ts';
import { IUser } from '@/features/user/types/user.types.ts';
export async function getSpaces(): Promise<IPagination<ISpace>> {
const req = await api.post("/spaces");
const req = await api.post('/spaces');
return req.data;
}
export async function getSpaceById(spaceId: string): Promise<ISpace> {
const req = await api.post<ISpace>("/spaces/info", { spaceId });
const req = await api.post<ISpace>('/spaces/info', { spaceId });
return req.data;
}
export async function createSpace(data: Partial<ISpace>): Promise<ISpace> {
const req = await api.post<ISpace>("/spaces/create", data);
const req = await api.post<ISpace>('/spaces/create', data);
return req.data;
}
export async function updateSpace(data: Partial<ISpace>): Promise<ISpace> {
const req = await api.post<ISpace>("/spaces/update", data);
const req = await api.post<ISpace>('/spaces/update', data);
return req.data;
}
export async function deleteSpace(spaceId: string): Promise<void> {
await api.post<void>('/spaces/delete', { spaceId });
}
export async function getSpaceMembers(
spaceId: string,
spaceId: string
): Promise<IPagination<IUser>> {
const req = await api.post<any>("/spaces/members", { spaceId });
const req = await api.post<any>('/spaces/members', { spaceId });
return req.data;
}
export async function addSpaceMember(data: IAddSpaceMember): Promise<void> {
await api.post("/spaces/members/add", data);
await api.post('/spaces/members/add', data);
}
export async function removeSpaceMember(
data: IRemoveSpaceMember,
data: IRemoveSpaceMember
): Promise<void> {
await api.post("/spaces/members/remove", data);
await api.post('/spaces/members/remove', data);
}
export async function changeMemberRole(
data: IChangeSpaceMemberRole,
data: IChangeSpaceMemberRole
): Promise<void> {
await api.post("/spaces/members/change-role", data);
await api.post('/spaces/members/change-role', data);
}

View File

@ -1,13 +1,28 @@
import { LoginForm } from "@/features/auth/components/login-form";
import useAuth from "@/features/auth/hooks/use-auth";
import { useEffect } from "react";
import { Helmet } from "react-helmet-async";
const ntlmAuth = import.meta.env.VITE_NTLM_AUTH;
export default function LoginPage() {
const { ntlmSignIn } = useAuth();
useEffect(() => {
if (ntlmAuth)
ntlmSignIn();
}, [])
return (
<>
<Helmet>
<title>Login</title>
</Helmet>
<LoginForm />
{!ntlmAuth && <LoginForm />}
</>
);
}

View File

@ -1,20 +1,34 @@
import { createTheme, MantineColorsTuple } from "@mantine/core";
import { createTheme, MantineColorsTuple } from '@mantine/core';
const blue: MantineColorsTuple = [
"#e7f3ff",
"#d0e4ff",
"#a1c6fa",
"#6ea6f6",
"#458bf2",
"#2b7af1",
"#0b60d8", //
"#1b72f2",
"#0056c1",
"#004aac",
'#e7f3ff',
'#d0e4ff',
'#a1c6fa',
'#6ea6f6',
'#458bf2',
'#2b7af1',
'#0b60d8',
'#1b72f2',
'#0056c1',
'#004aac',
];
const red: MantineColorsTuple = [
'#ffebeb',
'#fad7d7',
'#eeadad',
'#e3807f',
'#da5a59',
'#d54241',
'#d43535',
'#bc2727',
'#a82022',
'#93151b',
];
export const theme = createTheme({
colors: {
blue,
red,
},
});

View File

@ -59,11 +59,13 @@
"happy-dom": "^15.7.3",
"kysely": "^0.27.4",
"kysely-migration-cli": "^0.4.2",
"ldapjs": "^3.0.7",
"marked": "^13.0.3",
"mime-types": "^2.1.35",
"nanoid": "^5.0.7",
"nestjs-kysely": "^1.0.0",
"nodemailer": "^6.9.14",
"ntlm-server": "^0.1.3",
"passport-jwt": "^4.0.1",
"pg": "^8.12.0",
"pg-tsquery": "^8.4.2",
@ -85,6 +87,7 @@
"@types/debounce": "^1.2.4",
"@types/fs-extra": "^11.0.4",
"@types/jest": "^29.5.12",
"@types/ldapjs": "^3.0.6",
"@types/mime-types": "^2.1.4",
"@types/node": "^22.5.2",
"@types/nodemailer": "^6.4.15",

View File

@ -14,6 +14,7 @@ import { EventEmitterModule } from '@nestjs/event-emitter';
import { HealthModule } from './integrations/health/health.module';
import { ExportModule } from './integrations/export/export.module';
import { ImportModule } from './integrations/import/import.module';
import { NTLMModule } from './integrations/ntlm/ntlm.module';
@Module({
imports: [
@ -27,6 +28,7 @@ import { ImportModule } from './integrations/import/import.module';
HealthModule,
ImportModule,
ExportModule,
NTLMModule,
StorageModule.forRootAsync({
imports: [EnvironmentModule],
}),

View File

@ -0,0 +1,3 @@
export enum EventName {
COLLAB_PAGE_UPDATED = 'collab.page.updated',
}

View File

@ -182,7 +182,7 @@ export class AttachmentController {
if (!inlineFileExtensions.includes(attachment.fileExt)) {
res.header(
'Content-Disposition',
`attachment; filename="${attachment.fileName}"`,
`attachment; filename="${encodeURIComponent(attachment.fileName)}"`,
);
}

View File

@ -4,10 +4,11 @@ import { AttachmentController } from './attachment.controller';
import { StorageModule } from '../../integrations/storage/storage.module';
import { UserModule } from '../user/user.module';
import { WorkspaceModule } from '../workspace/workspace.module';
import { AttachmentProcessor } from './processors/attachment.processor';
@Module({
imports: [StorageModule, UserModule, WorkspaceModule],
controllers: [AttachmentController],
providers: [AttachmentService],
providers: [AttachmentService, AttachmentProcessor],
})
export class AttachmentModule {}

View File

@ -0,0 +1,47 @@
import { Logger, OnModuleDestroy } from '@nestjs/common';
import { OnWorkerEvent, Processor, WorkerHost } from '@nestjs/bullmq';
import { Job } from 'bullmq';
import { AttachmentService } from '../services/attachment.service';
import { QueueJob, QueueName } from 'src/integrations/queue/constants';
import { Space } from '@docmost/db/types/entity.types';
@Processor(QueueName.ATTACHEMENT_QUEUE)
export class AttachmentProcessor extends WorkerHost implements OnModuleDestroy {
private readonly logger = new Logger(AttachmentProcessor.name);
constructor(private readonly attachmentService: AttachmentService) {
super();
}
async process(job: Job<Space, void>): Promise<void> {
try {
if (job.name === QueueJob.DELETE_SPACE_ATTACHMENTS) {
await this.attachmentService.handleDeleteSpaceAttachments(job.data.id);
}
} catch (err) {
throw err;
}
}
@OnWorkerEvent('active')
onActive(job: Job) {
this.logger.debug(`Processing ${job.name} job`);
}
@OnWorkerEvent('failed')
onError(job: Job) {
this.logger.error(
`Error processing ${job.name} job. Reason: ${job.failedReason}`,
);
}
@OnWorkerEvent('completed')
onCompleted(job: Job) {
this.logger.debug(`Completed ${job.name} job`);
}
async onModuleDestroy(): Promise<void> {
if (this.worker) {
await this.worker.close();
}
}
}

View File

@ -256,4 +256,37 @@ export class AttachmentService {
trx,
);
}
async handleDeleteSpaceAttachments(spaceId: string) {
try {
const attachments = await this.attachmentRepo.findBySpaceId(spaceId);
if (!attachments || attachments.length === 0) {
return;
}
const failedDeletions = [];
await Promise.all(
attachments.map(async (attachment) => {
try {
await this.storageService.delete(attachment.filePath);
await this.attachmentRepo.deleteAttachmentById(attachment.id);
} catch (err) {
failedDeletions.push(attachment.id);
this.logger.log(
`DeleteSpaceAttachments: failed to delete attachment ${attachment.id}:`,
err,
);
}
}),
);
if(failedDeletions.length === attachments.length){
throw new Error(`Failed to delete any attachments for spaceId: ${spaceId}`);
}
} catch (err) {
throw err;
}
}
}

View File

@ -10,5 +10,6 @@ import { TokenModule } from './token.module';
imports: [TokenModule, WorkspaceModule],
controllers: [AuthController],
providers: [AuthService, SignupService, JwtStrategy],
exports: [AuthService]
})
export class AuthModule {}

View File

@ -14,6 +14,9 @@ import { executeTx } from '@docmost/db/utils';
import { InjectKysely } from 'nestjs-kysely';
import { SpaceMemberService } from './space-member.service';
import { SpaceRole } from '../../../common/helpers/types/permission';
import { QueueJob, QueueName } from 'src/integrations/queue/constants';
import { Queue } from 'bullmq';
import { InjectQueue } from '@nestjs/bullmq';
@Injectable()
export class SpaceService {
@ -21,6 +24,7 @@ export class SpaceService {
private spaceRepo: SpaceRepo,
private spaceMemberService: SpaceMemberService,
@InjectKysely() private readonly db: KyselyDB,
@InjectQueue(QueueName.ATTACHEMENT_QUEUE) private attachmentQueue: Queue,
) {}
async createSpace(
@ -88,10 +92,24 @@ export class SpaceService {
updateSpaceDto: UpdateSpaceDto,
workspaceId: string,
): Promise<Space> {
if (updateSpaceDto?.slug) {
const slugExists = await this.spaceRepo.slugExists(
updateSpaceDto.slug,
workspaceId,
);
if (slugExists) {
throw new BadRequestException(
'Space slug exists. Please use a unique space slug',
);
}
}
return await this.spaceRepo.updateSpace(
{
name: updateSpaceDto.name,
description: updateSpaceDto.description,
slug: updateSpaceDto.slug,
},
updateSpaceDto.spaceId,
workspaceId,
@ -120,4 +138,14 @@ export class SpaceService {
return spaces;
}
async deleteSpace(spaceId: string, workspaceId: string): Promise<void> {
const space = await this.spaceRepo.findById(spaceId, workspaceId);
if (!space) {
throw new NotFoundException('Space not found');
}
await this.spaceRepo.deleteSpace(spaceId, workspaceId);
await this.attachmentQueue.add(QueueJob.DELETE_SPACE_ATTACHMENTS, space);
}
}

View File

@ -95,7 +95,7 @@ export class SpaceController {
@HttpCode(HttpStatus.OK)
@Post('create')
createGroup(
createSpace(
@Body() createSpaceDto: CreateSpaceDto,
@AuthUser() user: User,
@AuthWorkspace() workspace: Workspace,
@ -111,7 +111,7 @@ export class SpaceController {
@HttpCode(HttpStatus.OK)
@Post('update')
async updateGroup(
async updateSpace(
@Body() updateSpaceDto: UpdateSpaceDto,
@AuthUser() user: User,
@AuthWorkspace() workspace: Workspace,
@ -126,6 +126,23 @@ export class SpaceController {
return this.spaceService.updateSpace(updateSpaceDto, workspace.id);
}
@HttpCode(HttpStatus.OK)
@Post('delete')
async deleteSpace(
@Body() spaceIdDto: SpaceIdDto,
@AuthUser() user: User,
@AuthWorkspace() workspace: Workspace,
) {
const ability = await this.spaceAbility.createForUser(
user,
spaceIdDto.spaceId,
);
if (ability.cannot(SpaceCaslAction.Manage, SpaceCaslSubject.Settings)) {
throw new ForbiddenException();
}
return this.spaceService.deleteSpace(spaceIdDto.spaceId, workspace.id);
}
@HttpCode(HttpStatus.OK)
@Post('members')
async getSpaceMembers(

View File

@ -40,6 +40,21 @@ export class AttachmentRepo {
.executeTakeFirst();
}
async findBySpaceId(
spaceId: string,
opts?: {
trx?: KyselyTransaction;
},
): Promise<Attachment[]> {
const db = dbOrTx(this.db, opts?.trx);
return db
.selectFrom('attachments')
.selectAll()
.where('spaceId', '=', spaceId)
.execute();
}
async updateAttachment(
updatableAttachment: UpdatableAttachment,
attachmentId: string,
@ -52,7 +67,7 @@ export class AttachmentRepo {
.executeTakeFirst();
}
async deleteAttachment(attachmentId: string): Promise<void> {
async deleteAttachmentById(attachmentId: string): Promise<void> {
await this.db
.deleteFrom('attachments')
.where('id', '=', attachmentId)

View File

@ -64,7 +64,7 @@ export class SpaceMemberRepo {
} else if (opts.groupId) {
query = query.where('groupId', '=', opts.groupId);
} else {
throw new BadRequestException('Please provider a userId or groupId');
throw new BadRequestException('Please provide a userId or groupId');
}
return query.executeTakeFirst();
}

View File

@ -98,6 +98,13 @@ export class EnvironmentService {
return secure === 'true';
}
getSmtpIgnoreTLS(): boolean {
const ignoretls = this.configService
.get<string>('SMTP_IGNORETLS', 'false')
.toLowerCase();
return ignoretls === 'true';
}
getSmtpUsername(): string {
return this.configService.get<string>('SMTP_USERNAME');
}
@ -110,6 +117,37 @@ export class EnvironmentService {
return this.configService.get<string>('POSTMARK_TOKEN');
}
getLdapBaseDn(): string {
return this.configService.get<string>('LDAP_BASEDN')
}
getLdapDomainSuffix(): string {
return this.configService.get<string>('LDAP_DOMAINSUFFIX');
}
getLdapUsername(): string {
return this.configService.get<string>('LDAP_USERNAME')
}
getLdapPassword(): string {
return this.configService.get<string>('LDAP_PASSWORD')
}
getLdapNameAttribute(): string {
return this.configService.get<string>('LDAP_NAMEATTRIBUTE')
}
getLdapMailAttribute(): string {
return this.configService.get<string>('LDAP_MAILATTRIBUTE')
}
usingNtlmAuth(): boolean {
const ntlmAuth = this.configService
.get<string>('VITE_NTLM_AUTH', 'false')
.toLowerCase();
return ntlmAuth === 'true';
}
isCloud(): boolean {
const cloudConfig = this.configService
.get<string>('CLOUD', 'false')

View File

@ -61,7 +61,8 @@ export class ImportController {
res.headers({
'Content-Type': getMimeType(fileExt),
'Content-Disposition': 'attachment; filename="' + fileName + '"',
'Content-Disposition':
'attachment; filename="' + encodeURIComponent(fileName) + '"',
});
res.send(rawContent);

View File

@ -41,6 +41,7 @@ export const mailDriverConfigProvider = {
connectionTimeout: 30 * 1000, // 30 seconds
auth,
secure: environmentService.getSmtpSecure(),
ignoreTLS: environmentService.getSmtpIgnoreTLS()
} as SMTPTransport.Options,
};

View File

@ -0,0 +1,86 @@
import {
Controller,
Get,
Req,
Res,
HttpException,
HttpStatus,
Post,
} from '@nestjs/common';
import { FastifyRequest, FastifyReply } from 'fastify';
import {
NTLMNegotiationMessage,
NTLMChallengeMessage,
NTLMAuthenticateMessage,
MessageType,
} from 'ntlm-server';
import { EnvironmentService } from '../environment/environment.service';
import { NTLMService } from './ntlm.service';
@Controller()
export class NTLMController {
constructor(
private readonly ntlmService: NTLMService,
private readonly environmentService: EnvironmentService,
) {}
@Post('auth/ntlm')
async ntlmAuth(@Req() req, @Res() res: FastifyReply) {
const authHeader = req.headers['authorization'];
if (!authHeader) {
// Step 1: Challenge the client for NTLM authentication
return res.status(401).header('WWW-Authenticate', 'NTLM').send();
}
if (authHeader.startsWith('NTLM ')) {
// Step 2: Handle NTLM negotiation message
const clientNegotiation = new NTLMNegotiationMessage(authHeader);
if (clientNegotiation.messageType === MessageType.NEGOTIATE) {
// Step 3: Send NTLM challenge message
const serverChallenge = new NTLMChallengeMessage(clientNegotiation);
const base64Challenge = serverChallenge.toBuffer().toString('base64');
return res
.status(401)
.header('WWW-Authenticate', `NTLM ${base64Challenge}`)
.send();
} else if (clientNegotiation.messageType === MessageType.AUTHENTICATE) {
// Step 4: Handle NTLM Authenticate message
const clientAuthentication = new NTLMAuthenticateMessage(authHeader);
// Here you'd perform LDAP or Active Directory authentication
const client = this.ntlmService.createClient(
clientAuthentication.domainName,
);
// Asynchronous bind to AD
await this.ntlmService.bindAsync(client);
const results = await this.ntlmService.searchAsync(client, {
scope: 'sub',
filter: `(userPrincipalName=${clientAuthentication.userName}@${clientAuthentication.domainName}*)`,
});
if (results.length == 1) {
const ntlmSignInResult = await this.ntlmService.login(
results.at(0)[this.environmentService.getLdapNameAttribute()],
results.at(0)[this.environmentService.getLdapMailAttribute()],
req.raw.workspaceId,
);
return res.status(200).send(ntlmSignInResult);
} else return res.status(403).send();
} else {
console.warn('Invalid NTLM Message received.');
return res.status(400).send('Invalid NTLM Message');
}
}
res.status(400).send('Bad NTLM request');
}
}

View File

@ -0,0 +1,13 @@
import { Module } from '@nestjs/common';
import { NTLMController } from './ntlm.controller';
import { NTLMService } from './ntlm.service';
import { TokenModule } from 'src/core/auth/token.module';
import { WorkspaceModule } from 'src/core/workspace/workspace.module';
import { AuthModule } from 'src/core/auth/auth.module';
@Module({
imports: [TokenModule, WorkspaceModule, AuthModule],
controllers: [NTLMController],
providers: [NTLMService],
})
export class NTLMModule {}

View File

@ -0,0 +1,110 @@
import { Inject, Injectable, UnauthorizedException } from '@nestjs/common';
import { EnvironmentService } from '../environment/environment.service';
import * as ldap from 'ldapjs';
import { UserRepo } from '@docmost/db/repos/user/user.repo';
import { TokensDto } from 'src/core/auth/dto/tokens.dto';
import { TokenService } from 'src/core/auth/services/token.service';
import { AuthService } from 'src/core/auth/services/auth.service';
@Injectable()
export class NTLMService {
constructor(
private readonly environmentService: EnvironmentService,
private authService: AuthService,
private tokenService: TokenService,
private userRepo: UserRepo,
) {}
createClient = (domain: string) =>
ldap.createClient({
url: 'ldap://' + domain + this.environmentService.getLdapDomainSuffix(),
});
// Promisified version of ldap.Client.bind
bindAsync = (client: ldap.Client): Promise<void> => {
return new Promise((resolve, reject) => {
client.bind(
this.environmentService.getLdapUsername(),
this.environmentService.getLdapPassword(),
(err) => {
if (err) {
reject(err);
} else {
resolve();
}
},
);
});
};
// Promisified version of client.search
searchAsync = (
client: ldap.Client,
options: ldap.SearchOptions,
): Promise<any[]> => {
const baseDN: string = this.environmentService.getLdapBaseDn();
return new Promise((resolve, reject) => {
const entries: any[] = [];
client.search(baseDN, options, (err, res) => {
if (err) {
reject(err);
}
res.on('searchEntry', (entry) => {
const attributes = Object.fromEntries(
entry.attributes.map(({ type, values }) => [
type,
values.length > 1 ? values : values[0],
]),
);
entries.push(attributes);
});
res.on('end', () => {
resolve(entries);
});
res.on('error', (error) => {
reject(error);
});
});
});
};
async login(name: string, email: string, workspaceId: string) {
const user = await this.userRepo.findByEmail(email, workspaceId, false);
if (!user) {
const tokensR = await this.authService.register(
{
name,
email,
password: this.generateRandomPassword(12),
},
workspaceId,
);
return tokensR;
}
user.lastLoginAt = new Date();
await this.userRepo.updateLastLogin(user.id, workspaceId);
const tokens: TokensDto = await this.tokenService.generateTokens(user);
return { tokens };
}
generateRandomPassword(length: number): string {
const characters =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+[]{}|;:,.<>?';
let password = '';
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * characters.length);
password += characters[randomIndex];
}
return password;
}
}

View File

@ -1,7 +1,10 @@
export enum QueueName {
EMAIL_QUEUE = '{email-queue}',
ATTACHEMENT_QUEUE = '{attachment-queue}',
}
export enum QueueJob {
SEND_EMAIL = 'send-email',
DELETE_SPACE_ATTACHMENTS = 'delete-space-attachments',
DELETE_PAGE_ATTACHMENTS = 'delete-page-attachments',
}

View File

@ -31,6 +31,9 @@ import { QueueName } from './constants';
BullModule.registerQueue({
name: QueueName.EMAIL_QUEUE,
}),
BullModule.registerQueue({
name: QueueName.ATTACHEMENT_QUEUE,
}),
],
exports: [BullModule],
})

316
pnpm-lock.yaml generated
View File

@ -158,7 +158,7 @@ importers:
devDependencies:
'@nx/js':
specifier: 19.6.3
version: 19.6.3(@babel/traverse@7.24.6)(@swc/core@1.5.25)(@types/node@22.5.2)(nx@19.6.3(@swc/core@1.5.25))(typescript@5.5.4)
version: 19.6.3(@babel/traverse@7.24.6)(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(nx@19.6.3(@swc/core@1.5.25(@swc/helpers@0.5.5)))(typescript@5.5.4)
'@types/uuid':
specifier: ^10.0.0
version: 10.0.0
@ -167,7 +167,7 @@ importers:
version: 8.2.2
nx:
specifier: 19.6.3
version: 19.6.3(@swc/core@1.5.25)
version: 19.6.3(@swc/core@1.5.25(@swc/helpers@0.5.5))
tsx:
specifier: ^4.19.0
version: 4.19.0
@ -445,6 +445,9 @@ importers:
kysely-migration-cli:
specifier: ^0.4.2
version: 0.4.2
ldapjs:
specifier: ^3.0.7
version: 3.0.7
marked:
specifier: ^13.0.3
version: 13.0.3
@ -460,6 +463,9 @@ importers:
nodemailer:
specifier: ^6.9.14
version: 6.9.14
ntlm-server:
specifier: ^0.1.3
version: 0.1.3
passport-jwt:
specifier: ^4.0.1
version: 4.0.1
@ -496,7 +502,7 @@ importers:
devDependencies:
'@nestjs/cli':
specifier: ^10.4.5
version: 10.4.5(@swc/core@1.5.25)
version: 10.4.5(@swc/core@1.5.25(@swc/helpers@0.5.5))
'@nestjs/schematics':
specifier: ^10.1.4
version: 10.1.4(chokidar@3.6.0)(typescript@5.5.4)
@ -518,6 +524,9 @@ importers:
'@types/jest':
specifier: ^29.5.12
version: 29.5.12
'@types/ldapjs':
specifier: ^3.0.6
version: 3.0.6
'@types/mime-types':
specifier: ^2.1.4
version: 2.1.4
@ -556,7 +565,7 @@ importers:
version: 5.2.1(@types/eslint@8.56.10)(eslint-config-prettier@9.1.0(eslint@9.9.1(jiti@1.21.0)))(eslint@9.9.1(jiti@1.21.0))(prettier@3.3.3)
jest:
specifier: ^29.7.0
version: 29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25)(@types/node@22.5.2)(typescript@5.5.4))
version: 29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(typescript@5.5.4))
kysely-codegen:
specifier: ^0.16.3
version: 0.16.3(kysely@0.27.4)(pg@8.12.0)
@ -574,13 +583,13 @@ importers:
version: 7.0.0
ts-jest:
specifier: ^29.2.5
version: 29.2.5(@babel/core@7.24.3)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.3))(jest@29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25)(@types/node@22.5.2)(typescript@5.5.4)))(typescript@5.5.4)
version: 29.2.5(@babel/core@7.24.3)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.3))(jest@29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(typescript@5.5.4)))(typescript@5.5.4)
ts-loader:
specifier: ^9.5.1
version: 9.5.1(typescript@5.5.4)(webpack@5.94.0(@swc/core@1.5.25))
version: 9.5.1(typescript@5.5.4)(webpack@5.94.0(@swc/core@1.5.25(@swc/helpers@0.5.5)))
ts-node:
specifier: ^10.9.2
version: 10.9.2(@swc/core@1.5.25)(@types/node@22.5.2)(typescript@5.5.4)
version: 10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(typescript@5.5.4)
tsconfig-paths:
specifier: ^4.2.0
version: 4.2.0
@ -2291,6 +2300,42 @@ packages:
'@jridgewell/trace-mapping@0.3.9':
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
'@ldapjs/asn1@1.2.0':
resolution: {integrity: sha512-KX/qQJ2xxzvO2/WOvr1UdQ+8P5dVvuOLk/C9b1bIkXxZss8BaR28njXdPgFCpj5aHaf1t8PmuVnea+N9YG9YMw==}
deprecated: This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md
'@ldapjs/asn1@2.0.0':
resolution: {integrity: sha512-G9+DkEOirNgdPmD0I8nu57ygQJKOOgFEMKknEuQvIHbGLwP3ny1mY+OTUYLCbCaGJP4sox5eYgBJRuSUpnAddA==}
deprecated: This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md
'@ldapjs/attribute@1.0.0':
resolution: {integrity: sha512-ptMl2d/5xJ0q+RgmnqOi3Zgwk/TMJYG7dYMC0Keko+yZU6n+oFM59MjQOUht5pxJeS4FWrImhu/LebX24vJNRQ==}
deprecated: This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md
'@ldapjs/change@1.0.0':
resolution: {integrity: sha512-EOQNFH1RIku3M1s0OAJOzGfAohuFYXFY4s73wOhRm4KFGhmQQ7MChOh2YtYu9Kwgvuq1B0xKciXVzHCGkB5V+Q==}
deprecated: This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md
'@ldapjs/controls@2.1.0':
resolution: {integrity: sha512-2pFdD1yRC9V9hXfAWvCCO2RRWK9OdIEcJIos/9cCVP9O4k72BY1bLDQQ4KpUoJnl4y/JoD4iFgM+YWT3IfITWw==}
deprecated: This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md
'@ldapjs/dn@1.1.0':
resolution: {integrity: sha512-R72zH5ZeBj/Fujf/yBu78YzpJjJXG46YHFo5E4W1EqfNpo1UsVPqdLrRMXeKIsJT3x9dJVIfR6OpzgINlKpi0A==}
deprecated: This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md
'@ldapjs/filter@2.1.1':
resolution: {integrity: sha512-TwPK5eEgNdUO1ABPBUQabcZ+h9heDORE4V9WNZqCtYLKc06+6+UAJ3IAbr0L0bYTnkkWC/JEQD2F+zAFsuikNw==}
deprecated: This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md
'@ldapjs/messages@1.3.0':
resolution: {integrity: sha512-K7xZpXJ21bj92jS35wtRbdcNrwmxAtPwy4myeh9duy/eR3xQKvikVycbdWVzkYEAVE5Ce520VXNOwCHjomjCZw==}
deprecated: This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md
'@ldapjs/protocol@1.2.1':
resolution: {integrity: sha512-O89xFDLW2gBoZWNXuXpBSM32/KealKCTb3JGtJdtUQc7RjAk8XzrRgyz02cPAwGKwKPxy0ivuC7UP9bmN87egQ==}
deprecated: This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md
'@lifeomic/attempt@3.0.3':
resolution: {integrity: sha512-GlM2AbzrErd/TmLL3E8hAHmb5Q7VhDJp35vIbyPVA5Rz55LZuRr8pwL3qrwwkVNo05gMX1J44gURKb4MHQZo7w==}
@ -3724,6 +3769,9 @@ packages:
'@types/katex@0.16.7':
resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==}
'@types/ldapjs@3.0.6':
resolution: {integrity: sha512-E2Tn1ltJDYBsidOT9QG4engaQeQzRQ9aYNxVmjCkD33F7cIeLPgrRDXAYs0O35mK2YDU20c/+ZkNjeAPRGLM0Q==}
'@types/methods@1.1.4':
resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==}
@ -4066,6 +4114,7 @@ packages:
are-we-there-yet@2.0.0:
resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==}
engines: {node: '>=10'}
deprecated: This package is no longer supported.
arg@4.1.3:
resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==}
@ -4082,6 +4131,10 @@ packages:
asap@2.0.6:
resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
assert-plus@1.0.0:
resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==}
engines: {node: '>=0.8'}
async-lock@1.4.1:
resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==}
@ -4158,6 +4211,10 @@ packages:
peerDependencies:
'@babel/core': ^7.0.0
backoff@2.5.0:
resolution: {integrity: sha512-wC5ihrnUXmR2douXmXLCe5O3zg3GKIyvRi/hi58a/XyRxVI+3/yM0PYueQOZXPXQ9pxBislYkw+sF9b7C/RuMA==}
engines: {node: '>= 0.6'}
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
@ -4474,6 +4531,9 @@ packages:
core-js-compat@3.35.0:
resolution: {integrity: sha512-5blwFAddknKeNgsjBzilkdQ0+YK8L1PfqPYq40NOYMYFSS38qj+hpTcLLWwpIwA2A5bje/x5jmVn2tzUMg9IVw==}
core-util-is@1.0.2:
resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==}
core-util-is@1.0.3:
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
@ -5086,6 +5146,10 @@ packages:
resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==}
engines: {node: '>=4'}
extsprintf@1.4.1:
resolution: {integrity: sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==}
engines: {'0': node >=0.6.0}
fast-content-type-parse@1.1.0:
resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==}
@ -5267,6 +5331,7 @@ packages:
gauge@3.0.2:
resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==}
engines: {node: '>=10'}
deprecated: This package is no longer supported.
generic-pool@3.9.0:
resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==}
@ -5949,6 +6014,10 @@ packages:
layout-base@1.0.2:
resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==}
ldapjs@3.0.7:
resolution: {integrity: sha512-1ky+WrN+4CFMuoekUOv7Y1037XWdjKpu0xAPwSP+9KdvmV9PG+qOKlssDV6a+U32apwxdD3is/BZcWOYzN30cg==}
deprecated: This package has been decomissioned. See https://github.com/ldapjs/node-ldapjs/blob/8ffd0bc9c149088a10ec4c1ec6a18450f76ad05d/README.md
leac@0.6.0:
resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==}
@ -6369,6 +6438,10 @@ packages:
npmlog@5.0.1:
resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
deprecated: This package is no longer supported.
ntlm-server@0.1.3:
resolution: {integrity: sha512-DhCApPhF1U7TLJ7vTiXNNqN9C/8huf6qG8lBDU8075zvWXXWZCazo623xZQZNV5jocgUDWl9U3/fNaw7hjJHKQ==}
nwsapi@2.2.10:
resolution: {integrity: sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==}
@ -6711,6 +6784,10 @@ packages:
postmark@4.0.5:
resolution: {integrity: sha512-nerZdd3TwOH4CgGboZnlUM/q7oZk0EqpZgJL+Y3Nup8kHeaukxouQ6JcFF3EJEijc4QbuNv1TefGhboAKtf/SQ==}
precond@0.2.3:
resolution: {integrity: sha512-QCYG84SgGyGzqJ/vlMsxeXd/pgL/I94ixdNFyh1PusWmTCyVfPJjZ1K1jvHtsbfnXQs2TSkEP2fR7QiMZAnKFQ==}
engines: {node: '>= 0.6'}
prelude-ls@1.2.1:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
@ -6736,6 +6813,9 @@ packages:
resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
process-warning@2.3.2:
resolution: {integrity: sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==}
process-warning@3.0.0:
resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==}
@ -7816,6 +7896,18 @@ packages:
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
engines: {node: '>= 0.8'}
vasync@2.2.1:
resolution: {integrity: sha512-Hq72JaTpcTFdWiNA4Y22Amej2GH3BFmBaKPPlDZ4/oC8HNn2ISHLkFrJU4Ds8R3jcUi7oo5Y9jcMHKjES+N9wQ==}
engines: {'0': node >=0.6.0}
verror@1.10.0:
resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==}
engines: {'0': node >=0.6.0}
verror@1.10.1:
resolution: {integrity: sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==}
engines: {node: '>=0.6.0'}
vite@5.4.2:
resolution: {integrity: sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==}
engines: {node: ^18.0.0 || >=20.0.0}
@ -10163,7 +10255,7 @@ snapshots:
jest-util: 29.7.0
slash: 3.0.0
'@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.5.25)(@types/node@22.5.2)(typescript@5.5.4))':
'@jest/core@29.7.0(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(typescript@5.5.4))':
dependencies:
'@jest/console': 29.7.0
'@jest/reporters': 29.7.0
@ -10177,7 +10269,7 @@ snapshots:
exit: 0.1.2
graceful-fs: 4.2.11
jest-changed-files: 29.7.0
jest-config: 29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25)(@types/node@22.5.2)(typescript@5.5.4))
jest-config: 29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(typescript@5.5.4))
jest-haste-map: 29.7.0
jest-message-util: 29.7.0
jest-regex-util: 29.6.3
@ -10358,6 +10450,50 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.1
'@jridgewell/sourcemap-codec': 1.4.15
'@ldapjs/asn1@1.2.0': {}
'@ldapjs/asn1@2.0.0': {}
'@ldapjs/attribute@1.0.0':
dependencies:
'@ldapjs/asn1': 2.0.0
'@ldapjs/protocol': 1.2.1
process-warning: 2.3.2
'@ldapjs/change@1.0.0':
dependencies:
'@ldapjs/asn1': 2.0.0
'@ldapjs/attribute': 1.0.0
'@ldapjs/controls@2.1.0':
dependencies:
'@ldapjs/asn1': 1.2.0
'@ldapjs/protocol': 1.2.1
'@ldapjs/dn@1.1.0':
dependencies:
'@ldapjs/asn1': 2.0.0
process-warning: 2.3.2
'@ldapjs/filter@2.1.1':
dependencies:
'@ldapjs/asn1': 2.0.0
'@ldapjs/protocol': 1.2.1
process-warning: 2.3.2
'@ldapjs/messages@1.3.0':
dependencies:
'@ldapjs/asn1': 2.0.0
'@ldapjs/attribute': 1.0.0
'@ldapjs/change': 1.0.0
'@ldapjs/controls': 2.1.0
'@ldapjs/dn': 1.1.0
'@ldapjs/filter': 2.1.1
'@ldapjs/protocol': 1.2.1
process-warning: 2.3.2
'@ldapjs/protocol@1.2.1': {}
'@lifeomic/attempt@3.0.3': {}
'@ljharb/through@2.3.13':
@ -10477,7 +10613,7 @@ snapshots:
bullmq: 5.12.12
tslib: 2.6.3
'@nestjs/cli@10.4.5(@swc/core@1.5.25)':
'@nestjs/cli@10.4.5(@swc/core@1.5.25(@swc/helpers@0.5.5))':
dependencies:
'@angular-devkit/core': 17.3.8(chokidar@3.6.0)
'@angular-devkit/schematics': 17.3.8(chokidar@3.6.0)
@ -10487,7 +10623,7 @@ snapshots:
chokidar: 3.6.0
cli-table3: 0.6.5
commander: 4.1.1
fork-ts-checker-webpack-plugin: 9.0.2(typescript@5.3.3)(webpack@5.94.0(@swc/core@1.5.25))
fork-ts-checker-webpack-plugin: 9.0.2(typescript@5.3.3)(webpack@5.94.0(@swc/core@1.5.25(@swc/helpers@0.5.5)))
glob: 10.4.2
inquirer: 8.2.6
node-emoji: 1.11.0
@ -10496,10 +10632,10 @@ snapshots:
tsconfig-paths: 4.2.0
tsconfig-paths-webpack-plugin: 4.1.0
typescript: 5.3.3
webpack: 5.94.0(@swc/core@1.5.25)
webpack: 5.94.0(@swc/core@1.5.25(@swc/helpers@0.5.5))
webpack-node-externals: 3.0.0
optionalDependencies:
'@swc/core': 1.5.25
'@swc/core': 1.5.25(@swc/helpers@0.5.5)
transitivePeerDependencies:
- esbuild
- uglify-js
@ -10683,15 +10819,15 @@ snapshots:
'@nodelib/fs.scandir': 2.1.5
fastq: 1.17.1
'@nrwl/devkit@19.6.3(nx@19.6.3(@swc/core@1.5.25))':
'@nrwl/devkit@19.6.3(nx@19.6.3(@swc/core@1.5.25(@swc/helpers@0.5.5)))':
dependencies:
'@nx/devkit': 19.6.3(nx@19.6.3(@swc/core@1.5.25))
'@nx/devkit': 19.6.3(nx@19.6.3(@swc/core@1.5.25(@swc/helpers@0.5.5)))
transitivePeerDependencies:
- nx
'@nrwl/js@19.6.3(@babel/traverse@7.24.6)(@swc/core@1.5.25)(@types/node@22.5.2)(nx@19.6.3(@swc/core@1.5.25))(typescript@5.5.4)':
'@nrwl/js@19.6.3(@babel/traverse@7.24.6)(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(nx@19.6.3(@swc/core@1.5.25(@swc/helpers@0.5.5)))(typescript@5.5.4)':
dependencies:
'@nx/js': 19.6.3(@babel/traverse@7.24.6)(@swc/core@1.5.25)(@types/node@22.5.2)(nx@19.6.3(@swc/core@1.5.25))(typescript@5.5.4)
'@nx/js': 19.6.3(@babel/traverse@7.24.6)(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(nx@19.6.3(@swc/core@1.5.25(@swc/helpers@0.5.5)))(typescript@5.5.4)
transitivePeerDependencies:
- '@babel/traverse'
- '@swc-node/register'
@ -10704,18 +10840,18 @@ snapshots:
- typescript
- verdaccio
'@nrwl/tao@19.6.3(@swc/core@1.5.25)':
'@nrwl/tao@19.6.3(@swc/core@1.5.25(@swc/helpers@0.5.5))':
dependencies:
nx: 19.6.3(@swc/core@1.5.25)
nx: 19.6.3(@swc/core@1.5.25(@swc/helpers@0.5.5))
tslib: 2.6.2
transitivePeerDependencies:
- '@swc-node/register'
- '@swc/core'
- debug
'@nrwl/workspace@19.6.3(@swc/core@1.5.25)':
'@nrwl/workspace@19.6.3(@swc/core@1.5.25(@swc/helpers@0.5.5))':
dependencies:
'@nx/workspace': 19.6.3(@swc/core@1.5.25)
'@nx/workspace': 19.6.3(@swc/core@1.5.25(@swc/helpers@0.5.5))
transitivePeerDependencies:
- '@swc-node/register'
- '@swc/core'
@ -10729,20 +10865,20 @@ snapshots:
transitivePeerDependencies:
- encoding
'@nx/devkit@19.6.3(nx@19.6.3(@swc/core@1.5.25))':
'@nx/devkit@19.6.3(nx@19.6.3(@swc/core@1.5.25(@swc/helpers@0.5.5)))':
dependencies:
'@nrwl/devkit': 19.6.3(nx@19.6.3(@swc/core@1.5.25))
'@nrwl/devkit': 19.6.3(nx@19.6.3(@swc/core@1.5.25(@swc/helpers@0.5.5)))
ejs: 3.1.9
enquirer: 2.3.6
ignore: 5.3.1
minimatch: 9.0.3
nx: 19.6.3(@swc/core@1.5.25)
nx: 19.6.3(@swc/core@1.5.25(@swc/helpers@0.5.5))
semver: 7.6.2
tmp: 0.2.1
tslib: 2.6.2
yargs-parser: 21.1.1
'@nx/js@19.6.3(@babel/traverse@7.24.6)(@swc/core@1.5.25)(@types/node@22.5.2)(nx@19.6.3(@swc/core@1.5.25))(typescript@5.5.4)':
'@nx/js@19.6.3(@babel/traverse@7.24.6)(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(nx@19.6.3(@swc/core@1.5.25(@swc/helpers@0.5.5)))(typescript@5.5.4)':
dependencies:
'@babel/core': 7.24.6
'@babel/plugin-proposal-decorators': 7.23.7(@babel/core@7.24.6)
@ -10751,9 +10887,9 @@ snapshots:
'@babel/preset-env': 7.23.8(@babel/core@7.24.6)
'@babel/preset-typescript': 7.23.3(@babel/core@7.24.6)
'@babel/runtime': 7.23.7
'@nrwl/js': 19.6.3(@babel/traverse@7.24.6)(@swc/core@1.5.25)(@types/node@22.5.2)(nx@19.6.3(@swc/core@1.5.25))(typescript@5.5.4)
'@nx/devkit': 19.6.3(nx@19.6.3(@swc/core@1.5.25))
'@nx/workspace': 19.6.3(@swc/core@1.5.25)
'@nrwl/js': 19.6.3(@babel/traverse@7.24.6)(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(nx@19.6.3(@swc/core@1.5.25(@swc/helpers@0.5.5)))(typescript@5.5.4)
'@nx/devkit': 19.6.3(nx@19.6.3(@swc/core@1.5.25(@swc/helpers@0.5.5)))
'@nx/workspace': 19.6.3(@swc/core@1.5.25(@swc/helpers@0.5.5))
babel-plugin-const-enum: 1.2.0(@babel/core@7.24.6)
babel-plugin-macros: 2.8.0
babel-plugin-transform-typescript-metadata: 0.3.2(@babel/core@7.24.6)(@babel/traverse@7.24.6)
@ -10771,7 +10907,7 @@ snapshots:
ora: 5.3.0
semver: 7.6.2
source-map-support: 0.5.19
ts-node: 10.9.1(@swc/core@1.5.25)(@types/node@22.5.2)(typescript@5.5.4)
ts-node: 10.9.1(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(typescript@5.5.4)
tsconfig-paths: 4.2.0
tslib: 2.6.2
transitivePeerDependencies:
@ -10815,13 +10951,13 @@ snapshots:
'@nx/nx-win32-x64-msvc@19.6.3':
optional: true
'@nx/workspace@19.6.3(@swc/core@1.5.25)':
'@nx/workspace@19.6.3(@swc/core@1.5.25(@swc/helpers@0.5.5))':
dependencies:
'@nrwl/workspace': 19.6.3(@swc/core@1.5.25)
'@nx/devkit': 19.6.3(nx@19.6.3(@swc/core@1.5.25))
'@nrwl/workspace': 19.6.3(@swc/core@1.5.25(@swc/helpers@0.5.5))
'@nx/devkit': 19.6.3(nx@19.6.3(@swc/core@1.5.25(@swc/helpers@0.5.5)))
chalk: 4.1.2
enquirer: 2.3.6
nx: 19.6.3(@swc/core@1.5.25)
nx: 19.6.3(@swc/core@1.5.25(@swc/helpers@0.5.5))
tslib: 2.6.2
yargs-parser: 21.1.1
transitivePeerDependencies:
@ -11443,7 +11579,7 @@ snapshots:
'@swc/core-win32-x64-msvc@1.5.25':
optional: true
'@swc/core@1.5.25':
'@swc/core@1.5.25(@swc/helpers@0.5.5)':
dependencies:
'@swc/counter': 0.1.3
'@swc/types': 0.1.7
@ -11458,6 +11594,7 @@ snapshots:
'@swc/core-win32-arm64-msvc': 1.5.25
'@swc/core-win32-ia32-msvc': 1.5.25
'@swc/core-win32-x64-msvc': 1.5.25
'@swc/helpers': 0.5.5
optional: true
'@swc/counter@0.1.3': {}
@ -11890,6 +12027,10 @@ snapshots:
'@types/katex@0.16.7': {}
'@types/ldapjs@3.0.6':
dependencies:
'@types/node': 22.5.2
'@types/methods@1.1.4': {}
'@types/mime-types@2.1.4': {}
@ -12301,6 +12442,8 @@ snapshots:
asap@2.0.6: {}
assert-plus@1.0.0: {}
async-lock@1.4.1: {}
async@3.2.5: {}
@ -12462,6 +12605,10 @@ snapshots:
babel-plugin-jest-hoist: 29.6.3
babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.6)
backoff@2.5.0:
dependencies:
precond: 0.2.3
balanced-match@1.0.2: {}
base64-js@1.5.1: {}
@ -12775,6 +12922,8 @@ snapshots:
dependencies:
browserslist: 4.23.0
core-util-is@1.0.2: {}
core-util-is@1.0.3: {}
cors@2.8.5:
@ -12803,13 +12952,13 @@ snapshots:
optionalDependencies:
typescript: 5.3.3
create-jest@29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25)(@types/node@22.5.2)(typescript@5.5.4)):
create-jest@29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(typescript@5.5.4)):
dependencies:
'@jest/types': 29.6.3
chalk: 4.1.2
exit: 0.1.2
graceful-fs: 4.2.11
jest-config: 29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25)(@types/node@22.5.2)(typescript@5.5.4))
jest-config: 29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(typescript@5.5.4))
jest-util: 29.7.0
prompts: 2.4.2
transitivePeerDependencies:
@ -13483,6 +13632,8 @@ snapshots:
iconv-lite: 0.4.24
tmp: 0.0.33
extsprintf@1.4.1: {}
fast-content-type-parse@1.1.0: {}
fast-decode-uri-component@1.0.1: {}
@ -13630,7 +13781,7 @@ snapshots:
cross-spawn: 7.0.3
signal-exit: 4.1.0
fork-ts-checker-webpack-plugin@9.0.2(typescript@5.3.3)(webpack@5.94.0(@swc/core@1.5.25)):
fork-ts-checker-webpack-plugin@9.0.2(typescript@5.3.3)(webpack@5.94.0(@swc/core@1.5.25(@swc/helpers@0.5.5))):
dependencies:
'@babel/code-frame': 7.24.6
chalk: 4.1.2
@ -13645,7 +13796,7 @@ snapshots:
semver: 7.6.2
tapable: 2.2.1
typescript: 5.3.3
webpack: 5.94.0(@swc/core@1.5.25)
webpack: 5.94.0(@swc/core@1.5.25(@swc/helpers@0.5.5))
form-data@4.0.0:
dependencies:
@ -14152,16 +14303,16 @@ snapshots:
- babel-plugin-macros
- supports-color
jest-cli@29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25)(@types/node@22.5.2)(typescript@5.5.4)):
jest-cli@29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(typescript@5.5.4)):
dependencies:
'@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.25)(@types/node@22.5.2)(typescript@5.5.4))
'@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(typescript@5.5.4))
'@jest/test-result': 29.7.0
'@jest/types': 29.6.3
chalk: 4.1.2
create-jest: 29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25)(@types/node@22.5.2)(typescript@5.5.4))
create-jest: 29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(typescript@5.5.4))
exit: 0.1.2
import-local: 3.1.0
jest-config: 29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25)(@types/node@22.5.2)(typescript@5.5.4))
jest-config: 29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(typescript@5.5.4))
jest-util: 29.7.0
jest-validate: 29.7.0
yargs: 17.7.2
@ -14171,7 +14322,7 @@ snapshots:
- supports-color
- ts-node
jest-config@29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25)(@types/node@22.5.2)(typescript@5.5.4)):
jest-config@29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(typescript@5.5.4)):
dependencies:
'@babel/core': 7.24.6
'@jest/test-sequencer': 29.7.0
@ -14197,7 +14348,7 @@ snapshots:
strip-json-comments: 3.1.1
optionalDependencies:
'@types/node': 22.5.2
ts-node: 10.9.2(@swc/core@1.5.25)(@types/node@22.5.2)(typescript@5.5.4)
ts-node: 10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(typescript@5.5.4)
transitivePeerDependencies:
- babel-plugin-macros
- supports-color
@ -14423,12 +14574,12 @@ snapshots:
merge-stream: 2.0.0
supports-color: 8.1.1
jest@29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25)(@types/node@22.5.2)(typescript@5.5.4)):
jest@29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(typescript@5.5.4)):
dependencies:
'@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.25)(@types/node@22.5.2)(typescript@5.5.4))
'@jest/core': 29.7.0(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(typescript@5.5.4))
'@jest/types': 29.6.3
import-local: 3.1.0
jest-cli: 29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25)(@types/node@22.5.2)(typescript@5.5.4))
jest-cli: 29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(typescript@5.5.4))
transitivePeerDependencies:
- '@types/node'
- babel-plugin-macros
@ -14610,6 +14761,23 @@ snapshots:
layout-base@1.0.2: {}
ldapjs@3.0.7:
dependencies:
'@ldapjs/asn1': 2.0.0
'@ldapjs/attribute': 1.0.0
'@ldapjs/change': 1.0.0
'@ldapjs/controls': 2.1.0
'@ldapjs/dn': 1.1.0
'@ldapjs/filter': 2.1.1
'@ldapjs/messages': 1.3.0
'@ldapjs/protocol': 1.2.1
abstract-logging: 2.0.1
assert-plus: 1.0.0
backoff: 2.5.0
once: 1.4.0
vasync: 2.2.1
verror: 1.10.1
leac@0.6.0: {}
less@4.2.0:
@ -15000,12 +15168,14 @@ snapshots:
gauge: 3.0.2
set-blocking: 2.0.0
ntlm-server@0.1.3: {}
nwsapi@2.2.10: {}
nx@19.6.3(@swc/core@1.5.25):
nx@19.6.3(@swc/core@1.5.25(@swc/helpers@0.5.5)):
dependencies:
'@napi-rs/wasm-runtime': 0.2.4
'@nrwl/tao': 19.6.3(@swc/core@1.5.25)
'@nrwl/tao': 19.6.3(@swc/core@1.5.25(@swc/helpers@0.5.5))
'@yarnpkg/lockfile': 1.1.0
'@yarnpkg/parsers': 3.0.0-rc.46
'@zkochan/js-yaml': 0.0.7
@ -15050,7 +15220,7 @@ snapshots:
'@nx/nx-linux-x64-musl': 19.6.3
'@nx/nx-win32-arm64-msvc': 19.6.3
'@nx/nx-win32-x64-msvc': 19.6.3
'@swc/core': 1.5.25
'@swc/core': 1.5.25(@swc/helpers@0.5.5)
transitivePeerDependencies:
- debug
@ -15379,6 +15549,8 @@ snapshots:
transitivePeerDependencies:
- debug
precond@0.2.3: {}
prelude-ls@1.2.1: {}
prettier-linter-helpers@1.0.0:
@ -15397,6 +15569,8 @@ snapshots:
proc-log@3.0.0: {}
process-warning@2.3.2: {}
process-warning@3.0.0: {}
process@0.11.10: {}
@ -16236,16 +16410,16 @@ snapshots:
mkdirp: 1.0.4
yallist: 4.0.0
terser-webpack-plugin@5.3.10(@swc/core@1.5.25)(webpack@5.94.0(@swc/core@1.5.25)):
terser-webpack-plugin@5.3.10(@swc/core@1.5.25(@swc/helpers@0.5.5))(webpack@5.94.0(@swc/core@1.5.25(@swc/helpers@0.5.5))):
dependencies:
'@jridgewell/trace-mapping': 0.3.25
jest-worker: 27.5.1
schema-utils: 3.3.0
serialize-javascript: 6.0.2
terser: 5.29.2
webpack: 5.94.0(@swc/core@1.5.25)
webpack: 5.94.0(@swc/core@1.5.25(@swc/helpers@0.5.5))
optionalDependencies:
'@swc/core': 1.5.25
'@swc/core': 1.5.25(@swc/helpers@0.5.5)
terser@5.29.2:
dependencies:
@ -16319,12 +16493,12 @@ snapshots:
ts-dedent@2.2.0: {}
ts-jest@29.2.5(@babel/core@7.24.3)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.3))(jest@29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25)(@types/node@22.5.2)(typescript@5.5.4)))(typescript@5.5.4):
ts-jest@29.2.5(@babel/core@7.24.3)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.3))(jest@29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(typescript@5.5.4)))(typescript@5.5.4):
dependencies:
bs-logger: 0.2.6
ejs: 3.1.10
fast-json-stable-stringify: 2.1.0
jest: 29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25)(@types/node@22.5.2)(typescript@5.5.4))
jest: 29.7.0(@types/node@22.5.2)(ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(typescript@5.5.4))
jest-util: 29.7.0
json5: 2.2.3
lodash.memoize: 4.1.2
@ -16338,7 +16512,7 @@ snapshots:
'@jest/types': 29.6.3
babel-jest: 29.7.0(@babel/core@7.24.3)
ts-loader@9.5.1(typescript@5.5.4)(webpack@5.94.0(@swc/core@1.5.25)):
ts-loader@9.5.1(typescript@5.5.4)(webpack@5.94.0(@swc/core@1.5.25(@swc/helpers@0.5.5))):
dependencies:
chalk: 4.1.2
enhanced-resolve: 5.16.0
@ -16346,9 +16520,9 @@ snapshots:
semver: 7.6.0
source-map: 0.7.4
typescript: 5.5.4
webpack: 5.94.0(@swc/core@1.5.25)
webpack: 5.94.0(@swc/core@1.5.25(@swc/helpers@0.5.5))
ts-node@10.9.1(@swc/core@1.5.25)(@types/node@22.5.2)(typescript@5.5.4):
ts-node@10.9.1(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(typescript@5.5.4):
dependencies:
'@cspotcode/source-map-support': 0.8.1
'@tsconfig/node10': 1.0.9
@ -16366,9 +16540,9 @@ snapshots:
v8-compile-cache-lib: 3.0.1
yn: 3.1.1
optionalDependencies:
'@swc/core': 1.5.25
'@swc/core': 1.5.25(@swc/helpers@0.5.5)
ts-node@10.9.2(@swc/core@1.5.25)(@types/node@22.5.2)(typescript@5.5.4):
ts-node@10.9.2(@swc/core@1.5.25(@swc/helpers@0.5.5))(@types/node@22.5.2)(typescript@5.5.4):
dependencies:
'@cspotcode/source-map-support': 0.8.1
'@tsconfig/node10': 1.0.9
@ -16386,7 +16560,7 @@ snapshots:
v8-compile-cache-lib: 3.0.1
yn: 3.1.1
optionalDependencies:
'@swc/core': 1.5.25
'@swc/core': 1.5.25(@swc/helpers@0.5.5)
tsconfig-paths-webpack-plugin@4.1.0:
dependencies:
@ -16533,6 +16707,22 @@ snapshots:
vary@1.1.2: {}
vasync@2.2.1:
dependencies:
verror: 1.10.0
verror@1.10.0:
dependencies:
assert-plus: 1.0.0
core-util-is: 1.0.2
extsprintf: 1.4.1
verror@1.10.1:
dependencies:
assert-plus: 1.0.0
core-util-is: 1.0.2
extsprintf: 1.4.1
vite@5.4.2(@types/node@22.5.2)(less@4.2.0)(sugarss@4.0.1(postcss@8.4.43))(terser@5.29.2):
dependencies:
esbuild: 0.21.5
@ -16589,7 +16779,7 @@ snapshots:
webpack-sources@3.2.3: {}
webpack@5.94.0(@swc/core@1.5.25):
webpack@5.94.0(@swc/core@1.5.25(@swc/helpers@0.5.5)):
dependencies:
'@types/estree': 1.0.5
'@webassemblyjs/ast': 1.12.1
@ -16611,7 +16801,7 @@ snapshots:
neo-async: 2.6.2
schema-utils: 3.3.0
tapable: 2.2.1
terser-webpack-plugin: 5.3.10(@swc/core@1.5.25)(webpack@5.94.0(@swc/core@1.5.25))
terser-webpack-plugin: 5.3.10(@swc/core@1.5.25(@swc/helpers@0.5.5))(webpack@5.94.0(@swc/core@1.5.25(@swc/helpers@0.5.5)))
watchpack: 2.4.1
webpack-sources: 3.2.3
transitivePeerDependencies: