mirror of
https://github.com/docmost/docmost.git
synced 2025-11-27 07:33:37 +10:00
Merge branch 'main' into i18n
This commit is contained in:
@@ -21,6 +21,9 @@ AWS_S3_BUCKET=
|
||||
AWS_S3_ENDPOINT=
|
||||
AWS_S3_FORCE_PATH_STYLE=
|
||||
|
||||
# default: 50mb
|
||||
FILE_UPLOAD_SIZE_LIMIT=
|
||||
|
||||
# options: smtp | postmark
|
||||
MAIL_DRIVER=smtp
|
||||
MAIL_FROM_ADDRESS=hello@example.com
|
||||
@@ -32,6 +35,7 @@ SMTP_PORT=587
|
||||
SMTP_USERNAME=
|
||||
SMTP_PASSWORD=
|
||||
SMTP_SECURE=false
|
||||
SMTP_IGNORETLS=false
|
||||
|
||||
# Postmark driver config
|
||||
POSTMARK_TOKEN=
|
||||
|
||||
@@ -30,6 +30,9 @@ COPY --from=builder /app/packages/editor-ext/package.json /app/packages/editor-e
|
||||
COPY --from=builder /app/package.json /app/package.json
|
||||
COPY --from=builder /app/pnpm*.yaml /app/
|
||||
|
||||
# Copy patches
|
||||
COPY --from=builder /app/patches /app/patches
|
||||
|
||||
RUN npm install -g pnpm
|
||||
|
||||
RUN chown -R node:node /app
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "client",
|
||||
"private": true,
|
||||
"version": "0.3.1",
|
||||
"version": "0.4.1",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
@@ -9,6 +9,7 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@docmost/editor-ext": "workspace:*",
|
||||
"@casl/ability": "^6.7.1",
|
||||
"@casl/react": "^4.0.0",
|
||||
"@emoji-mart/data": "^1.2.1",
|
||||
@@ -70,6 +71,6 @@
|
||||
"postcss-simple-vars": "^7.0.1",
|
||||
"prettier": "^3.3.3",
|
||||
"typescript": "^5.5.4",
|
||||
"vite": "^5.4.2"
|
||||
"vite": "^5.4.8"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,9 @@ import {
|
||||
UnstyledButton,
|
||||
Badge,
|
||||
Table,
|
||||
ScrollArea,
|
||||
ActionIcon,
|
||||
} from '@mantine/core';
|
||||
import { Link } from 'react-router-dom';
|
||||
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';
|
||||
@@ -19,13 +18,13 @@ import { useTranslation } from "react-i18next";
|
||||
interface Props {
|
||||
spaceId?: string;
|
||||
}
|
||||
export default function RecentChanges({ spaceId }: Props) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { data: pages, isLoading, isError } = useRecentChangesQuery(spaceId);
|
||||
export default function RecentChanges({spaceId}: Props) {
|
||||
const { t } = useTranslation();
|
||||
const {data: pages, isLoading, isError} = useRecentChangesQuery(spaceId);
|
||||
|
||||
if (isLoading) {
|
||||
return <PageListSkeleton />;
|
||||
return <PageListSkeleton/>;
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
@@ -33,7 +32,7 @@ export default function RecentChanges({ spaceId }: Props) {
|
||||
}
|
||||
|
||||
return pages && pages.items.length > 0 ? (
|
||||
<ScrollArea>
|
||||
<Table.ScrollContainer minWidth={500}>
|
||||
<Table highlightOnHover verticalSpacing="sm">
|
||||
<Table.Tbody>
|
||||
{pages.items.map((page) => (
|
||||
@@ -46,7 +45,7 @@ export default function RecentChanges({ spaceId }: Props) {
|
||||
<Group wrap="nowrap">
|
||||
{page.icon || (
|
||||
<ActionIcon variant='transparent' color='gray' size={18}>
|
||||
<IconFileDescription size={18} />
|
||||
<IconFileDescription size={18}/>
|
||||
</ActionIcon>
|
||||
)}
|
||||
|
||||
@@ -63,14 +62,14 @@ 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>
|
||||
</Table.Td>
|
||||
)}
|
||||
<Table.Td>
|
||||
<Text c="dimmed" size="xs" fw={500}>
|
||||
<Text c="dimmed" style={{whiteSpace: 'nowrap'}} size="xs" fw={500}>
|
||||
{formattedDate(page.updatedAt)}
|
||||
</Text>
|
||||
</Table.Td>
|
||||
@@ -78,7 +77,7 @@ export default function RecentChanges({ spaceId }: Props) {
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</ScrollArea>
|
||||
</Table.ScrollContainer>
|
||||
) : (
|
||||
<Text size="md" ta="center">
|
||||
{t("No pages yet")}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { Group, Text } from "@mantine/core";
|
||||
import {Group, Text, Tooltip} from "@mantine/core";
|
||||
import classes from "./app-header.module.css";
|
||||
import React from "react";
|
||||
import TopMenu from "@/components/layouts/global/top-menu.tsx";
|
||||
import { Link } from "react-router-dom";
|
||||
import {Link} from "react-router-dom";
|
||||
import APP_ROUTE from "@/lib/app-route.ts";
|
||||
import { useAtom } from "jotai/index";
|
||||
import {useAtom} from "jotai/index";
|
||||
import {
|
||||
desktopSidebarAtom,
|
||||
mobileSidebarAtom,
|
||||
} from "@/components/layouts/global/hooks/atoms/sidebar-atom.ts";
|
||||
import { useToggleSidebar } from "@/components/layouts/global/hooks/hooks/use-toggle-sidebar.ts";
|
||||
import {useToggleSidebar} from "@/components/layouts/global/hooks/hooks/use-toggle-sidebar.ts";
|
||||
import SidebarToggle from "@/components/ui/sidebar-toggle-button.tsx";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const links = [{ link: APP_ROUTE.HOME, label: "Home" }];
|
||||
const links = [{link: APP_ROUTE.HOME, label: "Home"}];
|
||||
|
||||
export function AppHeader() {
|
||||
const { t } = useTranslation();
|
||||
@@ -37,28 +37,33 @@ export function AppHeader() {
|
||||
<Group wrap="nowrap">
|
||||
{!isHomeRoute && (
|
||||
<>
|
||||
<SidebarToggle
|
||||
aria-label="sidebar toggle"
|
||||
opened={mobileOpened}
|
||||
onClick={toggleMobile}
|
||||
hiddenFrom="sm"
|
||||
size="sm"
|
||||
/>
|
||||
<Tooltip label="Sidebar toggle">
|
||||
|
||||
<SidebarToggle
|
||||
aria-label="sidebar toggle"
|
||||
opened={desktopOpened}
|
||||
onClick={toggleDesktop}
|
||||
visibleFrom="sm"
|
||||
size="sm"
|
||||
/>
|
||||
<SidebarToggle
|
||||
aria-label="Sidebar toggle"
|
||||
opened={mobileOpened}
|
||||
onClick={toggleMobile}
|
||||
hiddenFrom="sm"
|
||||
size="sm"
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip label="Sidebar toggle">
|
||||
<SidebarToggle
|
||||
aria-label="Sidebar toggle"
|
||||
opened={desktopOpened}
|
||||
onClick={toggleDesktop}
|
||||
visibleFrom="sm"
|
||||
size="sm"
|
||||
/>
|
||||
</Tooltip>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Text
|
||||
size="lg"
|
||||
fw={600}
|
||||
style={{ cursor: "pointer", userSelect: "none" }}
|
||||
style={{cursor: "pointer", userSelect: "none"}}
|
||||
component={Link}
|
||||
to="/home"
|
||||
>
|
||||
@@ -71,7 +76,7 @@ export function AppHeader() {
|
||||
</Group>
|
||||
|
||||
<Group px={"xl"}>
|
||||
<TopMenu />
|
||||
<TopMenu/>
|
||||
</Group>
|
||||
</Group>
|
||||
</>
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
import React from "react";
|
||||
import {
|
||||
IconLayoutSidebarRightCollapse,
|
||||
IconLayoutSidebarRightExpand,
|
||||
IconLayoutSidebarRightExpand
|
||||
} from "@tabler/icons-react";
|
||||
import {
|
||||
ActionIcon,
|
||||
BoxProps,
|
||||
ElementProps,
|
||||
MantineColor,
|
||||
MantineSize,
|
||||
} from "@mantine/core";
|
||||
import React from "react";
|
||||
import { ActionIcon, BoxProps, ElementProps, MantineColor, MantineSize } from "@mantine/core";
|
||||
|
||||
export interface SidebarToggleProps extends BoxProps, ElementProps<"button"> {
|
||||
size?: MantineSize | `compact-${MantineSize}` | (string & {});
|
||||
@@ -17,18 +11,18 @@ export interface SidebarToggleProps extends BoxProps, ElementProps<"button"> {
|
||||
opened?: boolean;
|
||||
}
|
||||
|
||||
export default function SidebarToggle({
|
||||
opened,
|
||||
size = "sm",
|
||||
...others
|
||||
}: SidebarToggleProps) {
|
||||
return (
|
||||
<ActionIcon size={size} {...others} variant="subtle" color="gray">
|
||||
{opened ? (
|
||||
<IconLayoutSidebarRightExpand />
|
||||
) : (
|
||||
<IconLayoutSidebarRightCollapse />
|
||||
)}
|
||||
</ActionIcon>
|
||||
);
|
||||
}
|
||||
const SidebarToggle = React.forwardRef<HTMLButtonElement, SidebarToggleProps>(
|
||||
({ opened, size = "sm", ...others }, ref) => {
|
||||
return (
|
||||
<ActionIcon size={size} {...others} variant="subtle" color="gray" ref={ref}>
|
||||
{opened ? (
|
||||
<IconLayoutSidebarRightExpand />
|
||||
) : (
|
||||
<IconLayoutSidebarRightCollapse />
|
||||
)}
|
||||
</ActionIcon>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default SidebarToggle;
|
||||
|
||||
@@ -20,7 +20,7 @@ import { useRedirectIfAuthenticated } from "@/features/auth/hooks/use-redirect-i
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const formSchema = z.object({
|
||||
name: z.string().min(2),
|
||||
name: z.string().trim().min(1),
|
||||
password: z.string().min(8),
|
||||
});
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ import classes from "@/features/auth/components/auth.module.css";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const formSchema = z.object({
|
||||
workspaceName: z.string().min(2).max(60),
|
||||
name: z.string().min(2).max(60),
|
||||
workspaceName: z.string().trim().min(3).max(50),
|
||||
name: z.string().min(1).max(50),
|
||||
email: z
|
||||
.string()
|
||||
.min(1, { message: "email is required" })
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { handleAttachmentUpload } from "@docmost/editor-ext";
|
||||
import { uploadFile } from "@/features/page/services/page-service.ts";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import {getFileUploadSizeLimit} from "@/lib/config.ts";
|
||||
import {formatBytes} from "@/lib";
|
||||
|
||||
export const uploadAttachmentAction = handleAttachmentUpload({
|
||||
onUpload: async (file: File, pageId: string): Promise<any> => {
|
||||
@@ -18,10 +20,10 @@ export const uploadAttachmentAction = handleAttachmentUpload({
|
||||
if (file.type.includes("image/") || file.type.includes("video/")) {
|
||||
return false;
|
||||
}
|
||||
if (file.size / 1024 / 1024 > 50) {
|
||||
if (file.size > getFileUploadSizeLimit()) {
|
||||
notifications.show({
|
||||
color: "red",
|
||||
message: `File exceeds the 50 MB attachment limit`,
|
||||
message: `File exceeds the ${formatBytes(getFileUploadSizeLimit())} attachment limit`,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { handleImageUpload } from "@docmost/editor-ext";
|
||||
import { uploadFile } from "@/features/page/services/page-service.ts";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import {getFileUploadSizeLimit} from "@/lib/config.ts";
|
||||
import { formatBytes } from "@/lib";
|
||||
|
||||
export const uploadImageAction = handleImageUpload({
|
||||
onUpload: async (file: File, pageId: string): Promise<any> => {
|
||||
@@ -18,10 +20,10 @@ export const uploadImageAction = handleImageUpload({
|
||||
if (!file.type.includes("image/")) {
|
||||
return false;
|
||||
}
|
||||
if (file.size / 1024 / 1024 > 50) {
|
||||
if (file.size > getFileUploadSizeLimit()) {
|
||||
notifications.show({
|
||||
color: "red",
|
||||
message: `File exceeds the 50 MB attachment limit`,
|
||||
message: `File exceeds the ${formatBytes(getFileUploadSizeLimit())} attachment limit`,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -17,10 +17,6 @@
|
||||
color: light-dark(var(--mantine-color-red-8), var(--mantine-color-red-7));
|
||||
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-gray-8));
|
||||
}
|
||||
|
||||
&:not(.error, .empty) * {
|
||||
font-family: KaTeX_Main, Times New Roman, serif;
|
||||
}
|
||||
}
|
||||
|
||||
.mathBlock {
|
||||
@@ -33,7 +29,7 @@
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.2s;
|
||||
margin: 0 0.1rem;
|
||||
overflow-x: scroll;
|
||||
overflow-x: auto;
|
||||
|
||||
.textInput {
|
||||
width: 400px;
|
||||
@@ -52,10 +48,4 @@
|
||||
color: light-dark(var(--mantine-color-red-8), var(--mantine-color-red-7));
|
||||
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-gray-8));
|
||||
}
|
||||
|
||||
&:not(.error, .empty) * {
|
||||
font-family: KaTeX_Main, Times New Roman, serif;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { handleVideoUpload } from "@docmost/editor-ext";
|
||||
import { uploadFile } from "@/features/page/services/page-service.ts";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import {getFileUploadSizeLimit} from "@/lib/config.ts";
|
||||
import {formatBytes} from "@/lib";
|
||||
|
||||
export const uploadVideoAction = handleVideoUpload({
|
||||
onUpload: async (file: File, pageId: string): Promise<any> => {
|
||||
@@ -19,13 +21,14 @@ export const uploadVideoAction = handleVideoUpload({
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.size / 1024 / 1024 > 50) {
|
||||
if (file.size > getFileUploadSizeLimit()) {
|
||||
notifications.show({
|
||||
color: "red",
|
||||
message: `File exceeds the 50 MB attachment limit`,
|
||||
message: `File exceeds the ${formatBytes(getFileUploadSizeLimit())} attachment limit`,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { MultiUserSelect } from "@/features/group/components/multi-user-select.t
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const formSchema = z.object({
|
||||
name: z.string().min(2).max(50),
|
||||
name: z.string().trim().min(2).max(50),
|
||||
description: z.string().max(500),
|
||||
});
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ export function EditGroupForm({ onClose }: EditGroupFormProps) {
|
||||
</Stack>
|
||||
|
||||
<Group justify="flex-end" mt="md">
|
||||
<Button type="submit">{t("Edit")}</Button>
|
||||
<Button type="submit">{t("Save")}</Button>
|
||||
</Group>
|
||||
</form>
|
||||
</Box>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Table, Group, Text, Anchor } from "@mantine/core";
|
||||
import { useGetGroupsQuery } from "@/features/group/queries/group-query";
|
||||
import {Table, Group, Text, Anchor} from "@mantine/core";
|
||||
import {useGetGroupsQuery} from "@/features/group/queries/group-query";
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { IconGroupCircle } from "@/components/icons/icon-people-circle.tsx";
|
||||
@@ -13,60 +13,62 @@ export default function GroupList() {
|
||||
return (
|
||||
<>
|
||||
{data && (
|
||||
<Table highlightOnHover verticalSpacing="sm">
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>{t("Group")}</Table.Th>
|
||||
<Table.Th>{t("Members")}</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
|
||||
<Table.Tbody>
|
||||
{data?.items.map((group, index) => (
|
||||
<Table.Tr key={index}>
|
||||
<Table.Td>
|
||||
<Anchor
|
||||
size="sm"
|
||||
underline="never"
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
color: "var(--mantine-color-text)",
|
||||
}}
|
||||
component={Link}
|
||||
to={`/settings/groups/${group.id}`}
|
||||
>
|
||||
<Group gap="sm">
|
||||
<IconGroupCircle />
|
||||
<div>
|
||||
<Text fz="sm" fw={500}>
|
||||
{group.name}
|
||||
</Text>
|
||||
<Text fz="xs" c="dimmed">
|
||||
{group.description}
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
</Anchor>
|
||||
</Table.Td>
|
||||
|
||||
<Table.Td>
|
||||
<Anchor
|
||||
size="sm"
|
||||
underline="never"
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
color: "var(--mantine-color-text)",
|
||||
}}
|
||||
component={Link}
|
||||
to={`/settings/groups/${group.id}`}
|
||||
>
|
||||
{formatMemberCount(group.memberCount, t)}
|
||||
</Anchor>
|
||||
</Table.Td>
|
||||
<Table.ScrollContainer minWidth={400}>
|
||||
<Table highlightOnHover verticalSpacing="sm">
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>{t("Group")}</Table.Th>
|
||||
<Table.Th>{t("Members")}</Table.Th>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Table.Thead>
|
||||
|
||||
<Table.Tbody>
|
||||
{data?.items.map((group, index) => (
|
||||
<Table.Tr key={index}>
|
||||
<Table.Td>
|
||||
<Anchor
|
||||
size="sm"
|
||||
underline="never"
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
color: "var(--mantine-color-text)",
|
||||
}}
|
||||
component={Link}
|
||||
to={`/settings/groups/${group.id}`}
|
||||
>
|
||||
<Group gap="sm" wrap="nowrap">
|
||||
<IconGroupCircle/>
|
||||
<div>
|
||||
<Text fz="sm" fw={500} lineClamp={1}>
|
||||
{group.name}
|
||||
</Text>
|
||||
<Text fz="xs" c="dimmed" lineClamp={2}>
|
||||
{group.description}
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
</Anchor>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Anchor
|
||||
size="sm"
|
||||
underline="never"
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
color: "var(--mantine-color-text)",
|
||||
whiteSpace: "nowrap"
|
||||
}}
|
||||
component={Link}
|
||||
to={`/settings/groups/${group.id}`}
|
||||
>
|
||||
{group.memberCount} members
|
||||
</Anchor>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Table.ScrollContainer>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Group, Table, Text, Badge, Menu, ActionIcon } from "@mantine/core";
|
||||
import {Group, Table, Text, Badge, Menu, ActionIcon} from "@mantine/core";
|
||||
import {
|
||||
useGroupMembersQuery,
|
||||
useRemoveGroupMemberMutation,
|
||||
} from "@/features/group/queries/group-query";
|
||||
import { useParams } from "react-router-dom";
|
||||
import {useParams} from "react-router-dom";
|
||||
import React from "react";
|
||||
import { IconDots } from "@tabler/icons-react";
|
||||
import { modals } from "@mantine/modals";
|
||||
import { CustomAvatar } from "@/components/ui/custom-avatar.tsx";
|
||||
import {IconDots} from "@tabler/icons-react";
|
||||
import {modals} from "@mantine/modals";
|
||||
import {CustomAvatar} from "@/components/ui/custom-avatar.tsx";
|
||||
import useUserRole from "@/hooks/use-user-role.tsx";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
@@ -16,7 +16,7 @@ export default function GroupMembersList() {
|
||||
const { groupId } = useParams();
|
||||
const { data, isLoading } = useGroupMembersQuery(groupId);
|
||||
const removeGroupMember = useRemoveGroupMemberMutation();
|
||||
const { isAdmin } = useUserRole();
|
||||
const {isAdmin} = useUserRole();
|
||||
|
||||
const onRemove = async (userId: string) => {
|
||||
const memberToRemove = {
|
||||
@@ -45,64 +45,63 @@ export default function GroupMembersList() {
|
||||
return (
|
||||
<>
|
||||
{data && (
|
||||
<Table verticalSpacing="sm">
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>{t("User")}</Table.Th>
|
||||
<Table.Th>{t("Status")}</Table.Th>
|
||||
<Table.Th></Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
|
||||
<Table.Tbody>
|
||||
{data?.items.map((user, index) => (
|
||||
<Table.Tr key={index}>
|
||||
<Table.Td>
|
||||
<Group gap="sm">
|
||||
<CustomAvatar avatarUrl={user.avatarUrl} name={user.name} />
|
||||
<div>
|
||||
<Text fz="sm" fw={500}>
|
||||
{user.name}
|
||||
</Text>
|
||||
<Text fz="xs" c="dimmed">
|
||||
{user.email}
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
</Table.Td>
|
||||
|
||||
<Table.Td>
|
||||
<Badge variant="light">{t("Active")}</Badge>
|
||||
</Table.Td>
|
||||
|
||||
<Table.Td>
|
||||
{isAdmin && (
|
||||
<Menu
|
||||
shadow="xl"
|
||||
position="bottom-end"
|
||||
offset={20}
|
||||
width={200}
|
||||
withArrow
|
||||
arrowPosition="center"
|
||||
>
|
||||
<Menu.Target>
|
||||
<ActionIcon variant="subtle" c="gray">
|
||||
<IconDots size={20} stroke={2} />
|
||||
</ActionIcon>
|
||||
</Menu.Target>
|
||||
|
||||
<Menu.Dropdown>
|
||||
<Menu.Item onClick={() => openRemoveModal(user.id)}>
|
||||
{t("Remove group member")}
|
||||
</Menu.Item>
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
)}
|
||||
</Table.Td>
|
||||
<Table.ScrollContainer minWidth={500}>
|
||||
<Table verticalSpacing="sm">
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>{t("User")}</Table.Th>
|
||||
<Table.Th>{t("Status")}</Table.Th>
|
||||
<Table.Th></Table.Th>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Table.Thead>
|
||||
|
||||
<Table.Tbody>
|
||||
{data?.items.map((user, index) => (
|
||||
<Table.Tr key={index}>
|
||||
<Table.Td>
|
||||
<Group gap="sm">
|
||||
<CustomAvatar avatarUrl={user.avatarUrl} name={user.name}/>
|
||||
<div>
|
||||
<Text fz="sm" fw={500}>
|
||||
{user.name}
|
||||
</Text>
|
||||
<Text fz="xs" c="dimmed">
|
||||
{user.email}
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Badge variant="light">{t("Active")}</Badge>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
{isAdmin && (
|
||||
<Menu
|
||||
shadow="xl"
|
||||
position="bottom-end"
|
||||
offset={20}
|
||||
width={200}
|
||||
withArrow
|
||||
arrowPosition="center"
|
||||
>
|
||||
<Menu.Target>
|
||||
<ActionIcon variant="subtle" c="gray">
|
||||
<IconDots size={20} stroke={2}/>
|
||||
</ActionIcon>
|
||||
</Menu.Target>
|
||||
<Menu.Dropdown>
|
||||
<Menu.Item onClick={() => openRemoveModal(user.id)}>
|
||||
{t("Remove group member")}
|
||||
</Menu.Item>
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
)}
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Table.ScrollContainer>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -29,24 +29,22 @@ export function useGetGroupsQuery(
|
||||
|
||||
export function useGroupQuery(groupId: string): UseQueryResult<IGroup, Error> {
|
||||
return useQuery({
|
||||
queryKey: ['groups', groupId],
|
||||
queryKey: ['group', groupId],
|
||||
queryFn: () => getGroupById(groupId),
|
||||
enabled: !!groupId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useGroupMembersQuery(groupId: string) {
|
||||
return useQuery({
|
||||
queryKey: ['groupMembers', groupId],
|
||||
queryFn: () => getGroupMembers(groupId),
|
||||
enabled: !!groupId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateGroupMutation() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<IGroup, Error, Partial<IGroup>>({
|
||||
mutationFn: (data) => createGroup(data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ['groups'],
|
||||
});
|
||||
|
||||
notifications.show({ message: 'Group created successfully' });
|
||||
},
|
||||
onError: () => {
|
||||
@@ -96,6 +94,14 @@ export function useDeleteGroupMutation() {
|
||||
});
|
||||
}
|
||||
|
||||
export function useGroupMembersQuery(groupId: string) {
|
||||
return useQuery({
|
||||
queryKey: ['groupMembers', groupId],
|
||||
queryFn: () => getGroupMembers(groupId),
|
||||
enabled: !!groupId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useAddGroupMemberMutation() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
|
||||
@@ -9,9 +9,10 @@ import { getSpaceUrl } from "@/lib/config.ts";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const formSchema = z.object({
|
||||
name: z.string().min(2).max(50),
|
||||
name: z.string().trim().min(2).max(50),
|
||||
slug: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(2)
|
||||
.max(50)
|
||||
.regex(
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Modal, Tabs, rem, Group, ScrollArea } from "@mantine/core";
|
||||
import {Modal, Tabs, rem, Group, ScrollArea, Text} from "@mantine/core";
|
||||
import SpaceMembersList from "@/features/space/components/space-members.tsx";
|
||||
import AddSpaceMembersModal from "@/features/space/components/add-space-members-modal.tsx";
|
||||
import React, { useMemo } from "react";
|
||||
import React, {useMemo} from "react";
|
||||
import SpaceDetails from "@/features/space/components/space-details.tsx";
|
||||
import { useSpaceQuery } from "@/features/space/queries/space-query.ts";
|
||||
import { useSpaceAbility } from "@/features/space/permissions/use-space-ability.ts";
|
||||
import {useSpaceQuery} from "@/features/space/queries/space-query.ts";
|
||||
import {useSpaceAbility} from "@/features/space/permissions/use-space-ability.ts";
|
||||
import {
|
||||
SpaceCaslAction,
|
||||
SpaceCaslSubject,
|
||||
@@ -39,14 +39,16 @@ export default function SpaceSettingsModal({
|
||||
xOffset={0}
|
||||
mah={400}
|
||||
>
|
||||
<Modal.Overlay />
|
||||
<Modal.Content style={{ overflow: "hidden" }}>
|
||||
<Modal.Overlay/>
|
||||
<Modal.Content style={{overflow: "hidden"}}>
|
||||
<Modal.Header py={0}>
|
||||
<Modal.Title fw={500}>{space?.name}</Modal.Title>
|
||||
<Modal.CloseButton />
|
||||
<Modal.Title>
|
||||
<Text fw={500} lineClamp={1}>{space?.name}</Text>
|
||||
</Modal.Title>
|
||||
<Modal.CloseButton/>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<div style={{ height: rem("600px") }}>
|
||||
<div style={{height: rem(600)}}>
|
||||
<Tabs defaultValue="members">
|
||||
<Tabs.List>
|
||||
<Tabs.Tab fw={500} value="general">
|
||||
@@ -57,34 +59,32 @@ export default function SpaceSettingsModal({
|
||||
</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
|
||||
<ScrollArea h="600" w="100%" scrollbarSize={5}>
|
||||
<Tabs.Panel value="general">
|
||||
<SpaceDetails
|
||||
spaceId={space?.id}
|
||||
readOnly={spaceAbility.cannot(
|
||||
SpaceCaslAction.Manage,
|
||||
SpaceCaslSubject.Settings,
|
||||
)}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
<Tabs.Panel value="general">
|
||||
<SpaceDetails
|
||||
spaceId={space?.id}
|
||||
readOnly={spaceAbility.cannot(
|
||||
SpaceCaslAction.Manage,
|
||||
SpaceCaslSubject.Settings,
|
||||
)}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
|
||||
<Tabs.Panel value="members">
|
||||
<Group my="md" justify="flex-end">
|
||||
{spaceAbility.can(
|
||||
SpaceCaslAction.Manage,
|
||||
SpaceCaslSubject.Member,
|
||||
) && <AddSpaceMembersModal spaceId={space?.id} />}
|
||||
</Group>
|
||||
<Tabs.Panel value="members">
|
||||
<Group my="md" justify="flex-end">
|
||||
{spaceAbility.can(
|
||||
SpaceCaslAction.Manage,
|
||||
SpaceCaslSubject.Member,
|
||||
) && <AddSpaceMembersModal spaceId={space?.id}/>}
|
||||
</Group>
|
||||
|
||||
<SpaceMembersList
|
||||
spaceId={space?.id}
|
||||
readOnly={spaceAbility.cannot(
|
||||
SpaceCaslAction.Manage,
|
||||
SpaceCaslSubject.Member,
|
||||
)}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
</ScrollArea>
|
||||
<SpaceMembersList
|
||||
spaceId={space?.id}
|
||||
readOnly={spaceAbility.cannot(
|
||||
SpaceCaslAction.Manage,
|
||||
SpaceCaslSubject.Member,
|
||||
)}
|
||||
/>
|
||||
</Tabs.Panel>
|
||||
</Tabs>
|
||||
</div>
|
||||
</Modal.Body>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Table, Group, Text, Avatar } from "@mantine/core";
|
||||
import React, { useState } from "react";
|
||||
import { useGetSpacesQuery } from "@/features/space/queries/space-query.ts";
|
||||
import {Table, Group, Text, Avatar} from "@mantine/core";
|
||||
import React, {useState} from "react";
|
||||
import {useGetSpacesQuery} from "@/features/space/queries/space-query.ts";
|
||||
import SpaceSettingsModal from "@/features/space/components/settings-modal.tsx";
|
||||
import { useDisclosure } from "@mantine/hooks";
|
||||
import { formatMemberCount } from "@/lib";
|
||||
@@ -20,44 +20,47 @@ export default function SpaceList() {
|
||||
return (
|
||||
<>
|
||||
{data && (
|
||||
<Table highlightOnHover verticalSpacing="sm">
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>{t("Space")}</Table.Th>
|
||||
<Table.Th>{t("Members")}</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
|
||||
<Table.Tbody>
|
||||
{data?.items.map((space, index) => (
|
||||
<Table.Tr
|
||||
key={index}
|
||||
style={{ cursor: "pointer" }}
|
||||
onClick={() => handleClick(space.id)}
|
||||
>
|
||||
<Table.Td>
|
||||
<Group gap="sm">
|
||||
<Avatar
|
||||
color="initials"
|
||||
variant="filled"
|
||||
name={space.name}
|
||||
/>
|
||||
<div>
|
||||
<Text fz="sm" fw={500}>
|
||||
{space.name}
|
||||
</Text>
|
||||
<Text fz="xs" c="dimmed">
|
||||
{space.description}
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
</Table.Td>
|
||||
|
||||
<Table.Td>{formatMemberCount(space.memberCount, t)}</Table.Td>
|
||||
<Table.ScrollContainer minWidth={400}>
|
||||
<Table highlightOnHover verticalSpacing="sm">
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>{t("Space")}</Table.Th>
|
||||
<Table.Th>{t("Members")}</Table.Th>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Table.Thead>
|
||||
|
||||
<Table.Tbody>
|
||||
{data?.items.map((space, index) => (
|
||||
<Table.Tr
|
||||
key={index}
|
||||
style={{cursor: "pointer"}}
|
||||
onClick={() => handleClick(space.id)}
|
||||
>
|
||||
<Table.Td>
|
||||
<Group gap="sm" wrap="nowrap">
|
||||
<Avatar
|
||||
color="initials"
|
||||
variant="filled"
|
||||
name={space.name}
|
||||
/>
|
||||
<div>
|
||||
<Text fz="sm" fw={500} lineClamp={1}>
|
||||
{space.name}
|
||||
</Text>
|
||||
<Text fz="xs" c="dimmed" lineClamp={2}>
|
||||
{space.description}
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Text size="sm" style={{whiteSpace: 'nowrap'}}>{formatMemberCount(space.memberCount)}</Text>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Table.ScrollContainer>
|
||||
)}
|
||||
|
||||
{selectedSpaceId && (
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { Group, Table, Text, Menu, ActionIcon } from "@mantine/core";
|
||||
import {Group, Table, Text, Menu, ActionIcon} from "@mantine/core";
|
||||
import React from "react";
|
||||
import { IconDots } from "@tabler/icons-react";
|
||||
import { modals } from "@mantine/modals";
|
||||
import { CustomAvatar } from "@/components/ui/custom-avatar.tsx";
|
||||
import {IconDots} from "@tabler/icons-react";
|
||||
import {modals} from "@mantine/modals";
|
||||
import {CustomAvatar} from "@/components/ui/custom-avatar.tsx";
|
||||
import {
|
||||
useChangeSpaceMemberRoleMutation,
|
||||
useRemoveSpaceMemberMutation,
|
||||
useSpaceMembersQuery,
|
||||
} from "@/features/space/queries/space-query.ts";
|
||||
import { IconGroupCircle } from "@/components/icons/icon-people-circle.tsx";
|
||||
import { IRemoveSpaceMember } from "@/features/space/types/space.types.ts";
|
||||
import {IconGroupCircle} from "@/components/icons/icon-people-circle.tsx";
|
||||
import {IRemoveSpaceMember} from "@/features/space/types/space.types.ts";
|
||||
import RoleSelectMenu from "@/components/ui/role-select-menu.tsx";
|
||||
import {
|
||||
getSpaceRoleLabel,
|
||||
@@ -19,16 +19,19 @@ import { formatMemberCount } from "@/lib";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
type MemberType = "user" | "group";
|
||||
|
||||
interface SpaceMembersProps {
|
||||
spaceId: string;
|
||||
readOnly?: boolean;
|
||||
}
|
||||
|
||||
export default function SpaceMembersList({
|
||||
spaceId,
|
||||
readOnly,
|
||||
}: SpaceMembersProps) {
|
||||
const { t } = useTranslation();
|
||||
const { data, isLoading } = useSpaceMembersQuery(spaceId);
|
||||
|
||||
const removeSpaceMember = useRemoveSpaceMemberMutation();
|
||||
const changeSpaceMemberRoleMutation = useChangeSpaceMemberRoleMutation();
|
||||
|
||||
@@ -96,91 +99,93 @@ export default function SpaceMembersList({
|
||||
return (
|
||||
<>
|
||||
{data && (
|
||||
<Table verticalSpacing={8}>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>{t("Member")}</Table.Th>
|
||||
<Table.Th>{t("Role")}</Table.Th>
|
||||
<Table.Th></Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
|
||||
<Table.Tbody>
|
||||
{data?.items.map((member, index) => (
|
||||
<Table.Tr key={index}>
|
||||
<Table.Td>
|
||||
<Group gap="sm">
|
||||
{member.type === "user" && (
|
||||
<CustomAvatar
|
||||
avatarUrl={member?.avatarUrl}
|
||||
name={member.name}
|
||||
/>
|
||||
)}
|
||||
|
||||
{member.type === "group" && <IconGroupCircle />}
|
||||
|
||||
<div>
|
||||
<Text fz="sm" fw={500}>
|
||||
{member?.name}
|
||||
</Text>
|
||||
<Text fz="xs" c="dimmed">
|
||||
{member.type == "user" && member?.email}
|
||||
|
||||
{member.type == "group" &&
|
||||
`Group - ${formatMemberCount(member?.memberCount, t)}`}
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
</Table.Td>
|
||||
|
||||
<Table.Td>
|
||||
<RoleSelectMenu
|
||||
roles={spaceRoleData}
|
||||
roleName={getSpaceRoleLabel(member.role)}
|
||||
onChange={(newRole) =>
|
||||
handleRoleChange(
|
||||
member.id,
|
||||
member.type,
|
||||
newRole,
|
||||
member.role,
|
||||
)
|
||||
}
|
||||
disabled={readOnly}
|
||||
/>
|
||||
</Table.Td>
|
||||
|
||||
<Table.Td>
|
||||
{!readOnly && (
|
||||
<Menu
|
||||
shadow="xl"
|
||||
position="bottom-end"
|
||||
offset={20}
|
||||
width={200}
|
||||
withArrow
|
||||
arrowPosition="center"
|
||||
>
|
||||
<Menu.Target>
|
||||
<ActionIcon variant="subtle" c="gray">
|
||||
<IconDots size={20} stroke={2} />
|
||||
</ActionIcon>
|
||||
</Menu.Target>
|
||||
|
||||
<Menu.Dropdown>
|
||||
<Menu.Item
|
||||
onClick={() =>
|
||||
openRemoveModal(member.id, member.type)
|
||||
}
|
||||
>
|
||||
{t("Remove space member")}
|
||||
</Menu.Item>
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
)}
|
||||
</Table.Td>
|
||||
<Table.ScrollContainer minWidth={500}>
|
||||
<Table verticalSpacing={8}>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>{t("Member")}</Table.Th>
|
||||
<Table.Th>{t("Role")}</Table.Th>
|
||||
<Table.Th></Table.Th>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Table.Thead>
|
||||
|
||||
<Table.Tbody>
|
||||
{data?.items.map((member, index) => (
|
||||
<Table.Tr key={index}>
|
||||
<Table.Td>
|
||||
<Group gap="sm">
|
||||
{member.type === "user" && (
|
||||
<CustomAvatar
|
||||
avatarUrl={member?.avatarUrl}
|
||||
name={member.name}
|
||||
/>
|
||||
)}
|
||||
|
||||
{member.type === "group" && <IconGroupCircle/>}
|
||||
|
||||
<div>
|
||||
<Text fz="sm" fw={500}>
|
||||
{member?.name}
|
||||
</Text>
|
||||
<Text fz="xs" c="dimmed">
|
||||
{member.type == "user" && member?.email}
|
||||
|
||||
{member.type == "group" &&
|
||||
`Group - ${formatMemberCount(member?.memberCount)}`}
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
</Table.Td>
|
||||
|
||||
<Table.Td>
|
||||
<RoleSelectMenu
|
||||
roles={spaceRoleData}
|
||||
roleName={getSpaceRoleLabel(member.role)}
|
||||
onChange={(newRole) =>
|
||||
handleRoleChange(
|
||||
member.id,
|
||||
member.type,
|
||||
newRole,
|
||||
member.role,
|
||||
)
|
||||
}
|
||||
disabled={readOnly}
|
||||
/>
|
||||
</Table.Td>
|
||||
|
||||
<Table.Td>
|
||||
{!readOnly && (
|
||||
<Menu
|
||||
shadow="xl"
|
||||
position="bottom-end"
|
||||
offset={20}
|
||||
width={200}
|
||||
withArrow
|
||||
arrowPosition="center"
|
||||
>
|
||||
<Menu.Target>
|
||||
<ActionIcon variant="subtle" c="gray">
|
||||
<IconDots size={20} stroke={2}/>
|
||||
</ActionIcon>
|
||||
</Menu.Target>
|
||||
|
||||
<Menu.Dropdown>
|
||||
<Menu.Item
|
||||
onClick={() =>
|
||||
openRemoveModal(member.id, member.type)
|
||||
}
|
||||
>
|
||||
{t("Remove space member")}
|
||||
</Menu.Item>
|
||||
</Menu.Dropdown>
|
||||
</Menu>
|
||||
)}
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Table.ScrollContainer>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -36,7 +36,7 @@ export function useGetSpacesQuery(
|
||||
|
||||
export function useSpaceQuery(spaceId: string): UseQueryResult<ISpace, Error> {
|
||||
return useQuery({
|
||||
queryKey: ['spaces', spaceId],
|
||||
queryKey: ['space', spaceId],
|
||||
queryFn: () => getSpaceById(spaceId),
|
||||
enabled: !!spaceId,
|
||||
staleTime: 5 * 60 * 1000,
|
||||
@@ -65,7 +65,7 @@ export function useGetSpaceBySlugQuery(
|
||||
spaceId: string
|
||||
): UseQueryResult<ISpace, Error> {
|
||||
return useQuery({
|
||||
queryKey: ['spaces', spaceId],
|
||||
queryKey: ['space', spaceId],
|
||||
queryFn: () => getSpaceById(spaceId),
|
||||
enabled: !!spaceId,
|
||||
staleTime: 5 * 60 * 1000,
|
||||
@@ -111,7 +111,7 @@ export function useDeleteSpaceMutation() {
|
||||
|
||||
if (variables.slug) {
|
||||
queryClient.removeQueries({
|
||||
queryKey: ['spaces', variables.slug],
|
||||
queryKey: ['space', variables.slug],
|
||||
exact: true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Group, Table, Avatar, Text, Alert } from "@mantine/core";
|
||||
import { useWorkspaceInvitationsQuery } from "@/features/workspace/queries/workspace-query.ts";
|
||||
import {Group, Table, Avatar, Text, Alert} from "@mantine/core";
|
||||
import {useWorkspaceInvitationsQuery} from "@/features/workspace/queries/workspace-query.ts";
|
||||
import React from "react";
|
||||
import { getUserRoleLabel } from "@/features/workspace/types/user-role-data.ts";
|
||||
import {getUserRoleLabel} from "@/features/workspace/types/user-role-data.ts";
|
||||
import InviteActionMenu from "@/features/workspace/components/members/components/invite-action-menu.tsx";
|
||||
import { IconInfoCircle } from "@tabler/icons-react";
|
||||
import { formattedDate } from "@/lib/time.ts";
|
||||
import {IconInfoCircle} from "@tabler/icons-react";
|
||||
import {formattedDate, timeAgo} from "@/lib/time.ts";
|
||||
import useUserRole from "@/hooks/use-user-role.tsx";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
@@ -13,7 +13,7 @@ export default function WorkspaceInvitesTable() {
|
||||
const { data, isLoading } = useWorkspaceInvitationsQuery({
|
||||
limit: 100,
|
||||
});
|
||||
const { isAdmin } = useUserRole();
|
||||
const {isAdmin} = useUserRole();
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -25,42 +25,44 @@ export default function WorkspaceInvitesTable() {
|
||||
|
||||
{data && (
|
||||
<>
|
||||
<Table verticalSpacing="sm">
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>{t("Email")}</Table.Th>
|
||||
<Table.Th>{t("Role")}</Table.Th>
|
||||
<Table.Th>{t("Date")}</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
|
||||
<Table.Tbody>
|
||||
{data?.items.map((invitation, index) => (
|
||||
<Table.Tr key={index}>
|
||||
<Table.Td>
|
||||
<Group gap="sm">
|
||||
<Avatar name={invitation.email} color="initials" />
|
||||
<div>
|
||||
<Text fz="sm" fw={500}>
|
||||
{invitation.email}
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
</Table.Td>
|
||||
|
||||
<Table.Td>{getUserRoleLabel(invitation.role)}</Table.Td>
|
||||
|
||||
<Table.Td>{formattedDate(invitation.createdAt)}</Table.Td>
|
||||
|
||||
<Table.Td>
|
||||
{isAdmin && (
|
||||
<InviteActionMenu invitationId={invitation.id} />
|
||||
)}
|
||||
</Table.Td>
|
||||
<Table.ScrollContainer minWidth={500}>
|
||||
<Table verticalSpacing="sm">
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>{t("Email")}</Table.Th>
|
||||
<Table.Th>{t("Role")}</Table.Th>
|
||||
<Table.Th>{t("Date")}</Table.Th>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Table.Thead>
|
||||
|
||||
<Table.Tbody>
|
||||
{data?.items.map((invitation, index) => (
|
||||
<Table.Tr key={index}>
|
||||
<Table.Td>
|
||||
<Group gap="sm">
|
||||
<Avatar name={invitation.email} color="initials"/>
|
||||
<div>
|
||||
<Text fz="sm" fw={500}>
|
||||
{invitation.email}
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
</Table.Td>
|
||||
|
||||
<Table.Td>{getUserRoleLabel(invitation.role)}</Table.Td>
|
||||
|
||||
<Table.Td>{timeAgo(invitation.createdAt)}</Table.Td>
|
||||
|
||||
<Table.Td>
|
||||
{isAdmin && (
|
||||
<InviteActionMenu invitationId={invitation.id}/>
|
||||
)}
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Table.ScrollContainer>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Group, Table, Text, Badge } from "@mantine/core";
|
||||
import {Group, Table, Text, Badge} from "@mantine/core";
|
||||
import {
|
||||
useChangeMemberRoleMutation,
|
||||
useWorkspaceMembersQuery,
|
||||
} from "@/features/workspace/queries/workspace-query.ts";
|
||||
import { CustomAvatar } from "@/components/ui/custom-avatar.tsx";
|
||||
import {CustomAvatar} from "@/components/ui/custom-avatar.tsx";
|
||||
import React from "react";
|
||||
import RoleSelectMenu from "@/components/ui/role-select-menu.tsx";
|
||||
import {
|
||||
@@ -18,7 +18,7 @@ export default function WorkspaceMembersTable() {
|
||||
const { t } = useTranslation();
|
||||
const { data, isLoading } = useWorkspaceMembersQuery({ limit: 100 });
|
||||
const changeMemberRoleMutation = useChangeMemberRoleMutation();
|
||||
const { isAdmin, isOwner } = useUserRole();
|
||||
const {isAdmin, isOwner} = useUserRole();
|
||||
|
||||
const assignableUserRoles = isOwner
|
||||
? userRoleData
|
||||
@@ -44,50 +44,50 @@ export default function WorkspaceMembersTable() {
|
||||
return (
|
||||
<>
|
||||
{data && (
|
||||
<Table verticalSpacing="sm">
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>{t("User")}</Table.Th>
|
||||
<Table.Th>{t("Status")}</Table.Th>
|
||||
<Table.Th>{t("Role")}</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
|
||||
<Table.Tbody>
|
||||
{data?.items.map((user, index) => (
|
||||
<Table.Tr key={index}>
|
||||
<Table.Td>
|
||||
<Group gap="sm">
|
||||
<CustomAvatar avatarUrl={user.avatarUrl} name={user.name} />
|
||||
<div>
|
||||
<Text fz="sm" fw={500}>
|
||||
{user.name}
|
||||
</Text>
|
||||
<Text fz="xs" c="dimmed">
|
||||
{user.email}
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
</Table.Td>
|
||||
|
||||
<Table.Td>
|
||||
<Badge variant="light">{t("Active")}</Badge>
|
||||
</Table.Td>
|
||||
|
||||
<Table.Td>
|
||||
<RoleSelectMenu
|
||||
roles={assignableUserRoles}
|
||||
roleName={getUserRoleLabel(user.role)}
|
||||
onChange={(newRole) =>
|
||||
handleRoleChange(user.id, user.role, newRole)
|
||||
}
|
||||
disabled={!isAdmin}
|
||||
/>
|
||||
</Table.Td>
|
||||
<Table.ScrollContainer minWidth={500}>
|
||||
<Table verticalSpacing="sm">
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>{t("User")}</Table.Th>
|
||||
<Table.Th>{t("Status")}</Table.Th>
|
||||
<Table.Th>{t("Role")}</Table.Th>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Table.Thead>
|
||||
|
||||
<Table.Tbody>
|
||||
{data?.items.map((user, index) => (
|
||||
<Table.Tr key={index}>
|
||||
<Table.Td>
|
||||
<Group gap="sm">
|
||||
<CustomAvatar avatarUrl={user.avatarUrl} name={user.name}/>
|
||||
<div>
|
||||
<Text fz="sm" fw={500}>
|
||||
{user.name}
|
||||
</Text>
|
||||
<Text fz="xs" c="dimmed">
|
||||
{user.email}
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Badge variant="light">{t("Active")}</Badge>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<RoleSelectMenu
|
||||
roles={assignableUserRoles}
|
||||
roleName={getUserRoleLabel(user.role)}
|
||||
onChange={(newRole) =>
|
||||
handleRoleChange(user.id, user.role, newRole)
|
||||
}
|
||||
disabled={!isAdmin}
|
||||
/>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
</Table.ScrollContainer>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,51 +1,62 @@
|
||||
import bytes from "bytes";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
CONFIG?: Record<string, string>;
|
||||
}
|
||||
interface Window {
|
||||
CONFIG?: Record<string, string>;
|
||||
}
|
||||
}
|
||||
|
||||
export function getAppName(): string{
|
||||
return 'Docmost';
|
||||
}
|
||||
|
||||
export function getAppUrl(): string {
|
||||
//let appUrl = window.CONFIG?.APP_URL || process.env.APP_URL;
|
||||
//let appUrl = window.CONFIG?.APP_URL || process.env.APP_URL;
|
||||
|
||||
// if (import.meta.env.DEV) {
|
||||
// return appUrl || "http://localhost:3000";
|
||||
//}
|
||||
// if (import.meta.env.DEV) {
|
||||
// return appUrl || "http://localhost:3000";
|
||||
//}
|
||||
|
||||
return `${window.location.protocol}//${window.location.host}`;
|
||||
return `${window.location.protocol}//${window.location.host}`;
|
||||
}
|
||||
|
||||
export function getBackendUrl(): string {
|
||||
return getAppUrl() + '/api';
|
||||
return getAppUrl() + '/api';
|
||||
}
|
||||
|
||||
export function getCollaborationUrl(): string {
|
||||
const COLLAB_PATH = '/collab';
|
||||
const COLLAB_PATH = '/collab';
|
||||
|
||||
let url = getAppUrl();
|
||||
if (import.meta.env.DEV) {
|
||||
url = process.env.APP_URL;
|
||||
}
|
||||
let url = getAppUrl();
|
||||
if (import.meta.env.DEV) {
|
||||
url = process.env.APP_URL;
|
||||
}
|
||||
|
||||
const wsProtocol = url.startsWith('https') ? 'wss' : 'ws';
|
||||
return `${wsProtocol}://${url.split('://')[1]}${COLLAB_PATH}`;
|
||||
const wsProtocol = url.startsWith('https') ? 'wss' : 'ws';
|
||||
return `${wsProtocol}://${url.split('://')[1]}${COLLAB_PATH}`;
|
||||
}
|
||||
|
||||
export function getAvatarUrl(avatarUrl: string) {
|
||||
if (!avatarUrl) {
|
||||
return null;
|
||||
}
|
||||
if (!avatarUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (avatarUrl?.startsWith('http')) {
|
||||
return avatarUrl;
|
||||
}
|
||||
if (avatarUrl?.startsWith('http')) {
|
||||
return avatarUrl;
|
||||
}
|
||||
|
||||
return getBackendUrl() + '/attachments/img/avatar/' + avatarUrl;
|
||||
return getBackendUrl() + '/attachments/img/avatar/' + avatarUrl;
|
||||
}
|
||||
|
||||
export function getSpaceUrl(spaceSlug: string) {
|
||||
return '/s/' + spaceSlug;
|
||||
return '/s/' + spaceSlug;
|
||||
}
|
||||
|
||||
export function getFileUrl(src: string) {
|
||||
return src?.startsWith('/files/') ? getBackendUrl() + src : src;
|
||||
return src?.startsWith('/files/') ? getBackendUrl() + src : src;
|
||||
}
|
||||
|
||||
export function getFileUploadSizeLimit() {
|
||||
const limit = window.CONFIG?.FILE_UPLOAD_SIZE_LIMIT || process?.env.FILE_UPLOAD_SIZE_LIMIT || '50mb';
|
||||
return bytes(limit);
|
||||
}
|
||||
@@ -55,11 +55,21 @@ export async function svgStringToFile(
|
||||
return new File([blob], fileName, { type: "image/svg+xml" });
|
||||
}
|
||||
|
||||
// Convert a string holding Base64 encoded UTF-8 data into a proper UTF-8 encoded string
|
||||
// as a replacement for `atob`.
|
||||
// based on: https://developer.mozilla.org/en-US/docs/Glossary/Base64
|
||||
function decodeBase64(base64: string): string {
|
||||
// convert string to bytes
|
||||
const bytes = Uint8Array.from(atob(base64), (m) => m.codePointAt(0));
|
||||
// properly decode bytes to UTF-8 encoded string
|
||||
return new TextDecoder().decode(bytes);
|
||||
}
|
||||
|
||||
export function decodeBase64ToSvgString(base64Data: string): string {
|
||||
const base64Prefix = 'data:image/svg+xml;base64,';
|
||||
if (base64Data.startsWith(base64Prefix)) {
|
||||
base64Data = base64Data.replace(base64Prefix, '');
|
||||
}
|
||||
|
||||
return atob(base64Data);
|
||||
return decodeBase64(base64Data);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { ForgotPasswordForm } from "@/features/auth/components/forgot-password-form";
|
||||
import { getAppName } from "@/lib/config";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
|
||||
export default function ForgotPassword() {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Forgot Password - Docmost</title>
|
||||
<title>Forgot Password - {getAppName()}</title>
|
||||
</Helmet>
|
||||
<ForgotPasswordForm />
|
||||
</>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { InviteSignUpForm } from "@/features/auth/components/invite-sign-up-form.tsx";
|
||||
import {getAppName} from "@/lib/config.ts";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function InviteSignup() {
|
||||
@@ -8,7 +9,7 @@ export default function InviteSignup() {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{t("Invitation signup")} - Docmost</title>
|
||||
<title>{t("Invitation Signup")} - {getAppName()}</title>
|
||||
</Helmet>
|
||||
<InviteSignUpForm />
|
||||
</>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { LoginForm } from "@/features/auth/components/login-form";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import {getAppName} from "@/lib/config.ts";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function LoginPage() {
|
||||
@@ -8,7 +9,7 @@ export default function LoginPage() {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{t("Login")} - Docmost</title>
|
||||
<title>{t("Login")} - {getAppName()}</title>
|
||||
</Helmet>
|
||||
<LoginForm />
|
||||
</>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Link, useSearchParams } from "react-router-dom";
|
||||
import { useVerifyUserTokenQuery } from "@/features/auth/queries/auth-query";
|
||||
import { Button, Container, Group, Text } from "@mantine/core";
|
||||
import APP_ROUTE from "@/lib/app-route";
|
||||
import {getAppName} from "@/lib/config.ts";
|
||||
|
||||
export default function PasswordReset() {
|
||||
const [searchParams] = useSearchParams();
|
||||
@@ -21,7 +22,7 @@ export default function PasswordReset() {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Password Reset - Docmost</title>
|
||||
<title>Password Reset - {getAppName()}</title>
|
||||
</Helmet>
|
||||
<Container my={40}>
|
||||
<Text size="lg" ta="center">
|
||||
@@ -45,7 +46,7 @@ export default function PasswordReset() {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Password Reset - Docmost</title>
|
||||
<title>Password Reset - {getAppName()}</title>
|
||||
</Helmet>
|
||||
<PasswordResetForm resetToken={resetToken} />
|
||||
</>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { SetupWorkspaceForm } from "@/features/auth/components/setup-workspace-f
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import React, { useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import {getAppName} from "@/lib/config.ts";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function SetupWorkspace() {
|
||||
@@ -34,7 +35,7 @@ export default function SetupWorkspace() {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{t("Setup workspace")} - Docmost</title>
|
||||
<title>{t("Setup Workspace")} - {getAppName()}</title>
|
||||
</Helmet>
|
||||
<SetupWorkspaceForm />
|
||||
</>
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
import { Container, Space } from "@mantine/core";
|
||||
import {Container, Space} from "@mantine/core";
|
||||
import HomeTabs from "@/features/home/components/home-tabs";
|
||||
import SpaceGrid from "@/features/space/components/space-grid.tsx";
|
||||
import {getAppName} from "@/lib/config.ts";
|
||||
import {Helmet} from "react-helmet-async";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<Container size={"800"} pt="xl">
|
||||
<SpaceGrid />
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Home - {getAppName()}</title>
|
||||
</Helmet>
|
||||
<Container size={"800"} pt="xl">
|
||||
<SpaceGrid/>
|
||||
|
||||
<Space h="xl" />
|
||||
<Space h="xl"/>
|
||||
|
||||
<HomeTabs />
|
||||
</Container>
|
||||
);
|
||||
<HomeTabs/>
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,20 +2,24 @@ import SettingsTitle from "@/components/settings/settings-title.tsx";
|
||||
import AccountLanguage from "@/features/user/components/account-languate";
|
||||
import AccountTheme from "@/features/user/components/account-theme.tsx";
|
||||
import PageWidthPref from "@/features/user/components/page-width-pref.tsx";
|
||||
import { Divider } from "@mantine/core";
|
||||
import {Divider} from "@mantine/core";
|
||||
import {getAppName} from "@/lib/config.ts";
|
||||
import {Helmet} from "react-helmet-async";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function AccountPreferences() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<SettingsTitle title={t("Preferences")} />
|
||||
<AccountTheme />
|
||||
<Divider my={"md"} />
|
||||
<AccountLanguage />
|
||||
<Divider my={"md"} />
|
||||
<PageWidthPref />
|
||||
</>
|
||||
);
|
||||
export default function AccountPreferences() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{t("Preferences")} - {getAppName()}</title>
|
||||
</Helmet>
|
||||
<SettingsTitle title={t("Preferences")}/>
|
||||
<AccountTheme/>
|
||||
<Divider my={"md"}/>
|
||||
<PageWidthPref/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import ChangePassword from "@/features/user/components/change-password";
|
||||
import { Divider } from "@mantine/core";
|
||||
import AccountAvatar from "@/features/user/components/account-avatar";
|
||||
import SettingsTitle from "@/components/settings/settings-title.tsx";
|
||||
import {getAppName} from "@/lib/config.ts";
|
||||
import {Helmet} from "react-helmet-async";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function AccountSettings() {
|
||||
@@ -11,6 +13,9 @@ export default function AccountSettings() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{t("My Profile")} - {getAppName()}</title>
|
||||
</Helmet>
|
||||
<SettingsTitle title={t("My Profile")} />
|
||||
|
||||
<AccountAvatar />
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import SettingsTitle from "@/components/settings/settings-title.tsx";
|
||||
import GroupMembersList from "@/features/group/components/group-members";
|
||||
import GroupDetails from "@/features/group/components/group-details";
|
||||
import {getAppName} from "@/lib/config.ts";
|
||||
import {Helmet} from "react-helmet-async";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
|
||||
export default function GroupInfo() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<SettingsTitle title={t("Manage Group")} />
|
||||
<GroupDetails />
|
||||
<GroupMembersList />
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{t("Manage Group")} - {getAppName()}</title>
|
||||
</Helmet>
|
||||
<SettingsTitle title={t("Manage Group")} />
|
||||
<GroupDetails/>
|
||||
<GroupMembersList/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ import SettingsTitle from "@/components/settings/settings-title.tsx";
|
||||
import { Group } from "@mantine/core";
|
||||
import CreateGroupModal from "@/features/group/components/create-group-modal";
|
||||
import useUserRole from "@/hooks/use-user-role.tsx";
|
||||
import {getAppName} from "@/lib/config.ts";
|
||||
import {Helmet} from "react-helmet-async";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function Groups() {
|
||||
@@ -11,6 +13,9 @@ export default function Groups() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{t("Groups")} - {getAppName()}</title>
|
||||
</Helmet>
|
||||
<SettingsTitle title={t("Groups")} />
|
||||
|
||||
<Group my="md" justify="flex-end">
|
||||
|
||||
@@ -1,23 +1,29 @@
|
||||
import SettingsTitle from "@/components/settings/settings-title.tsx";
|
||||
import SpaceList from "@/features/space/components/space-list.tsx";
|
||||
import useUserRole from "@/hooks/use-user-role.tsx";
|
||||
import { Group } from "@mantine/core";
|
||||
import {Group} from "@mantine/core";
|
||||
import CreateSpaceModal from "@/features/space/components/create-space-modal.tsx";
|
||||
import {getAppName} from "@/lib/config.ts";
|
||||
import {Helmet} from "react-helmet-async";
|
||||
import {getAppName} from "@/lib/config.ts";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function Spaces() {
|
||||
const { t } = useTranslation();
|
||||
const { isAdmin } = useUserRole();
|
||||
const { t } = useTranslation();
|
||||
const {isAdmin} = useUserRole();
|
||||
|
||||
return (
|
||||
<>
|
||||
<SettingsTitle title={t("Spaces")} />
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{t("Spaces")} - {getAppName()}</title>
|
||||
</Helmet>
|
||||
<SettingsTitle title={t("Spaces")} />
|
||||
|
||||
<Group my="md" justify="flex-end">
|
||||
{isAdmin && <CreateSpaceModal />}
|
||||
</Group>
|
||||
<Group my="md" justify="flex-end">
|
||||
{isAdmin && <CreateSpaceModal/>}
|
||||
</Group>
|
||||
|
||||
<SpaceList />
|
||||
</>
|
||||
);
|
||||
<SpaceList/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,64 +1,69 @@
|
||||
import WorkspaceInviteModal from "@/features/workspace/components/members/components/workspace-invite-modal";
|
||||
import { Group, SegmentedControl, Space, Text } from "@mantine/core";
|
||||
import {Group, SegmentedControl, Space, Text} from "@mantine/core";
|
||||
import WorkspaceMembersTable from "@/features/workspace/components/members/components/workspace-members-table";
|
||||
import SettingsTitle from "@/components/settings/settings-title.tsx";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
import {useEffect, useState} from "react";
|
||||
import {useNavigate, useSearchParams} from "react-router-dom";
|
||||
import WorkspaceInvitesTable from "@/features/workspace/components/members/components/workspace-invites-table.tsx";
|
||||
import useUserRole from "@/hooks/use-user-role.tsx";
|
||||
import {getAppName} from "@/lib/config.ts";
|
||||
import {Helmet} from "react-helmet-async";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function WorkspaceMembers() {
|
||||
const { t } = useTranslation();
|
||||
const [segmentValue, setSegmentValue] = useState("members");
|
||||
const [searchParams] = useSearchParams();
|
||||
const { isAdmin } = useUserRole();
|
||||
const navigate = useNavigate();
|
||||
const [segmentValue, setSegmentValue] = useState("members");
|
||||
const [searchParams] = useSearchParams();
|
||||
const {isAdmin} = useUserRole();
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
const currentTab = searchParams.get("tab");
|
||||
if (currentTab === "invites") {
|
||||
setSegmentValue(currentTab);
|
||||
}
|
||||
}, [searchParams.get("tab")]);
|
||||
useEffect(() => {
|
||||
const currentTab = searchParams.get("tab");
|
||||
if (currentTab === "invites") {
|
||||
setSegmentValue(currentTab);
|
||||
}
|
||||
}, [searchParams.get("tab")]);
|
||||
|
||||
const handleSegmentChange = (value: string) => {
|
||||
setSegmentValue(value);
|
||||
if (value === "invites") {
|
||||
navigate(`?tab=${value}`);
|
||||
} else {
|
||||
navigate("");
|
||||
}
|
||||
};
|
||||
const handleSegmentChange = (value: string) => {
|
||||
setSegmentValue(value);
|
||||
if (value === "invites") {
|
||||
navigate(`?tab=${value}`);
|
||||
} else {
|
||||
navigate("");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<SettingsTitle title={t("Members")} />
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{t("Members")} - {getAppName()}</title>
|
||||
</Helmet>
|
||||
<SettingsTitle title={t("Members")}/>
|
||||
|
||||
{/* <WorkspaceInviteSection /> */}
|
||||
{/* <Divider my="lg" /> */}
|
||||
{/* <WorkspaceInviteSection /> */}
|
||||
{/* <Divider my="lg" /> */}
|
||||
|
||||
<Group justify="space-between">
|
||||
<SegmentedControl
|
||||
value={segmentValue}
|
||||
onChange={handleSegmentChange}
|
||||
data={[
|
||||
{ label: t("Members"), value: "members" },
|
||||
{ label: t("Pending"), value: "invites" },
|
||||
]}
|
||||
withItemsBorders={false}
|
||||
/>
|
||||
<Group justify="space-between">
|
||||
<SegmentedControl
|
||||
value={segmentValue}
|
||||
onChange={handleSegmentChange}
|
||||
data={[
|
||||
{ label: t("Members"), value: "members" },
|
||||
{ label: t("Pending"), value: "invites" },
|
||||
]}
|
||||
withItemsBorders={false}
|
||||
/>
|
||||
|
||||
{isAdmin && <WorkspaceInviteModal />}
|
||||
</Group>
|
||||
{isAdmin && <WorkspaceInviteModal/>}
|
||||
</Group>
|
||||
|
||||
<Space h="lg" />
|
||||
<Space h="lg"/>
|
||||
|
||||
{segmentValue === "invites" ? (
|
||||
<WorkspaceInvitesTable />
|
||||
) : (
|
||||
<WorkspaceMembersTable />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
{segmentValue === "invites" ? (
|
||||
<WorkspaceInvitesTable/>
|
||||
) : (
|
||||
<WorkspaceMembersTable/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import SettingsTitle from "@/components/settings/settings-title.tsx";
|
||||
import WorkspaceNameForm from "@/features/workspace/components/settings/components/workspace-name-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {getAppName} from "@/lib/config.ts";
|
||||
import {Helmet} from "react-helmet-async";
|
||||
|
||||
export default function WorkspaceSettings() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<SettingsTitle title={t("General")} />
|
||||
<WorkspaceNameForm />
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>Workspace Settings - {getAppName()}</title>
|
||||
</Helmet>
|
||||
<SettingsTitle title={t("General")} />
|
||||
<WorkspaceNameForm/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
import { Container } from "@mantine/core";
|
||||
import {Container} from "@mantine/core";
|
||||
import SpaceHomeTabs from "@/features/space/components/space-home-tabs.tsx";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useGetSpaceBySlugQuery } from "@/features/space/queries/space-query.ts";
|
||||
import {useParams} from "react-router-dom";
|
||||
import {useGetSpaceBySlugQuery} from "@/features/space/queries/space-query.ts";
|
||||
import {getAppName} from "@/lib/config.ts";
|
||||
import {Helmet} from "react-helmet-async";
|
||||
|
||||
export default function SpaceHome() {
|
||||
const { spaceSlug } = useParams();
|
||||
const { data: space } = useGetSpaceBySlugQuery(spaceSlug);
|
||||
const {spaceSlug} = useParams();
|
||||
const {data: space} = useGetSpaceBySlugQuery(spaceSlug);
|
||||
|
||||
return (
|
||||
<Container size={"800"} pt="xl">
|
||||
{space && <SpaceHomeTabs />}
|
||||
</Container>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>{space?.name || 'Overview'} - {getAppName()}</title>
|
||||
</Helmet>
|
||||
<Container size={"800"} pt="xl">
|
||||
{space && <SpaceHomeTabs/>}
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import { Title, Text, Stack } from '@mantine/core';
|
||||
import { ThemeToggle } from '@/components/theme-toggle';
|
||||
|
||||
export function Welcome() {
|
||||
return (
|
||||
<Stack>
|
||||
<Title ta="center" mt={100}>
|
||||
<Text
|
||||
inherit
|
||||
variant="gradient"
|
||||
component="span"
|
||||
gradient={{ from: 'pink', to: 'yellow' }}
|
||||
>
|
||||
Welcome
|
||||
</Text>
|
||||
</Title>
|
||||
<Text ta="center" size="lg" maw={580} mx="auto" mt="xl">
|
||||
Welcome to something new and interesting.
|
||||
</Text>
|
||||
<ThemeToggle />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
@@ -5,12 +5,13 @@ import * as path from "path";
|
||||
export const envPath = path.resolve(process.cwd(), "..", "..");
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
const { APP_URL } = loadEnv(mode, envPath, "");
|
||||
const { APP_URL, FILE_UPLOAD_SIZE_LIMIT } = loadEnv(mode, envPath, "");
|
||||
|
||||
return {
|
||||
define: {
|
||||
"process.env": {
|
||||
APP_URL,
|
||||
FILE_UPLOAD_SIZE_LIMIT
|
||||
},
|
||||
'APP_VERSION': JSON.stringify(process.env.npm_package_version),
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "server",
|
||||
"version": "0.3.1",
|
||||
"version": "0.4.1",
|
||||
"description": "",
|
||||
"author": "",
|
||||
"private": true,
|
||||
@@ -51,7 +51,6 @@
|
||||
"@socket.io/redis-adapter": "^8.3.0",
|
||||
"bcrypt": "^5.1.1",
|
||||
"bullmq": "^5.12.12",
|
||||
"bytes": "^3.1.2",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.1",
|
||||
"fix-esm": "^1.0.1",
|
||||
@@ -81,7 +80,6 @@
|
||||
"@nestjs/schematics": "^10.1.4",
|
||||
"@nestjs/testing": "^10.4.1",
|
||||
"@types/bcrypt": "^5.0.2",
|
||||
"@types/bytes": "^3.1.4",
|
||||
"@types/debounce": "^1.2.4",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/jest": "^29.5.12",
|
||||
|
||||
@@ -16,4 +16,3 @@ export const inlineFileExtensions = [
|
||||
'.mp4',
|
||||
'.mov',
|
||||
];
|
||||
export const MAX_FILE_SIZE = '50MB';
|
||||
|
||||
@@ -1,308 +1,310 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
Controller,
|
||||
ForbiddenException,
|
||||
Get,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
Logger,
|
||||
NotFoundException,
|
||||
Param,
|
||||
Post,
|
||||
Req,
|
||||
Res,
|
||||
UseGuards,
|
||||
UseInterceptors,
|
||||
BadRequestException,
|
||||
Controller,
|
||||
ForbiddenException,
|
||||
Get,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
Logger,
|
||||
NotFoundException,
|
||||
Param,
|
||||
Post,
|
||||
Req,
|
||||
Res,
|
||||
UseGuards,
|
||||
UseInterceptors,
|
||||
} from '@nestjs/common';
|
||||
import { AttachmentService } from './services/attachment.service';
|
||||
import { FastifyReply } from 'fastify';
|
||||
import { FileInterceptor } from '../../common/interceptors/file.interceptor';
|
||||
import {AttachmentService} from './services/attachment.service';
|
||||
import {FastifyReply} from 'fastify';
|
||||
import {FileInterceptor} from '../../common/interceptors/file.interceptor';
|
||||
import * as bytes from 'bytes';
|
||||
import { AuthUser } from '../../common/decorators/auth-user.decorator';
|
||||
import { AuthWorkspace } from '../../common/decorators/auth-workspace.decorator';
|
||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||
import { User, Workspace } from '@docmost/db/types/entity.types';
|
||||
import { StorageService } from '../../integrations/storage/storage.service';
|
||||
import {AuthUser} from '../../common/decorators/auth-user.decorator';
|
||||
import {AuthWorkspace} from '../../common/decorators/auth-workspace.decorator';
|
||||
import {JwtAuthGuard} from '../../common/guards/jwt-auth.guard';
|
||||
import {User, Workspace} from '@docmost/db/types/entity.types';
|
||||
import {StorageService} from '../../integrations/storage/storage.service';
|
||||
import {
|
||||
getAttachmentFolderPath,
|
||||
validAttachmentTypes,
|
||||
getAttachmentFolderPath,
|
||||
validAttachmentTypes,
|
||||
} from './attachment.utils';
|
||||
import { getMimeType } from '../../common/helpers';
|
||||
import {getMimeType} from '../../common/helpers';
|
||||
import {
|
||||
AttachmentType,
|
||||
inlineFileExtensions,
|
||||
MAX_AVATAR_SIZE,
|
||||
MAX_FILE_SIZE,
|
||||
AttachmentType,
|
||||
inlineFileExtensions,
|
||||
MAX_AVATAR_SIZE,
|
||||
} from './attachment.constants';
|
||||
import {
|
||||
SpaceCaslAction,
|
||||
SpaceCaslSubject,
|
||||
SpaceCaslAction,
|
||||
SpaceCaslSubject,
|
||||
} from '../casl/interfaces/space-ability.type';
|
||||
import SpaceAbilityFactory from '../casl/abilities/space-ability.factory';
|
||||
import {
|
||||
WorkspaceCaslAction,
|
||||
WorkspaceCaslSubject,
|
||||
WorkspaceCaslAction,
|
||||
WorkspaceCaslSubject,
|
||||
} from '../casl/interfaces/workspace-ability.type';
|
||||
import WorkspaceAbilityFactory from '../casl/abilities/workspace-ability.factory';
|
||||
import { PageRepo } from '@docmost/db/repos/page/page.repo';
|
||||
import { AttachmentRepo } from '@docmost/db/repos/attachment/attachment.repo';
|
||||
import { validate as isValidUUID } from 'uuid';
|
||||
import {PageRepo} from '@docmost/db/repos/page/page.repo';
|
||||
import {AttachmentRepo} from '@docmost/db/repos/attachment/attachment.repo';
|
||||
import {validate as isValidUUID} from 'uuid';
|
||||
import {EnvironmentService} from "../../integrations/environment/environment.service";
|
||||
|
||||
@Controller()
|
||||
export class AttachmentController {
|
||||
private readonly logger = new Logger(AttachmentController.name);
|
||||
private readonly logger = new Logger(AttachmentController.name);
|
||||
|
||||
constructor(
|
||||
private readonly attachmentService: AttachmentService,
|
||||
private readonly storageService: StorageService,
|
||||
private readonly workspaceAbility: WorkspaceAbilityFactory,
|
||||
private readonly spaceAbility: SpaceAbilityFactory,
|
||||
private readonly pageRepo: PageRepo,
|
||||
private readonly attachmentRepo: AttachmentRepo,
|
||||
) {}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Post('files/upload')
|
||||
@UseInterceptors(FileInterceptor)
|
||||
async uploadFile(
|
||||
@Req() req: any,
|
||||
@Res() res: FastifyReply,
|
||||
@AuthUser() user: User,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
) {
|
||||
const maxFileSize = bytes(MAX_FILE_SIZE);
|
||||
|
||||
let file = null;
|
||||
try {
|
||||
file = await req.file({
|
||||
limits: { fileSize: maxFileSize, fields: 3, files: 1 },
|
||||
});
|
||||
} catch (err: any) {
|
||||
this.logger.error(err.message);
|
||||
if (err?.statusCode === 413) {
|
||||
throw new BadRequestException(
|
||||
`File too large. Exceeds the ${MAX_FILE_SIZE} limit`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!file) {
|
||||
throw new BadRequestException('Failed to upload file');
|
||||
}
|
||||
|
||||
const pageId = file.fields?.pageId?.value;
|
||||
|
||||
if (!pageId) {
|
||||
throw new BadRequestException('PageId is required');
|
||||
}
|
||||
|
||||
const page = await this.pageRepo.findById(pageId);
|
||||
|
||||
if (!page) {
|
||||
throw new NotFoundException('Page not found');
|
||||
}
|
||||
|
||||
const spaceAbility = await this.spaceAbility.createForUser(
|
||||
user,
|
||||
page.spaceId,
|
||||
);
|
||||
if (spaceAbility.cannot(SpaceCaslAction.Manage, SpaceCaslSubject.Page)) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
|
||||
const spaceId = page.spaceId;
|
||||
|
||||
const attachmentId = file.fields?.attachmentId?.value;
|
||||
if (attachmentId && !isValidUUID(attachmentId)) {
|
||||
throw new BadRequestException('Invalid attachment id');
|
||||
}
|
||||
|
||||
try {
|
||||
const fileResponse = await this.attachmentService.uploadFile({
|
||||
filePromise: file,
|
||||
pageId: pageId,
|
||||
spaceId: spaceId,
|
||||
userId: user.id,
|
||||
workspaceId: workspace.id,
|
||||
attachmentId: attachmentId,
|
||||
});
|
||||
|
||||
return res.send(fileResponse);
|
||||
} catch (err: any) {
|
||||
if (err?.statusCode === 413) {
|
||||
const errMessage = `File too large. Exceeds the ${MAX_FILE_SIZE} limit`;
|
||||
this.logger.error(errMessage);
|
||||
throw new BadRequestException(errMessage);
|
||||
}
|
||||
this.logger.error(err);
|
||||
throw new BadRequestException('Error processing file upload.');
|
||||
}
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get('/files/:fileId/:fileName')
|
||||
async getFile(
|
||||
@Res() res: FastifyReply,
|
||||
@AuthUser() user: User,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@Param('fileId') fileId: string,
|
||||
@Param('fileName') fileName?: string,
|
||||
) {
|
||||
if (!isValidUUID(fileId)) {
|
||||
throw new NotFoundException('Invalid file id');
|
||||
}
|
||||
|
||||
const attachment = await this.attachmentRepo.findById(fileId);
|
||||
if (
|
||||
!attachment ||
|
||||
attachment.workspaceId !== workspace.id ||
|
||||
!attachment.pageId ||
|
||||
!attachment.spaceId
|
||||
constructor(
|
||||
private readonly attachmentService: AttachmentService,
|
||||
private readonly storageService: StorageService,
|
||||
private readonly workspaceAbility: WorkspaceAbilityFactory,
|
||||
private readonly spaceAbility: SpaceAbilityFactory,
|
||||
private readonly pageRepo: PageRepo,
|
||||
private readonly attachmentRepo: AttachmentRepo,
|
||||
private readonly environmentService: EnvironmentService,
|
||||
) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
const spaceAbility = await this.spaceAbility.createForUser(
|
||||
user,
|
||||
attachment.spaceId,
|
||||
);
|
||||
|
||||
if (spaceAbility.cannot(SpaceCaslAction.Read, SpaceCaslSubject.Page)) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
|
||||
try {
|
||||
const fileStream = await this.storageService.read(attachment.filePath);
|
||||
res.headers({
|
||||
'Content-Type': attachment.mimeType,
|
||||
'Cache-Control': 'public, max-age=3600',
|
||||
});
|
||||
|
||||
if (!inlineFileExtensions.includes(attachment.fileExt)) {
|
||||
res.header(
|
||||
'Content-Disposition',
|
||||
`attachment; filename="${encodeURIComponent(attachment.fileName)}"`,
|
||||
);
|
||||
}
|
||||
|
||||
return res.send(fileStream);
|
||||
} catch (err) {
|
||||
this.logger.error(err);
|
||||
throw new NotFoundException('File not found');
|
||||
}
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Post('attachments/upload-image')
|
||||
@UseInterceptors(FileInterceptor)
|
||||
async uploadAvatarOrLogo(
|
||||
@Req() req: any,
|
||||
@Res() res: FastifyReply,
|
||||
@AuthUser() user: User,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
) {
|
||||
const maxFileSize = bytes(MAX_AVATAR_SIZE);
|
||||
|
||||
let file = null;
|
||||
try {
|
||||
file = await req.file({
|
||||
limits: { fileSize: maxFileSize, fields: 3, files: 1 },
|
||||
});
|
||||
} catch (err: any) {
|
||||
if (err?.statusCode === 413) {
|
||||
throw new BadRequestException(
|
||||
`File too large. Exceeds the ${MAX_AVATAR_SIZE} limit`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!file) {
|
||||
throw new BadRequestException('Invalid file upload');
|
||||
}
|
||||
|
||||
const attachmentType = file.fields?.type?.value;
|
||||
const spaceId = file.fields?.spaceId?.value;
|
||||
|
||||
if (!attachmentType) {
|
||||
throw new BadRequestException('attachment type is required');
|
||||
}
|
||||
|
||||
if (
|
||||
!validAttachmentTypes.includes(attachmentType) ||
|
||||
attachmentType === AttachmentType.File
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Post('files/upload')
|
||||
@UseInterceptors(FileInterceptor)
|
||||
async uploadFile(
|
||||
@Req() req: any,
|
||||
@Res() res: FastifyReply,
|
||||
@AuthUser() user: User,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
) {
|
||||
throw new BadRequestException('Invalid image attachment type');
|
||||
const maxFileSize = bytes(this.environmentService.getFileUploadSizeLimit());
|
||||
|
||||
let file = null;
|
||||
try {
|
||||
file = await req.file({
|
||||
limits: {fileSize: maxFileSize, fields: 3, files: 1},
|
||||
});
|
||||
} catch (err: any) {
|
||||
this.logger.error(err.message);
|
||||
if (err?.statusCode === 413) {
|
||||
throw new BadRequestException(
|
||||
`File too large. Exceeds the ${this.environmentService.getFileUploadSizeLimit()} limit`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!file) {
|
||||
throw new BadRequestException('Failed to upload file');
|
||||
}
|
||||
|
||||
const pageId = file.fields?.pageId?.value;
|
||||
|
||||
if (!pageId) {
|
||||
throw new BadRequestException('PageId is required');
|
||||
}
|
||||
|
||||
const page = await this.pageRepo.findById(pageId);
|
||||
|
||||
if (!page) {
|
||||
throw new NotFoundException('Page not found');
|
||||
}
|
||||
|
||||
const spaceAbility = await this.spaceAbility.createForUser(
|
||||
user,
|
||||
page.spaceId,
|
||||
);
|
||||
if (spaceAbility.cannot(SpaceCaslAction.Manage, SpaceCaslSubject.Page)) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
|
||||
const spaceId = page.spaceId;
|
||||
|
||||
const attachmentId = file.fields?.attachmentId?.value;
|
||||
if (attachmentId && !isValidUUID(attachmentId)) {
|
||||
throw new BadRequestException('Invalid attachment id');
|
||||
}
|
||||
|
||||
try {
|
||||
const fileResponse = await this.attachmentService.uploadFile({
|
||||
filePromise: file,
|
||||
pageId: pageId,
|
||||
spaceId: spaceId,
|
||||
userId: user.id,
|
||||
workspaceId: workspace.id,
|
||||
attachmentId: attachmentId,
|
||||
});
|
||||
|
||||
return res.send(fileResponse);
|
||||
} catch (err: any) {
|
||||
if (err?.statusCode === 413) {
|
||||
const errMessage = `File too large. Exceeds the ${this.environmentService.getFileUploadSizeLimit()} limit`;
|
||||
this.logger.error(errMessage);
|
||||
throw new BadRequestException(errMessage);
|
||||
}
|
||||
this.logger.error(err);
|
||||
throw new BadRequestException('Error processing file upload.');
|
||||
}
|
||||
}
|
||||
|
||||
if (attachmentType === AttachmentType.WorkspaceLogo) {
|
||||
const ability = this.workspaceAbility.createForUser(user, workspace);
|
||||
if (
|
||||
ability.cannot(
|
||||
WorkspaceCaslAction.Manage,
|
||||
WorkspaceCaslSubject.Settings,
|
||||
)
|
||||
) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
}
|
||||
|
||||
if (attachmentType === AttachmentType.SpaceLogo) {
|
||||
if (!spaceId) {
|
||||
throw new BadRequestException('spaceId is required');
|
||||
}
|
||||
|
||||
const spaceAbility = await this.spaceAbility.createForUser(user, spaceId);
|
||||
if (
|
||||
spaceAbility.cannot(SpaceCaslAction.Manage, SpaceCaslSubject.Settings)
|
||||
) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const fileResponse = await this.attachmentService.uploadImage(
|
||||
file,
|
||||
attachmentType,
|
||||
user.id,
|
||||
workspace.id,
|
||||
spaceId,
|
||||
);
|
||||
|
||||
return res.send(fileResponse);
|
||||
} catch (err: any) {
|
||||
this.logger.error(err);
|
||||
throw new BadRequestException('Error processing file upload.');
|
||||
}
|
||||
}
|
||||
|
||||
@Get('attachments/img/:attachmentType/:fileName')
|
||||
async getLogoOrAvatar(
|
||||
@Res() res: FastifyReply,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@Param('attachmentType') attachmentType: AttachmentType,
|
||||
@Param('fileName') fileName?: string,
|
||||
) {
|
||||
if (
|
||||
!validAttachmentTypes.includes(attachmentType) ||
|
||||
attachmentType === AttachmentType.File
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get('/files/:fileId/:fileName')
|
||||
async getFile(
|
||||
@Res() res: FastifyReply,
|
||||
@AuthUser() user: User,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@Param('fileId') fileId: string,
|
||||
@Param('fileName') fileName?: string,
|
||||
) {
|
||||
throw new BadRequestException('Invalid image attachment type');
|
||||
if (!isValidUUID(fileId)) {
|
||||
throw new NotFoundException('Invalid file id');
|
||||
}
|
||||
|
||||
const attachment = await this.attachmentRepo.findById(fileId);
|
||||
if (
|
||||
!attachment ||
|
||||
attachment.workspaceId !== workspace.id ||
|
||||
!attachment.pageId ||
|
||||
!attachment.spaceId
|
||||
) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
const spaceAbility = await this.spaceAbility.createForUser(
|
||||
user,
|
||||
attachment.spaceId,
|
||||
);
|
||||
|
||||
if (spaceAbility.cannot(SpaceCaslAction.Read, SpaceCaslSubject.Page)) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
|
||||
try {
|
||||
const fileStream = await this.storageService.read(attachment.filePath);
|
||||
res.headers({
|
||||
'Content-Type': attachment.mimeType,
|
||||
'Cache-Control': 'public, max-age=3600',
|
||||
});
|
||||
|
||||
if (!inlineFileExtensions.includes(attachment.fileExt)) {
|
||||
res.header(
|
||||
'Content-Disposition',
|
||||
`attachment; filename="${encodeURIComponent(attachment.fileName)}"`,
|
||||
);
|
||||
}
|
||||
|
||||
return res.send(fileStream);
|
||||
} catch (err) {
|
||||
this.logger.error(err);
|
||||
throw new NotFoundException('File not found');
|
||||
}
|
||||
}
|
||||
|
||||
const filePath = `${getAttachmentFolderPath(attachmentType, workspace.id)}/${fileName}`;
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Post('attachments/upload-image')
|
||||
@UseInterceptors(FileInterceptor)
|
||||
async uploadAvatarOrLogo(
|
||||
@Req() req: any,
|
||||
@Res() res: FastifyReply,
|
||||
@AuthUser() user: User,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
) {
|
||||
const maxFileSize = bytes(MAX_AVATAR_SIZE);
|
||||
|
||||
try {
|
||||
const fileStream = await this.storageService.read(filePath);
|
||||
res.headers({
|
||||
'Content-Type': getMimeType(filePath),
|
||||
'Cache-Control': 'public, max-age=86400',
|
||||
});
|
||||
return res.send(fileStream);
|
||||
} catch (err) {
|
||||
this.logger.error(err);
|
||||
throw new NotFoundException('File not found');
|
||||
let file = null;
|
||||
try {
|
||||
file = await req.file({
|
||||
limits: {fileSize: maxFileSize, fields: 3, files: 1},
|
||||
});
|
||||
} catch (err: any) {
|
||||
if (err?.statusCode === 413) {
|
||||
throw new BadRequestException(
|
||||
`File too large. Exceeds the ${MAX_AVATAR_SIZE} limit`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!file) {
|
||||
throw new BadRequestException('Invalid file upload');
|
||||
}
|
||||
|
||||
const attachmentType = file.fields?.type?.value;
|
||||
const spaceId = file.fields?.spaceId?.value;
|
||||
|
||||
if (!attachmentType) {
|
||||
throw new BadRequestException('attachment type is required');
|
||||
}
|
||||
|
||||
if (
|
||||
!validAttachmentTypes.includes(attachmentType) ||
|
||||
attachmentType === AttachmentType.File
|
||||
) {
|
||||
throw new BadRequestException('Invalid image attachment type');
|
||||
}
|
||||
|
||||
if (attachmentType === AttachmentType.WorkspaceLogo) {
|
||||
const ability = this.workspaceAbility.createForUser(user, workspace);
|
||||
if (
|
||||
ability.cannot(
|
||||
WorkspaceCaslAction.Manage,
|
||||
WorkspaceCaslSubject.Settings,
|
||||
)
|
||||
) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
}
|
||||
|
||||
if (attachmentType === AttachmentType.SpaceLogo) {
|
||||
if (!spaceId) {
|
||||
throw new BadRequestException('spaceId is required');
|
||||
}
|
||||
|
||||
const spaceAbility = await this.spaceAbility.createForUser(user, spaceId);
|
||||
if (
|
||||
spaceAbility.cannot(SpaceCaslAction.Manage, SpaceCaslSubject.Settings)
|
||||
) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const fileResponse = await this.attachmentService.uploadImage(
|
||||
file,
|
||||
attachmentType,
|
||||
user.id,
|
||||
workspace.id,
|
||||
spaceId,
|
||||
);
|
||||
|
||||
return res.send(fileResponse);
|
||||
} catch (err: any) {
|
||||
this.logger.error(err);
|
||||
throw new BadRequestException('Error processing file upload.');
|
||||
}
|
||||
}
|
||||
|
||||
@Get('attachments/img/:attachmentType/:fileName')
|
||||
async getLogoOrAvatar(
|
||||
@Res() res: FastifyReply,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@Param('attachmentType') attachmentType: AttachmentType,
|
||||
@Param('fileName') fileName?: string,
|
||||
) {
|
||||
if (
|
||||
!validAttachmentTypes.includes(attachmentType) ||
|
||||
attachmentType === AttachmentType.File
|
||||
) {
|
||||
throw new BadRequestException('Invalid image attachment type');
|
||||
}
|
||||
|
||||
const filePath = `${getAttachmentFolderPath(attachmentType, workspace.id)}/${fileName}`;
|
||||
|
||||
try {
|
||||
const fileStream = await this.storageService.read(filePath);
|
||||
res.headers({
|
||||
'Content-Type': getMimeType(filePath),
|
||||
'Cache-Control': 'public, max-age=86400',
|
||||
});
|
||||
return res.send(fileStream);
|
||||
} catch (err) {
|
||||
this.logger.error(err);
|
||||
throw new NotFoundException('File not found');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import { IsNotEmpty, IsString, MaxLength, MinLength } from 'class-validator';
|
||||
import { CreateUserDto } from './create-user.dto';
|
||||
import {Transform, TransformFnParams} from "class-transformer";
|
||||
|
||||
export class CreateAdminUserDto extends CreateUserDto {
|
||||
@IsNotEmpty()
|
||||
@MinLength(3)
|
||||
@MaxLength(35)
|
||||
@MinLength(1)
|
||||
@MaxLength(50)
|
||||
@Transform(({ value }: TransformFnParams) => value?.trim())
|
||||
name: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@MinLength(4)
|
||||
@MaxLength(35)
|
||||
@MinLength(3)
|
||||
@MaxLength(50)
|
||||
@IsString()
|
||||
@Transform(({ value }: TransformFnParams) => value?.trim())
|
||||
workspaceName: string;
|
||||
}
|
||||
|
||||
@@ -6,12 +6,14 @@ import {
|
||||
MaxLength,
|
||||
MinLength,
|
||||
} from 'class-validator';
|
||||
import {Transform, TransformFnParams} from "class-transformer";
|
||||
|
||||
export class CreateUserDto {
|
||||
@IsOptional()
|
||||
@MinLength(2)
|
||||
@MaxLength(60)
|
||||
@MinLength(1)
|
||||
@MaxLength(50)
|
||||
@IsString()
|
||||
@Transform(({ value }: TransformFnParams) => value?.trim())
|
||||
name: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { IsString, MinLength } from 'class-validator';
|
||||
import { IsString } from 'class-validator';
|
||||
|
||||
export class VerifyUserTokenDto {
|
||||
@IsString()
|
||||
|
||||
@@ -31,7 +31,7 @@ export class TokenService {
|
||||
workspaceId,
|
||||
type: JwtType.REFRESH,
|
||||
};
|
||||
const expiresIn = '30d'; // todo: fix
|
||||
const expiresIn = this.environmentService.getJwtTokenExpiresIn();
|
||||
return this.jwtService.sign(payload, { expiresIn });
|
||||
}
|
||||
|
||||
|
||||
@@ -7,11 +7,13 @@ import {
|
||||
MaxLength,
|
||||
MinLength,
|
||||
} from 'class-validator';
|
||||
import {Transform, TransformFnParams} from "class-transformer";
|
||||
|
||||
export class CreateGroupDto {
|
||||
@MinLength(2)
|
||||
@MaxLength(50)
|
||||
@IsString()
|
||||
@Transform(({ value }: TransformFnParams) => value?.trim())
|
||||
name: string;
|
||||
|
||||
@IsOptional()
|
||||
|
||||
@@ -5,11 +5,13 @@ import {
|
||||
MaxLength,
|
||||
MinLength,
|
||||
} from 'class-validator';
|
||||
import {Transform, TransformFnParams} from "class-transformer";
|
||||
|
||||
export class CreateSpaceDto {
|
||||
@MinLength(2)
|
||||
@MaxLength(50)
|
||||
@IsString()
|
||||
@Transform(({ value }: TransformFnParams) => value?.trim())
|
||||
name: string;
|
||||
|
||||
@IsOptional()
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { IsOptional, IsString, MaxLength, MinLength } from 'class-validator';
|
||||
import {IsAlphanumeric, IsOptional, IsString, MaxLength, MinLength} from 'class-validator';
|
||||
import {Transform, TransformFnParams} from "class-transformer";
|
||||
|
||||
export class CreateWorkspaceDto {
|
||||
@MinLength(4)
|
||||
@MaxLength(64)
|
||||
@IsString()
|
||||
@Transform(({ value }: TransformFnParams) => value?.trim())
|
||||
name: string;
|
||||
|
||||
@IsOptional()
|
||||
@MinLength(4)
|
||||
@MaxLength(30)
|
||||
@IsString()
|
||||
@IsAlphanumeric()
|
||||
hostname?: string;
|
||||
|
||||
@IsOptional()
|
||||
|
||||
@@ -248,7 +248,7 @@ export class WorkspaceInvitationService {
|
||||
});
|
||||
|
||||
await this.mailService.sendToQueue({
|
||||
to: invitation.email,
|
||||
to: invitedByUser.email,
|
||||
subject: `${newUser.name} has accepted your Docmost invite`,
|
||||
template: emailTemplate,
|
||||
});
|
||||
|
||||
@@ -43,6 +43,11 @@ export class EnvironmentService {
|
||||
return this.configService.get<string>('STORAGE_DRIVER', 'local');
|
||||
}
|
||||
|
||||
getFileUploadSizeLimit(): string {
|
||||
|
||||
return this.configService.get<string>('FILE_UPLOAD_SIZE_LIMIT', '50mb');
|
||||
}
|
||||
|
||||
getAwsS3AccessKeyId(): string {
|
||||
return this.configService.get<string>('AWS_S3_ACCESS_KEY_ID');
|
||||
}
|
||||
@@ -98,6 +103,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');
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ export class RedisHealthIndicator extends HealthIndicator {
|
||||
});
|
||||
|
||||
await redis.ping();
|
||||
redis.disconnect();
|
||||
return this.getStatus(key, true);
|
||||
} catch (e) {
|
||||
this.logger.error(e);
|
||||
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
import { FileInterceptor } from '../../common/interceptors/file.interceptor';
|
||||
import * as bytes from 'bytes';
|
||||
import * as path from 'path';
|
||||
import { MAX_FILE_SIZE } from '../../core/attachment/attachment.constants';
|
||||
import { ImportService } from './import.service';
|
||||
import { AuthWorkspace } from '../../common/decorators/auth-workspace.decorator';
|
||||
|
||||
@@ -45,7 +44,7 @@ export class ImportController {
|
||||
) {
|
||||
const validFileExtensions = ['.md', '.html'];
|
||||
|
||||
const maxFileSize = bytes(MAX_FILE_SIZE);
|
||||
const maxFileSize = bytes('100mb');
|
||||
|
||||
let file = null;
|
||||
try {
|
||||
@@ -56,7 +55,7 @@ export class ImportController {
|
||||
this.logger.error(err.message);
|
||||
if (err?.statusCode === 413) {
|
||||
throw new BadRequestException(
|
||||
`File too large. Exceeds the ${MAX_FILE_SIZE} limit`,
|
||||
`File too large. Exceeds the 100mb import limit`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ export const mailDriverConfigProvider = {
|
||||
connectionTimeout: 30 * 1000, // 30 seconds
|
||||
auth,
|
||||
secure: environmentService.getSmtpSecure(),
|
||||
ignoreTLS: environmentService.getSmtpIgnoreTLS()
|
||||
} as SMTPTransport.Options,
|
||||
};
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ export class StaticModule implements OnModuleInit {
|
||||
ENV: this.environmentService.getNodeEnv(),
|
||||
APP_URL: this.environmentService.getAppUrl(),
|
||||
IS_CLOUD: this.environmentService.isCloud(),
|
||||
FILE_UPLOAD_SIZE_LIMIT: this.environmentService.getFileUploadSizeLimit()
|
||||
};
|
||||
|
||||
const windowScriptContent = `<script>window.CONFIG=${JSON.stringify(configString)};</script>`;
|
||||
|
||||
10
package.json
10
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "docmost",
|
||||
"homepage": "https://docmost.com",
|
||||
"version": "0.3.1",
|
||||
"version": "0.4.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "nx run-many -t build",
|
||||
@@ -59,6 +59,7 @@
|
||||
"@tiptap/react": "^2.6.6",
|
||||
"@tiptap/starter-kit": "^2.6.6",
|
||||
"@tiptap/suggestion": "^2.6.6",
|
||||
"bytes": "^3.1.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"fractional-indexing-jittered": "^0.9.1",
|
||||
"ioredis": "^5.4.1",
|
||||
@@ -68,6 +69,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nx/js": "19.6.3",
|
||||
"@types/bytes": "^3.1.4",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"concurrently": "^8.2.2",
|
||||
"nx": "19.6.3",
|
||||
@@ -79,7 +81,9 @@
|
||||
"packages/*"
|
||||
]
|
||||
},
|
||||
"prettier": {
|
||||
"semi": true
|
||||
"pnpm": {
|
||||
"patchedDependencies": {
|
||||
"react-arborist@3.4.0": "patches/react-arborist@3.4.0.patch"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
33
patches/react-arborist@3.4.0.patch
Normal file
33
patches/react-arborist@3.4.0.patch
Normal file
@@ -0,0 +1,33 @@
|
||||
diff --git a/dist/module/components/default-container.js b/dist/module/components/default-container.js
|
||||
index 47724f59b482454fe3144dbb98bd16d3df6a9c17..2285e35ea0073a773b7b74e22758056fd3514c1a 100644
|
||||
--- a/dist/module/components/default-container.js
|
||||
+++ b/dist/module/components/default-container.js
|
||||
@@ -34,28 +34,6 @@ export function DefaultContainer() {
|
||||
return;
|
||||
}
|
||||
if (e.key === "Backspace") {
|
||||
- if (!tree.props.onDelete)
|
||||
- return;
|
||||
- const ids = Array.from(tree.selectedIds);
|
||||
- if (ids.length > 1) {
|
||||
- let nextFocus = tree.mostRecentNode;
|
||||
- while (nextFocus && nextFocus.isSelected) {
|
||||
- nextFocus = nextFocus.nextSibling;
|
||||
- }
|
||||
- if (!nextFocus)
|
||||
- nextFocus = tree.lastNode;
|
||||
- tree.focus(nextFocus, { scroll: false });
|
||||
- tree.delete(Array.from(ids));
|
||||
- }
|
||||
- else {
|
||||
- const node = tree.focusedNode;
|
||||
- if (node) {
|
||||
- const sib = node.nextSibling;
|
||||
- const parent = node.parent;
|
||||
- tree.focus(sib || parent, { scroll: false });
|
||||
- tree.delete(node);
|
||||
- }
|
||||
- }
|
||||
return;
|
||||
}
|
||||
if (e.key === "Tab" && !e.shiftKey) {
|
||||
198
pnpm-lock.yaml
generated
198
pnpm-lock.yaml
generated
@@ -4,6 +4,11 @@ settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
patchedDependencies:
|
||||
react-arborist@3.4.0:
|
||||
hash: gjrtleyvvmuvu5j5zdnhxauhsu
|
||||
path: patches/react-arborist@3.4.0.patch
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
@@ -137,6 +142,9 @@ importers:
|
||||
'@tiptap/suggestion':
|
||||
specifier: ^2.6.6
|
||||
version: 2.6.6(@tiptap/core@2.6.6(@tiptap/pm@2.6.6))(@tiptap/pm@2.6.6)
|
||||
bytes:
|
||||
specifier: ^3.1.2
|
||||
version: 3.1.2
|
||||
cross-env:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3
|
||||
@@ -158,7 +166,10 @@ 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/bytes':
|
||||
specifier: ^3.1.4
|
||||
version: 3.1.4
|
||||
'@types/uuid':
|
||||
specifier: ^10.0.0
|
||||
version: 10.0.0
|
||||
@@ -167,7 +178,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
|
||||
@@ -180,6 +191,9 @@ importers:
|
||||
'@casl/react':
|
||||
specifier: ^4.0.0
|
||||
version: 4.0.0(@casl/ability@6.7.1)(react@18.3.1)
|
||||
'@docmost/editor-ext':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/editor-ext
|
||||
'@emoji-mart/data':
|
||||
specifier: ^1.2.1
|
||||
version: 1.2.1
|
||||
@@ -260,7 +274,7 @@ importers:
|
||||
version: 18.3.1
|
||||
react-arborist:
|
||||
specifier: ^3.4.0
|
||||
version: 3.4.0(@types/node@22.5.2)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
version: 3.4.0(patch_hash=gjrtleyvvmuvu5j5zdnhxauhsu)(@types/node@22.5.2)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
react-clear-modal:
|
||||
specifier: ^2.0.9
|
||||
version: 2.0.9(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
@@ -324,7 +338,7 @@ importers:
|
||||
version: 8.3.0(eslint@9.9.1(jiti@1.21.0))(typescript@5.5.4)
|
||||
'@vitejs/plugin-react':
|
||||
specifier: ^4.3.1
|
||||
version: 4.3.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))
|
||||
version: 4.3.1(vite@5.4.8(@types/node@22.5.2)(less@4.2.0)(sugarss@4.0.1(postcss@8.4.43))(terser@5.29.2))
|
||||
eslint:
|
||||
specifier: ^9.9.1
|
||||
version: 9.9.1(jiti@1.21.0)
|
||||
@@ -353,8 +367,8 @@ importers:
|
||||
specifier: ^5.5.4
|
||||
version: 5.5.4
|
||||
vite:
|
||||
specifier: ^5.4.2
|
||||
version: 5.4.2(@types/node@22.5.2)(less@4.2.0)(sugarss@4.0.1(postcss@8.4.43))(terser@5.29.2)
|
||||
specifier: ^5.4.8
|
||||
version: 5.4.8(@types/node@22.5.2)(less@4.2.0)(sugarss@4.0.1(postcss@8.4.43))(terser@5.29.2)
|
||||
|
||||
apps/server:
|
||||
dependencies:
|
||||
@@ -378,7 +392,7 @@ importers:
|
||||
version: 7.0.4
|
||||
'@nestjs/bullmq':
|
||||
specifier: ^10.2.1
|
||||
version: 10.2.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(bullmq@5.12.12)
|
||||
version: 10.2.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(bullmq@5.12.12)
|
||||
'@nestjs/common':
|
||||
specifier: ^10.4.1
|
||||
version: 10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)
|
||||
@@ -390,7 +404,7 @@ importers:
|
||||
version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)
|
||||
'@nestjs/event-emitter':
|
||||
specifier: ^2.0.4
|
||||
version: 2.0.4(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))
|
||||
version: 2.0.4(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)
|
||||
'@nestjs/jwt':
|
||||
specifier: ^10.2.0
|
||||
version: 10.2.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))
|
||||
@@ -402,13 +416,13 @@ importers:
|
||||
version: 10.0.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(passport@0.7.0)
|
||||
'@nestjs/platform-fastify':
|
||||
specifier: ^10.4.1
|
||||
version: 10.4.1(@fastify/static@7.0.4)(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))
|
||||
version: 10.4.1(@fastify/static@7.0.4)(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)
|
||||
'@nestjs/platform-socket.io':
|
||||
specifier: ^10.4.1
|
||||
version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.1)(rxjs@7.8.1)
|
||||
'@nestjs/terminus':
|
||||
specifier: ^10.2.3
|
||||
version: 10.2.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)
|
||||
version: 10.2.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)
|
||||
'@nestjs/websockets':
|
||||
specifier: ^10.4.1
|
||||
version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(@nestjs/platform-socket.io@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)
|
||||
@@ -427,9 +441,6 @@ importers:
|
||||
bullmq:
|
||||
specifier: ^5.12.12
|
||||
version: 5.12.12
|
||||
bytes:
|
||||
specifier: ^3.1.2
|
||||
version: 3.1.2
|
||||
class-transformer:
|
||||
specifier: ^0.5.1
|
||||
version: 0.5.1
|
||||
@@ -462,7 +473,7 @@ importers:
|
||||
version: 5.0.7
|
||||
nestjs-kysely:
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(kysely@0.27.4)(reflect-metadata@0.2.2)
|
||||
version: 1.0.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(kysely@0.27.4)(reflect-metadata@0.2.2)
|
||||
nodemailer:
|
||||
specifier: ^6.9.14
|
||||
version: 6.9.14
|
||||
@@ -502,19 +513,16 @@ 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)
|
||||
'@nestjs/testing':
|
||||
specifier: ^10.4.1
|
||||
version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))
|
||||
version: 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)
|
||||
'@types/bcrypt':
|
||||
specifier: ^5.0.2
|
||||
version: 5.0.2
|
||||
'@types/bytes':
|
||||
specifier: ^3.1.4
|
||||
version: 3.1.4
|
||||
'@types/debounce':
|
||||
specifier: ^1.2.4
|
||||
version: 1.2.4
|
||||
@@ -562,7 +570,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)
|
||||
@@ -580,13 +588,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
|
||||
@@ -4049,6 +4057,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==}
|
||||
@@ -5244,6 +5253,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==}
|
||||
@@ -6346,6 +6356,7 @@ packages:
|
||||
|
||||
npmlog@5.0.1:
|
||||
resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
|
||||
deprecated: This package is no longer supported.
|
||||
|
||||
nwsapi@2.2.10:
|
||||
resolution: {integrity: sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==}
|
||||
@@ -7791,8 +7802,8 @@ packages:
|
||||
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
vite@5.4.2:
|
||||
resolution: {integrity: sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==}
|
||||
vite@5.4.8:
|
||||
resolution: {integrity: sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@@ -10130,7 +10141,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
|
||||
@@ -10144,7 +10155,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
|
||||
@@ -10430,21 +10441,21 @@ snapshots:
|
||||
'@emnapi/runtime': 1.2.0
|
||||
'@tybys/wasm-util': 0.9.0
|
||||
|
||||
'@nestjs/bull-shared@10.2.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))':
|
||||
'@nestjs/bull-shared@10.2.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)':
|
||||
dependencies:
|
||||
'@nestjs/common': 10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)
|
||||
'@nestjs/core': 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)
|
||||
tslib: 2.6.3
|
||||
|
||||
'@nestjs/bullmq@10.2.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(bullmq@5.12.12)':
|
||||
'@nestjs/bullmq@10.2.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(bullmq@5.12.12)':
|
||||
dependencies:
|
||||
'@nestjs/bull-shared': 10.2.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))
|
||||
'@nestjs/bull-shared': 10.2.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)
|
||||
'@nestjs/common': 10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)
|
||||
'@nestjs/core': 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)
|
||||
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)
|
||||
@@ -10454,7 +10465,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
|
||||
@@ -10463,10 +10474,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
|
||||
@@ -10507,7 +10518,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
|
||||
'@nestjs/event-emitter@2.0.4(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))':
|
||||
'@nestjs/event-emitter@2.0.4(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)':
|
||||
dependencies:
|
||||
'@nestjs/common': 10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)
|
||||
'@nestjs/core': 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)
|
||||
@@ -10532,7 +10543,7 @@ snapshots:
|
||||
'@nestjs/common': 10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)
|
||||
passport: 0.7.0
|
||||
|
||||
'@nestjs/platform-fastify@10.4.1(@fastify/static@7.0.4)(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))':
|
||||
'@nestjs/platform-fastify@10.4.1(@fastify/static@7.0.4)(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)':
|
||||
dependencies:
|
||||
'@fastify/cors': 9.0.1
|
||||
'@fastify/formbody': 7.4.0
|
||||
@@ -10582,7 +10593,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- chokidar
|
||||
|
||||
'@nestjs/terminus@10.2.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)':
|
||||
'@nestjs/terminus@10.2.3(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)':
|
||||
dependencies:
|
||||
'@nestjs/common': 10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)
|
||||
'@nestjs/core': 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)
|
||||
@@ -10591,7 +10602,7 @@ snapshots:
|
||||
reflect-metadata: 0.2.2
|
||||
rxjs: 7.8.1
|
||||
|
||||
'@nestjs/testing@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))':
|
||||
'@nestjs/testing@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)':
|
||||
dependencies:
|
||||
'@nestjs/common': 10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)
|
||||
'@nestjs/core': 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)
|
||||
@@ -10650,15 +10661,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'
|
||||
@@ -10671,18 +10682,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'
|
||||
@@ -10696,20 +10707,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)
|
||||
@@ -10718,9 +10729,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)
|
||||
@@ -10738,7 +10749,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:
|
||||
@@ -10782,13 +10793,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:
|
||||
@@ -11397,7 +11408,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
|
||||
@@ -11412,6 +11423,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': {}
|
||||
@@ -12036,14 +12048,14 @@ snapshots:
|
||||
dependencies:
|
||||
'@ucast/core': 1.10.2
|
||||
|
||||
'@vitejs/plugin-react@4.3.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))':
|
||||
'@vitejs/plugin-react@4.3.1(vite@5.4.8(@types/node@22.5.2)(less@4.2.0)(sugarss@4.0.1(postcss@8.4.43))(terser@5.29.2))':
|
||||
dependencies:
|
||||
'@babel/core': 7.24.6
|
||||
'@babel/plugin-transform-react-jsx-self': 7.24.6(@babel/core@7.24.6)
|
||||
'@babel/plugin-transform-react-jsx-source': 7.24.6(@babel/core@7.24.6)
|
||||
'@types/babel__core': 7.20.5
|
||||
react-refresh: 0.14.2
|
||||
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)
|
||||
vite: 5.4.8(@types/node@22.5.2)(less@4.2.0)(sugarss@4.0.1(postcss@8.4.43))(terser@5.29.2)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -12757,13 +12769,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:
|
||||
@@ -13581,7 +13593,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
|
||||
@@ -13596,7 +13608,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:
|
||||
@@ -14110,16 +14122,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
|
||||
@@ -14129,7 +14141,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
|
||||
@@ -14155,7 +14167,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
|
||||
@@ -14381,12 +14393,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
|
||||
@@ -14864,7 +14876,7 @@ snapshots:
|
||||
|
||||
neo-async@2.6.2: {}
|
||||
|
||||
nestjs-kysely@1.0.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(kysely@0.27.4)(reflect-metadata@0.2.2):
|
||||
nestjs-kysely@1.0.0(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.1)(kysely@0.27.4)(reflect-metadata@0.2.2):
|
||||
dependencies:
|
||||
'@nestjs/common': 10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)
|
||||
'@nestjs/core': 10.4.1(@nestjs/common@10.4.1(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/websockets@10.4.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)
|
||||
@@ -14951,10 +14963,10 @@ snapshots:
|
||||
|
||||
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
|
||||
@@ -14999,7 +15011,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
|
||||
|
||||
@@ -15493,7 +15505,7 @@ snapshots:
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
|
||||
react-arborist@3.4.0(@types/node@22.5.2)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
react-arborist@3.4.0(patch_hash=gjrtleyvvmuvu5j5zdnhxauhsu)(@types/node@22.5.2)(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
react: 18.3.1
|
||||
react-dnd: 14.0.5(@types/node@22.5.2)(@types/react@18.3.5)(react@18.3.1)
|
||||
@@ -16152,16 +16164,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:
|
||||
@@ -16235,12 +16247,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
|
||||
@@ -16254,7 +16266,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
|
||||
@@ -16262,9 +16274,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
|
||||
@@ -16282,9 +16294,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
|
||||
@@ -16302,7 +16314,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:
|
||||
@@ -16449,7 +16461,7 @@ snapshots:
|
||||
|
||||
vary@1.1.2: {}
|
||||
|
||||
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):
|
||||
vite@5.4.8(@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
|
||||
postcss: 8.4.43
|
||||
@@ -16507,7 +16519,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
|
||||
@@ -16529,7 +16541,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:
|
||||
|
||||
Reference in New Issue
Block a user