Merge branch 'shell-updates' into 'main'
Shell updates See merge request digiresilience/link/link-stack!1
This commit is contained in:
commit
8949b10671
25 changed files with 3980 additions and 4813 deletions
|
|
@ -18,6 +18,7 @@ COPY --from=builder ${APP_DIR}/out/package-lock.json ./package-lock.json
|
||||||
RUN npm ci --omit=dev
|
RUN npm ci --omit=dev
|
||||||
|
|
||||||
COPY --from=builder ${APP_DIR}/out/full/ .
|
COPY --from=builder ${APP_DIR}/out/full/ .
|
||||||
|
ARG LINK_EMBEDDED=true
|
||||||
RUN npm i -g turbo
|
RUN npm i -g turbo
|
||||||
RUN turbo run build --filter=leafcutter
|
RUN turbo run build --filter=leafcutter
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,7 @@ import { useAppContext } from "./AppProvider";
|
||||||
|
|
||||||
export const Layout: FC<PropsWithChildren> = ({ children }) => {
|
export const Layout: FC<PropsWithChildren> = ({ children }) => {
|
||||||
const [cookies, setCookie] = useCookies(["cookieConsent"]);
|
const [cookies, setCookie] = useCookies(["cookieConsent"]);
|
||||||
|
|
||||||
const consentGranted = cookies.cookieConsent === "true";
|
const consentGranted = cookies.cookieConsent === "true";
|
||||||
|
|
||||||
const {
|
const {
|
||||||
publicRuntimeConfig: { embedded },
|
publicRuntimeConfig: { embedded },
|
||||||
} = getConfig();
|
} = getConfig();
|
||||||
|
|
|
||||||
|
|
@ -395,11 +395,21 @@
|
||||||
"display": "LGBT / Gender / Sexuality",
|
"display": "LGBT / Gender / Sexuality",
|
||||||
"description": ""
|
"description": ""
|
||||||
},
|
},
|
||||||
|
"male": {
|
||||||
|
"category": "",
|
||||||
|
"display": "Male",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
"media": {
|
"media": {
|
||||||
"category": "",
|
"category": "",
|
||||||
"display": "Media",
|
"display": "Media",
|
||||||
"description": ""
|
"description": ""
|
||||||
},
|
},
|
||||||
|
"non-binary": {
|
||||||
|
"category": "",
|
||||||
|
"display": "Non-binary",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
"policy-politics": {
|
"policy-politics": {
|
||||||
"category": "",
|
"category": "",
|
||||||
"display": "Policy / Politics",
|
"display": "Policy / Politics",
|
||||||
|
|
@ -415,6 +425,11 @@
|
||||||
"display": "Refugees",
|
"display": "Refugees",
|
||||||
"description": ""
|
"description": ""
|
||||||
},
|
},
|
||||||
|
"transgender": {
|
||||||
|
"category": "",
|
||||||
|
"display": "Transgender",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
"womens-rights": {
|
"womens-rights": {
|
||||||
"category": "",
|
"category": "",
|
||||||
"display": "Womens' Rights",
|
"display": "Womens' Rights",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
publicRuntimeConfig: {
|
publicRuntimeConfig: {
|
||||||
embedded: Boolean(process.env.LINK_EMBEDDED),
|
embedded: true
|
||||||
},
|
},
|
||||||
basePath: "/proxy/leafcutter",
|
basePath: "/proxy/leafcutter",
|
||||||
assetPrefix: "/proxy/leafcutter",
|
assetPrefix: "/proxy/leafcutter",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { FC, useState } from "react";
|
import { FC, useState } from "react";
|
||||||
|
import useSWR from "swr";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Grid,
|
Grid,
|
||||||
|
|
@ -19,12 +20,14 @@ import {
|
||||||
Cottage as CottageIcon,
|
Cottage as CottageIcon,
|
||||||
Settings as SettingsIcon,
|
Settings as SettingsIcon,
|
||||||
ExpandCircleDown as ExpandCircleDownIcon,
|
ExpandCircleDown as ExpandCircleDownIcon,
|
||||||
|
Dvr as DvrIcon,
|
||||||
} from "@mui/icons-material";
|
} from "@mui/icons-material";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import LinkLogo from "public/link-logo-small.png";
|
import LinkLogo from "public/link-logo-small.png";
|
||||||
import { useSession } from "next-auth/react";
|
import { useSession, signOut } from "next-auth/react";
|
||||||
|
import { getTicketOverviewCountsQuery } from "graphql/getTicketOverviewCountsQuery";
|
||||||
|
|
||||||
const openWidth = 270;
|
const openWidth = 270;
|
||||||
const closedWidth = 100;
|
const closedWidth = 100;
|
||||||
|
|
@ -38,8 +41,9 @@ const MenuItem = ({
|
||||||
selected = false,
|
selected = false,
|
||||||
open = true,
|
open = true,
|
||||||
badge,
|
badge,
|
||||||
|
target = "_self",
|
||||||
}: any) => (
|
}: any) => (
|
||||||
<Link href={href}>
|
<Link href={href} target={target}>
|
||||||
<ListItemButton
|
<ListItemButton
|
||||||
sx={{
|
sx={{
|
||||||
p: 0,
|
p: 0,
|
||||||
|
|
@ -124,7 +128,7 @@ const MenuItem = ({
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{badge && (
|
{badge && badge > 0 ? (
|
||||||
<ListItemSecondaryAction>
|
<ListItemSecondaryAction>
|
||||||
<Typography
|
<Typography
|
||||||
color="textSecondary"
|
color="textSecondary"
|
||||||
|
|
@ -142,7 +146,7 @@ const MenuItem = ({
|
||||||
{badge}
|
{badge}
|
||||||
</Typography>
|
</Typography>
|
||||||
</ListItemSecondaryAction>
|
</ListItemSecondaryAction>
|
||||||
)}
|
) : null}
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
|
@ -153,9 +157,28 @@ interface SidebarProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
|
export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
|
||||||
const { pathname } = useRouter();
|
const router = useRouter();
|
||||||
|
const { pathname } = router;
|
||||||
const { data: session } = useSession();
|
const { data: session } = useSession();
|
||||||
const username = session?.user?.name || "User";
|
const username = session?.user?.name || "User";
|
||||||
|
const { data: overviewData, error: overviewError }: any = useSWR(
|
||||||
|
{
|
||||||
|
document: getTicketOverviewCountsQuery,
|
||||||
|
},
|
||||||
|
{ refreshInterval: 10000 }
|
||||||
|
);
|
||||||
|
const findOverviewCountByID = (id: number) =>
|
||||||
|
overviewData?.ticketOverviews?.edges?.find((overview: any) =>
|
||||||
|
overview.node.id.endsWith(`/${id}`)
|
||||||
|
)?.node?.ticketCount ?? 0;
|
||||||
|
const assignedCount = findOverviewCountByID(1);
|
||||||
|
const urgentCount = findOverviewCountByID(7);
|
||||||
|
const pendingCount = findOverviewCountByID(3);
|
||||||
|
const unassignedCount = findOverviewCountByID(2);
|
||||||
|
|
||||||
|
const logout = () => {
|
||||||
|
signOut({ callbackUrl: "/login" });
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Drawer
|
<Drawer
|
||||||
|
|
@ -341,7 +364,6 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
|
||||||
iconSize={20}
|
iconSize={20}
|
||||||
open={open}
|
open={open}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Collapse
|
<Collapse
|
||||||
in={pathname.startsWith("/tickets")}
|
in={pathname.startsWith("/tickets")}
|
||||||
timeout="auto"
|
timeout="auto"
|
||||||
|
|
@ -355,7 +377,7 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
|
||||||
Icon={FeaturedPlayListIcon}
|
Icon={FeaturedPlayListIcon}
|
||||||
iconSize={0}
|
iconSize={0}
|
||||||
selected={pathname.endsWith("/tickets/assigned")}
|
selected={pathname.endsWith("/tickets/assigned")}
|
||||||
badge={3}
|
badge={assignedCount}
|
||||||
open={open}
|
open={open}
|
||||||
/>
|
/>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
|
|
@ -364,7 +386,7 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
|
||||||
Icon={FeaturedPlayListIcon}
|
Icon={FeaturedPlayListIcon}
|
||||||
iconSize={0}
|
iconSize={0}
|
||||||
selected={pathname.endsWith("/tickets/urgent")}
|
selected={pathname.endsWith("/tickets/urgent")}
|
||||||
badge={1}
|
badge={urgentCount}
|
||||||
open={open}
|
open={open}
|
||||||
/>
|
/>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
|
|
@ -373,7 +395,7 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
|
||||||
Icon={FeaturedPlayListIcon}
|
Icon={FeaturedPlayListIcon}
|
||||||
iconSize={0}
|
iconSize={0}
|
||||||
selected={pathname.endsWith("/tickets/pending")}
|
selected={pathname.endsWith("/tickets/pending")}
|
||||||
badge={9}
|
badge={pendingCount}
|
||||||
open={open}
|
open={open}
|
||||||
/>
|
/>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
|
|
@ -381,21 +403,12 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
|
||||||
href="/tickets/unassigned"
|
href="/tickets/unassigned"
|
||||||
Icon={FeaturedPlayListIcon}
|
Icon={FeaturedPlayListIcon}
|
||||||
iconSize={0}
|
iconSize={0}
|
||||||
selected={pathname.endsWith("/tickets/unnassigned")}
|
selected={pathname.endsWith("/tickets/unassigned")}
|
||||||
badge={27}
|
badge={unassignedCount}
|
||||||
open={open}
|
|
||||||
/>
|
|
||||||
<MenuItem
|
|
||||||
name="New Ticket UI"
|
|
||||||
href="/tickets/2"
|
|
||||||
Icon={SettingsIcon}
|
|
||||||
iconSize={0}
|
|
||||||
selected={pathname.endsWith("/tickets/2")}
|
|
||||||
open={open}
|
open={open}
|
||||||
/>
|
/>
|
||||||
</List>
|
</List>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
|
|
||||||
<MenuItem
|
<MenuItem
|
||||||
name="Knowledge Base"
|
name="Knowledge Base"
|
||||||
href="/knowledge"
|
href="/knowledge"
|
||||||
|
|
@ -406,7 +419,7 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
|
||||||
/>
|
/>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
name="Leafcutter"
|
name="Leafcutter"
|
||||||
href="/leafcutter"
|
href="/leafcutter/about"
|
||||||
Icon={AnalyticsIcon}
|
Icon={AnalyticsIcon}
|
||||||
iconSize={20}
|
iconSize={20}
|
||||||
selected={pathname.endsWith("/leafcutter")}
|
selected={pathname.endsWith("/leafcutter")}
|
||||||
|
|
@ -419,6 +432,7 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
|
||||||
onClick={undefined}
|
onClick={undefined}
|
||||||
>
|
>
|
||||||
<List component="div" disablePadding>
|
<List component="div" disablePadding>
|
||||||
|
{/*
|
||||||
<MenuItem
|
<MenuItem
|
||||||
name="Dashboard"
|
name="Dashboard"
|
||||||
href="/leafcutter"
|
href="/leafcutter"
|
||||||
|
|
@ -442,7 +456,7 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
|
||||||
selected={pathname.endsWith("/leafcutter/trends")}
|
selected={pathname.endsWith("/leafcutter/trends")}
|
||||||
open={open}
|
open={open}
|
||||||
/>
|
/>
|
||||||
|
*/}
|
||||||
<MenuItem
|
<MenuItem
|
||||||
name="FAQ"
|
name="FAQ"
|
||||||
href="/leafcutter/faq"
|
href="/leafcutter/faq"
|
||||||
|
|
@ -509,13 +523,21 @@ export const Sidebar: FC<SidebarProps> = ({ open, setOpen }) => {
|
||||||
/>
|
/>
|
||||||
</List>
|
</List>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
|
<MenuItem
|
||||||
|
name="Zammad Interface"
|
||||||
|
href="/proxy/zammad"
|
||||||
|
Icon={DvrIcon}
|
||||||
|
iconSize={20}
|
||||||
|
open={open}
|
||||||
|
target="_blank"
|
||||||
|
/>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
name="Logout"
|
name="Logout"
|
||||||
href="/logout"
|
href="/logout"
|
||||||
Icon={LogoutIcon}
|
Icon={LogoutIcon}
|
||||||
iconSize={20}
|
iconSize={20}
|
||||||
open={open}
|
open={open}
|
||||||
|
onClick={logout}
|
||||||
/>
|
/>
|
||||||
</List>
|
</List>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
|
||||||
84
apps/link/components/StyledDataGrid.tsx
Normal file
84
apps/link/components/StyledDataGrid.tsx
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
import { FC } from "react";
|
||||||
|
import { Box } from "@mui/material";
|
||||||
|
import {
|
||||||
|
DataGridPro,
|
||||||
|
GridColDef,
|
||||||
|
GridColumnVisibilityModel,
|
||||||
|
GridRowSelectionModel,
|
||||||
|
} from "@mui/x-data-grid-pro";
|
||||||
|
import { useCookies } from "react-cookie";
|
||||||
|
|
||||||
|
interface StyledDataGridProps {
|
||||||
|
name: string;
|
||||||
|
columns: GridColDef[];
|
||||||
|
rows: any[];
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
onRowClick?: (row: any) => void;
|
||||||
|
height?: string;
|
||||||
|
selectedRows?: GridRowSelectionModel;
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
setSelectedRows?: (rows: GridRowSelectionModel) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const StyledDataGrid: FC<StyledDataGridProps> = ({
|
||||||
|
name,
|
||||||
|
columns,
|
||||||
|
rows,
|
||||||
|
onRowClick,
|
||||||
|
height = "calc(100vh - 20px)",
|
||||||
|
selectedRows,
|
||||||
|
setSelectedRows,
|
||||||
|
}) => {
|
||||||
|
const cookieName = `${name}DataGridColumnState`;
|
||||||
|
const [cookies, setCookie] = useCookies([cookieName]);
|
||||||
|
const handleColumnVisibilityChange = (model: GridColumnVisibilityModel) =>
|
||||||
|
setCookie(cookieName, model, { path: "/" });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "#ddd",
|
||||||
|
border: 0,
|
||||||
|
width: "100%",
|
||||||
|
height,
|
||||||
|
".MuiDataGrid-row:nth-of-type(1n)": {
|
||||||
|
backgroundColor: "#f3f3f3",
|
||||||
|
},
|
||||||
|
".MuiDataGrid-row:nth-of-type(2n)": {
|
||||||
|
backgroundColor: "#fff",
|
||||||
|
},
|
||||||
|
".MuiDataGrid-columnHeaderTitle": {
|
||||||
|
color: "#333",
|
||||||
|
fontWeight: "bold",
|
||||||
|
fontSize: 11,
|
||||||
|
margin: "0 auto",
|
||||||
|
},
|
||||||
|
".MuiDataGrid-columnHeader": {
|
||||||
|
backgroundColor: "#ccc",
|
||||||
|
border: 0,
|
||||||
|
borderBottom: "3px solid #ddd",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DataGridPro
|
||||||
|
rows={rows}
|
||||||
|
columns={columns}
|
||||||
|
density="compact"
|
||||||
|
hideFooter
|
||||||
|
sx={{ height }}
|
||||||
|
rowBuffer={30}
|
||||||
|
checkboxSelection={!!setSelectedRows}
|
||||||
|
onRowSelectionModelChange={setSelectedRows}
|
||||||
|
rowSelectionModel={selectedRows}
|
||||||
|
rowHeight={46}
|
||||||
|
scrollbarSize={0}
|
||||||
|
disableVirtualization
|
||||||
|
columnVisibilityModel={
|
||||||
|
(cookies[cookieName] as GridColumnVisibilityModel) ?? undefined
|
||||||
|
}
|
||||||
|
onColumnVisibilityModelChange={handleColumnVisibilityChange}
|
||||||
|
onRowClick={onRowClick}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
71
apps/link/components/TicketList.tsx
Normal file
71
apps/link/components/TicketList.tsx
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
import { FC } from "react";
|
||||||
|
import { Grid, Box } from "@mui/material";
|
||||||
|
import { GridColDef } from "@mui/x-data-grid-pro";
|
||||||
|
import { StyledDataGrid } from "./StyledDataGrid";
|
||||||
|
import { typography } from "styles/theme";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
|
interface TicketListProps {
|
||||||
|
title: string;
|
||||||
|
tickets: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TicketList: FC<TicketListProps> = ({ title, tickets }) => {
|
||||||
|
const router = useRouter();
|
||||||
|
let gridColumns: GridColDef[] = [
|
||||||
|
{
|
||||||
|
field: "number",
|
||||||
|
headerName: "Number",
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: "title",
|
||||||
|
headerName: "Title",
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: "customer",
|
||||||
|
headerName: "Sender",
|
||||||
|
valueGetter: (params) => params.row?.customer?.fullname,
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: "group",
|
||||||
|
headerName: "Group",
|
||||||
|
valueGetter: (params) => params.row?.group?.name,
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
console.log({ tickets });
|
||||||
|
const rowClick = ({ row }) => {
|
||||||
|
router.push(`/tickets/${row.internalId}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{ height: "100vh", backgroundColor: "#ddd", p: 3 }}>
|
||||||
|
<Grid container direction="column">
|
||||||
|
<Grid item>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
backgroundColor: "#ddd",
|
||||||
|
px: "8px",
|
||||||
|
pb: "16px",
|
||||||
|
...typography.h4,
|
||||||
|
fontSize: 24,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</Box>
|
||||||
|
</Grid>
|
||||||
|
<Grid item>
|
||||||
|
<StyledDataGrid
|
||||||
|
name={title}
|
||||||
|
columns={gridColumns}
|
||||||
|
rows={tickets}
|
||||||
|
onRowClick={rowClick}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
19
apps/link/graphql/getTicketOverviewCountsQuery.ts
Normal file
19
apps/link/graphql/getTicketOverviewCountsQuery.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { gql } from 'graphql-request';
|
||||||
|
|
||||||
|
export const getTicketOverviewCountsQuery = gql`
|
||||||
|
query ticketOverviewTicketCount {
|
||||||
|
ticketOverviews {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
ticketCount
|
||||||
|
}
|
||||||
|
cursor
|
||||||
|
}
|
||||||
|
pageInfo {
|
||||||
|
endCursor
|
||||||
|
hasNextPage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`;
|
||||||
60
apps/link/graphql/getTicketsByOverviewQuery.ts
Normal file
60
apps/link/graphql/getTicketsByOverviewQuery.ts
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
import { gql } from 'graphql-request';
|
||||||
|
|
||||||
|
export const getTicketsByOverviewQuery = gql`
|
||||||
|
query ticketsByOverview($overviewId: ID!, $orderBy: String, $orderDirection: EnumOrderDirection, $cursor: String, $showPriority: Boolean = false, $showUpdatedBy: Boolean = false, $pageSize: Int = 10) {
|
||||||
|
ticketsByOverview(
|
||||||
|
overviewId: $overviewId
|
||||||
|
orderBy: $orderBy
|
||||||
|
orderDirection: $orderDirection
|
||||||
|
after: $cursor
|
||||||
|
first: $pageSize
|
||||||
|
) {
|
||||||
|
totalCount
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
internalId
|
||||||
|
number
|
||||||
|
title
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
updatedBy @include(if: $showUpdatedBy) {
|
||||||
|
id
|
||||||
|
fullname
|
||||||
|
}
|
||||||
|
customer {
|
||||||
|
id
|
||||||
|
firstname
|
||||||
|
lastname
|
||||||
|
fullname
|
||||||
|
}
|
||||||
|
organization {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
state {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
stateType {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
group {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
priority @include(if: $showPriority) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
uiColor
|
||||||
|
defaultCreate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cursor
|
||||||
|
}
|
||||||
|
pageInfo {
|
||||||
|
endCursor
|
||||||
|
hasNextPage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
import { NextResponse } from 'next/server';
|
import { NextResponse } from 'next/server';
|
||||||
import type { NextRequest } from 'next/server';
|
import type { NextRequest } from 'next/server';
|
||||||
import { withAuth } from "next-auth/middleware";
|
import { withAuth, NextRequestWithAuth } from "next-auth/middleware";
|
||||||
import { getToken } from "next-auth/jwt";
|
|
||||||
|
|
||||||
const rewriteURL = (request: NextRequest, originBaseURL: string, destinationBaseURL: string, headers: any = {}) => {
|
const rewriteURL = (request: NextRequest, originBaseURL: string, destinationBaseURL: string, headers: any = {}) => {
|
||||||
|
if (request.nextUrl.protocol.startsWith('ws')) {
|
||||||
|
return NextResponse.next();
|
||||||
|
}
|
||||||
|
|
||||||
if (request.nextUrl.pathname.includes('/_next/static/development/')) {
|
if (request.nextUrl.pathname.includes('/_next/static/development/')) {
|
||||||
return NextResponse.next();
|
return NextResponse.next();
|
||||||
}
|
}
|
||||||
|
|
@ -11,31 +14,54 @@ const rewriteURL = (request: NextRequest, originBaseURL: string, destinationBase
|
||||||
const destinationURL = request.url.replace(originBaseURL, destinationBaseURL);
|
const destinationURL = request.url.replace(originBaseURL, destinationBaseURL);
|
||||||
console.log(`Rewriting ${request.url} to ${destinationURL}`);
|
console.log(`Rewriting ${request.url} to ${destinationURL}`);
|
||||||
|
|
||||||
return NextResponse.rewrite(new URL(destinationURL), { ...request.headers, ...headers });
|
const requestHeaders = new Headers(request.headers);
|
||||||
|
for (const [key, value] of Object.entries(headers)) {
|
||||||
|
// @ts-ignore
|
||||||
|
requestHeaders.set(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
requestHeaders.delete('connection');
|
||||||
|
|
||||||
|
console.log({ finalHeaders: requestHeaders });
|
||||||
|
|
||||||
|
return NextResponse.rewrite(new URL(destinationURL), { request: { headers: requestHeaders } });
|
||||||
};
|
};
|
||||||
|
|
||||||
const checkRewrites = async (request: NextRequest) => {
|
const checkRewrites = async (request: NextRequestWithAuth) => {
|
||||||
|
console.log({ currentURL: request.nextUrl.href });
|
||||||
|
|
||||||
|
const linkBaseURL = process.env.LINK_URL ?? "http://localhost:3000";
|
||||||
|
const zammadURL = process.env.ZAMMAD_URL ?? "http://zammad-nginx:8080";
|
||||||
|
const leafcutterURL = process.env.LEAFCUTTER_URL ?? "http://leafcutter:3000";
|
||||||
|
const metamigoURL = process.env.METAMIGO_URL ?? "http://metamigo:3000";
|
||||||
|
|
||||||
if (request.nextUrl.pathname.startsWith('/proxy/leafcutter')) {
|
if (request.nextUrl.pathname.startsWith('/proxy/leafcutter')) {
|
||||||
return rewriteURL(request, process.env.LINK_URL, process.env.LEAFCUTTER_URL);
|
return rewriteURL(request, linkBaseURL, leafcutterURL);
|
||||||
} else if (request.nextUrl.pathname.startsWith('/proxy/metamigo')) {
|
} else if (request.nextUrl.pathname.startsWith('/proxy/metamigo')) {
|
||||||
return rewriteURL(request, process.env.LINK_URL, process.env.METAMIGO_URL);
|
return rewriteURL(request, linkBaseURL, metamigoURL);
|
||||||
} else if (request.nextUrl.pathname.startsWith('/proxy/zammad')) {
|
} else if (request.nextUrl.pathname.startsWith('/proxy/zammad')) {
|
||||||
const session = await getToken({
|
console.log('proxying to zammad');
|
||||||
req: request,
|
const { token } = request.nextauth;
|
||||||
secret: process.env.NEXTAUTH_SECRET,
|
|
||||||
});
|
console.log({ nextauth: request.nextauth });
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
'X-Forwarded-User': session.email.toLowerCase(),
|
'X-Forwarded-User': token.email.toLowerCase(),
|
||||||
host: 'zammad.example.com'
|
host: 'link-stack-dev.digiresilience.org'
|
||||||
};
|
};
|
||||||
|
|
||||||
return rewriteURL(request, `${process.env.LINK_URL}/proxy/zammad`, process.env.ZAMMAD_URL, headers);
|
console.log({ headers });
|
||||||
} else if (request.nextUrl.pathname.startsWith('/assets')) {
|
|
||||||
|
return rewriteURL(request, `${linkBaseURL}/proxy/zammad`, zammadURL, headers);
|
||||||
|
} else if (request.nextUrl.pathname.startsWith('/assets') || request.nextUrl.pathname.startsWith('/api/v1')) {
|
||||||
console.log('asset');
|
console.log('asset');
|
||||||
return rewriteURL(request, `${process.env.LINK_URL}`, process.env.ZAMMAD_URL);
|
return rewriteURL(request, linkBaseURL, zammadURL);
|
||||||
} else if (request.nextUrl.pathname.startsWith('/proxy/assets') || request.nextUrl.pathname.startsWith('/proxy/api')) {
|
} else if (request.nextUrl.pathname.startsWith('/proxy/assets')) {
|
||||||
console.log('proxy asset');
|
console.log('proxy asset');
|
||||||
return rewriteURL(request, `${process.env.LINK_URL}/proxy`, process.env.ZAMMAD_URL);
|
return rewriteURL(request, `${linkBaseURL}/proxy`, zammadURL);
|
||||||
|
} else if (request.nextUrl.pathname.startsWith('/proxy/api')) {
|
||||||
|
console.log('proxy api');
|
||||||
|
return rewriteURL(request, `${linkBaseURL}/proxy`, zammadURL);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -71,4 +97,3 @@ export default withAuth(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,17 @@ const nextConfig = {
|
||||||
linkURL: process.env.LINK_URL,
|
linkURL: process.env.LINK_URL,
|
||||||
leafcutterURL: process.env.LEAFCUTTER_URL,
|
leafcutterURL: process.env.LEAFCUTTER_URL,
|
||||||
metamigoURL: process.env.METAMIGO_URL,
|
metamigoURL: process.env.METAMIGO_URL,
|
||||||
|
muiLicenseKey: process.env.MUI_LICENSE_KEY,
|
||||||
|
},
|
||||||
|
async rewrites() {
|
||||||
|
return {
|
||||||
|
fallback: [
|
||||||
|
{
|
||||||
|
source: "/:path*",
|
||||||
|
destination: `/proxy/zammad/:path*`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,10 @@ import "styles/global.css";
|
||||||
import { LicenseInfo } from "@mui/x-data-grid-pro";
|
import { LicenseInfo } from "@mui/x-data-grid-pro";
|
||||||
import { SWRConfig } from "swr";
|
import { SWRConfig } from "swr";
|
||||||
import { GraphQLClient } from "graphql-request";
|
import { GraphQLClient } from "graphql-request";
|
||||||
|
import getConfig from "next/config";
|
||||||
|
|
||||||
LicenseInfo.setLicenseKey(process.env.MUI_LICENSE_KEY);
|
const { publicRuntimeConfig } = getConfig();
|
||||||
|
LicenseInfo.setLicenseKey(publicRuntimeConfig.muiLicenseKey);
|
||||||
|
|
||||||
const clientSideEmotionCache: any = createEmotionCache();
|
const clientSideEmotionCache: any = createEmotionCache();
|
||||||
|
|
||||||
|
|
|
||||||
5
apps/link/pages/api/v1/users
Normal file
5
apps/link/pages/api/v1/users
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
|
||||||
|
export default function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
res.redirect(307, '/proxy/zammad/api/v1' + req.url.substring('/api/v1'.length));
|
||||||
|
}
|
||||||
|
|
@ -49,7 +49,7 @@ const Login: FC<LoginProps> = ({ session }) => {
|
||||||
sx={buttonStyles}
|
sx={buttonStyles}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
signIn("google", {
|
signIn("google", {
|
||||||
callbackUrl: `${origin}/auth/sso`,
|
callbackUrl: `${origin}/proxy/zammad/auth/sso`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,32 @@
|
||||||
import { FC } from "react";
|
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import { Grid } from "@mui/material";
|
import useSWR from "swr";
|
||||||
|
import { NextPage } from "next";
|
||||||
import { Layout } from "components/Layout";
|
import { Layout } from "components/Layout";
|
||||||
import { ZammadWrapper } from "components/ZammadWrapper";
|
import { TicketList } from "components/TicketList";
|
||||||
|
import { getTicketsByOverviewQuery } from "graphql/getTicketsByOverviewQuery";
|
||||||
|
|
||||||
const Assigned: FC = () => (
|
const Assigned: NextPage = () => {
|
||||||
<Layout>
|
const { data: ticketData, error: ticketError }: any = useSWR(
|
||||||
<Head>
|
{
|
||||||
<title>Link Shell</title>
|
document: getTicketsByOverviewQuery,
|
||||||
</Head>
|
variables: { overviewId: "gid://zammad/Overview/1" },
|
||||||
<Grid
|
},
|
||||||
container
|
{ refreshInterval: 10000 }
|
||||||
spacing={0}
|
|
||||||
sx={{ height: "100%", width: "100%" }}
|
|
||||||
direction="column"
|
|
||||||
>
|
|
||||||
<Grid
|
|
||||||
item
|
|
||||||
sx={{
|
|
||||||
height: "100%",
|
|
||||||
width: "100%",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ZammadWrapper path="/#ticket/view/my_assigned" />
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Layout>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const shouldRender = !ticketError && ticketData;
|
||||||
|
const tickets =
|
||||||
|
ticketData?.ticketsByOverview?.edges.map((edge: any) => edge.node) || [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<Head>
|
||||||
|
<title>Link Shell – Assigned Tickets</title>
|
||||||
|
</Head>
|
||||||
|
{shouldRender && <TicketList title="Assigned" tickets={tickets} />}
|
||||||
|
{ticketError && <div>{ticketError.toString()}</div>}
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default Assigned;
|
export default Assigned;
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,32 @@
|
||||||
import { FC } from "react";
|
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import { Grid } from "@mui/material";
|
import useSWR from "swr";
|
||||||
|
import { NextPage } from "next";
|
||||||
import { Layout } from "components/Layout";
|
import { Layout } from "components/Layout";
|
||||||
import { ZammadWrapper } from "components/ZammadWrapper";
|
import { TicketList } from "components/TicketList";
|
||||||
|
import { getTicketsByOverviewQuery } from "graphql/getTicketsByOverviewQuery";
|
||||||
|
|
||||||
const Pending: FC = () => (
|
const Pending: NextPage = () => {
|
||||||
<Layout>
|
const { data: ticketData, error: ticketError }: any = useSWR(
|
||||||
<Head>
|
{
|
||||||
<title>Link Shell</title>
|
document: getTicketsByOverviewQuery,
|
||||||
</Head>
|
variables: { overviewId: "gid://zammad/Overview/3" },
|
||||||
<Grid
|
},
|
||||||
container
|
{ refreshInterval: 10000 }
|
||||||
spacing={0}
|
|
||||||
sx={{ height: "100%", width: "100%" }}
|
|
||||||
direction="column"
|
|
||||||
>
|
|
||||||
<Grid
|
|
||||||
item
|
|
||||||
sx={{
|
|
||||||
height: "100%",
|
|
||||||
width: "100%",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ZammadWrapper path="/#ticket/view/my_pending_reached" />
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Layout>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const shouldRender = !ticketError && ticketData;
|
||||||
|
const tickets =
|
||||||
|
ticketData?.ticketsByOverview?.edges.map((edge: any) => edge.node) || [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<Head>
|
||||||
|
<title>Link Shell – Assigned Tickets</title>
|
||||||
|
</Head>
|
||||||
|
{shouldRender && <TicketList title="Pending" tickets={tickets} />}
|
||||||
|
{ticketError && <div>{ticketError.toString()}</div>}
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default Pending;
|
export default Pending;
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,32 @@
|
||||||
import { FC } from "react";
|
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import { Grid } from "@mui/material";
|
import useSWR from "swr";
|
||||||
|
import { NextPage } from "next";
|
||||||
import { Layout } from "components/Layout";
|
import { Layout } from "components/Layout";
|
||||||
import { ZammadWrapper } from "components/ZammadWrapper";
|
import { TicketList } from "components/TicketList";
|
||||||
|
import { getTicketsByOverviewQuery } from "graphql/getTicketsByOverviewQuery";
|
||||||
|
|
||||||
const Unassigned: FC = () => (
|
const Unassigned: NextPage = () => {
|
||||||
<Layout>
|
const { data: ticketData, error: ticketError }: any = useSWR(
|
||||||
<Head>
|
{
|
||||||
<title>Link Shell</title>
|
document: getTicketsByOverviewQuery,
|
||||||
</Head>
|
variables: { overviewId: "gid://zammad/Overview/2" },
|
||||||
<Grid
|
},
|
||||||
container
|
{ refreshInterval: 10000 }
|
||||||
spacing={0}
|
|
||||||
sx={{ height: "100%", width: "100%" }}
|
|
||||||
direction="column"
|
|
||||||
>
|
|
||||||
<Grid
|
|
||||||
item
|
|
||||||
sx={{
|
|
||||||
height: "100%",
|
|
||||||
width: "100%",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ZammadWrapper path="/#ticket/view/all_unassigned" />
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Layout>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const shouldRender = !ticketError && ticketData;
|
||||||
|
const tickets =
|
||||||
|
ticketData?.ticketsByOverview?.edges.map((edge: any) => edge.node) || [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<Head>
|
||||||
|
<title>Link Shell – Assigned Tickets</title>
|
||||||
|
</Head>
|
||||||
|
{shouldRender && <TicketList title="Unassigned" tickets={tickets} />}
|
||||||
|
{ticketError && <div>{ticketError.toString()}</div>}
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default Unassigned;
|
export default Unassigned;
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,32 @@
|
||||||
import { FC } from "react";
|
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import { Grid } from "@mui/material";
|
import useSWR from "swr";
|
||||||
|
import { NextPage } from "next";
|
||||||
import { Layout } from "components/Layout";
|
import { Layout } from "components/Layout";
|
||||||
import { ZammadWrapper } from "components/ZammadWrapper";
|
import { TicketList } from "components/TicketList";
|
||||||
|
import { getTicketsByOverviewQuery } from "graphql/getTicketsByOverviewQuery";
|
||||||
|
|
||||||
const Urgent: FC = () => (
|
const Urgent: NextPage = () => {
|
||||||
<Layout>
|
const { data: ticketData, error: ticketError }: any = useSWR(
|
||||||
<Head>
|
{
|
||||||
<title>Link Shell</title>
|
document: getTicketsByOverviewQuery,
|
||||||
</Head>
|
variables: { overviewId: "gid://zammad/Overview/7" },
|
||||||
<Grid
|
},
|
||||||
container
|
{ refreshInterval: 10000 }
|
||||||
spacing={0}
|
|
||||||
sx={{ height: "100%", width: "100%" }}
|
|
||||||
direction="column"
|
|
||||||
>
|
|
||||||
<Grid
|
|
||||||
item
|
|
||||||
sx={{
|
|
||||||
height: "100%",
|
|
||||||
width: "100%",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ZammadWrapper path="/#ticket/view/all_escalated" />
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Layout>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const shouldRender = !ticketError && ticketData;
|
||||||
|
const tickets =
|
||||||
|
ticketData?.ticketsByOverview?.edges.map((edge: any) => edge.node) || [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<Head>
|
||||||
|
<title>Link Shell – Urgent Tickets</title>
|
||||||
|
</Head>
|
||||||
|
{shouldRender && <TicketList title="Urgent" tickets={tickets} />}
|
||||||
|
{ticketError && <div>{ticketError.toString()}</div>}
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default Urgent;
|
export default Urgent;
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@
|
||||||
"pg": "^8.11.0",
|
"pg": "^8.11.0",
|
||||||
"pg-monitor": "^2.0.0",
|
"pg-monitor": "^2.0.0",
|
||||||
"pg-promise": "^11.4.3",
|
"pg-promise": "^11.4.3",
|
||||||
|
"postgraphile": "4.12.3",
|
||||||
"postgraphile-plugin-connection-filter": "^2.3.0",
|
"postgraphile-plugin-connection-filter": "^2.3.0",
|
||||||
"remeda": "^1.18.1",
|
"remeda": "^1.18.1",
|
||||||
"twilio": "^4.11.1",
|
"twilio": "^4.11.1",
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import {
|
||||||
TextField,
|
TextField,
|
||||||
EmailField,
|
EmailField,
|
||||||
BooleanField,
|
BooleanField,
|
||||||
ListProps,
|
|
||||||
} from "react-admin";
|
} from "react-admin";
|
||||||
|
|
||||||
const UserList = () => (
|
const UserList = () => (
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import Cognito from "next-auth/providers/cognito";
|
||||||
import { loadConfig, IAppConfig } from "@digiresilience/metamigo-config";
|
import { loadConfig, IAppConfig } from "@digiresilience/metamigo-config";
|
||||||
import { MetamigoAdapter } from "../../../lib/nextauth-adapter";
|
import { MetamigoAdapter } from "../../../lib/nextauth-adapter";
|
||||||
import { CloudflareAccessProvider } from "../../../lib/cloudflare";
|
import { CloudflareAccessProvider } from "../../../lib/cloudflare";
|
||||||
import { AdapterSession, AdapterUser } from "next-auth/adapters";
|
|
||||||
|
|
||||||
const nextAuthOptions = (config: IAppConfig, req: NextApiRequest) => {
|
const nextAuthOptions = (config: IAppConfig, req: NextApiRequest) => {
|
||||||
const { nextAuth, cfaccess } = config;
|
const { nextAuth, cfaccess } = config;
|
||||||
|
|
@ -70,8 +69,8 @@ const nextAuthOptions = (config: IAppConfig, req: NextApiRequest) => {
|
||||||
providers,
|
providers,
|
||||||
adapter,
|
adapter,
|
||||||
callbacks: {
|
callbacks: {
|
||||||
async session({session, token, user}) {
|
async session({ session, user }: any) {
|
||||||
session.user.id = user.id
|
session.user.id = user.id;
|
||||||
session.user.userRole = user.userRole;
|
session.user.userRole = user.userRole;
|
||||||
return session;
|
return session;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -171,6 +171,17 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- zammad-data:/opt/zammad
|
- zammad-data:/opt/zammad
|
||||||
|
|
||||||
|
opensearch:
|
||||||
|
container_name: opensearch
|
||||||
|
build: ./docker/opensearch
|
||||||
|
restart: ${RESTART}
|
||||||
|
volumes:
|
||||||
|
- opensearch-data:/usr/share/opensearch/data
|
||||||
|
|
||||||
|
opensearch-dashboards:
|
||||||
|
container_name: opensearch-dashboards
|
||||||
|
build: ./docker/opensearch-dashboards
|
||||||
|
restart: ${RESTART}
|
||||||
|
|
||||||
metamigo-postgresql:
|
metamigo-postgresql:
|
||||||
build: ./docker/postgresql
|
build: ./docker/postgresql
|
||||||
|
|
@ -256,40 +267,49 @@ services:
|
||||||
# volumes:
|
# volumes:
|
||||||
# - /var/run/docker.sock:/tmp/docker.sock:ro
|
# - /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
|
|
||||||
# link:
|
link:
|
||||||
# container_name: link
|
container_name: link
|
||||||
# build:
|
restart: ${RESTART}
|
||||||
# context: .
|
build:
|
||||||
# dockerfile: ./apps/link/Dockerfile
|
context: .
|
||||||
# expose:
|
dockerfile: ./apps/link/Dockerfile
|
||||||
# - "3000"
|
expose:
|
||||||
# ports:
|
- "3000"
|
||||||
# - "8003:3000"
|
ports:
|
||||||
# environment:
|
- "8003:3000"
|
||||||
# <<: *common-global-variables
|
environment:
|
||||||
# ZAMMAD_PROXY_URL: ${ZAMMAD_PROXY_URL}
|
ZAMMAD_PROXY_URL: ${ZAMMAD_PROXY_URL}
|
||||||
# ZAMMAD_URL: ${ZAMMAD_URL}
|
ZAMMAD_API_TOKEN: ${ZAMMAD_API_TOKEN}
|
||||||
# ZAMMAD_API_TOKEN: ${ZAMMAD_API_TOKEN}
|
ZAMMAD_VIRUAL_HOST: ${ZAMMAD_VIRTUAL_HOST}
|
||||||
# ZAMMAD_VIRUAL_HOST: ${ZAMMAD_VIRTUAL_HOST}
|
LINK_URL: ${LINK_URL}
|
||||||
# LINK_URL: ${LINK_URL}
|
LEAFCUTTER_URL: http://leafcutter:3000
|
||||||
# NEXTAUTH_URL: ${LINK_URL}
|
METAMIGO_URL: http://metamigo-frontend:3000
|
||||||
# NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
|
ZAMMAD_URL: http://localhost:8001
|
||||||
# NEXTAUTH_AUDIENCE: ${NEXTAUTH_AUDIENCE}
|
NEXTAUTH_URL: ${LINK_URL}
|
||||||
# NEXTAUTH_SIGNING_KEY_B64: ${NEXTAUTH_SIGNING_KEY_B64}
|
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
|
||||||
# NEXTAUTH_ENCRYPTION_KEY_B64: ${NEXTAUTH_ENCRYPTION_KEY_B64}
|
NEXTAUTH_AUDIENCE: ${NEXTAUTH_AUDIENCE}
|
||||||
# GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID}
|
NEXTAUTH_SIGNING_KEY_B64: ${NEXTAUTH_SIGNING_KEY_B64}
|
||||||
# GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET}
|
NEXTAUTH_ENCRYPTION_KEY_B64: ${NEXTAUTH_ENCRYPTION_KEY_B64}
|
||||||
|
GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID}
|
||||||
|
GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET}
|
||||||
|
|
||||||
# leafcutter:
|
leafcutter:
|
||||||
# container_name: leafcutter
|
container_name: leafcutter
|
||||||
# build:
|
restart: ${RESTART}
|
||||||
# context: .
|
build:
|
||||||
# dockerfile: ./apps/leafcutter/Dockerfile
|
context: .
|
||||||
# ports:
|
dockerfile: ./apps/leafcutter/Dockerfile
|
||||||
# - "8004:3000"
|
expose:
|
||||||
# environment:
|
- "3000"
|
||||||
# <<: *common-global-variables
|
ports:
|
||||||
# LINK_EMBEDDED: "true"
|
- "8004:3000"
|
||||||
|
environment:
|
||||||
|
LINK_EMBEDDED: "true"
|
||||||
|
NEXTAUTH_URL: ${LINK_URL}
|
||||||
|
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
|
||||||
|
NEXTAUTH_AUDIENCE: ${NEXTAUTH_AUDIENCE}
|
||||||
|
NEXTAUTH_SIGNING_KEY_B64: ${NEXTAUTH_SIGNING_KEY_B64}
|
||||||
|
NEXTAUTH_ENCRYPTION_KEY_B64: ${NEXTAUTH_ENCRYPTION_KEY_B64}
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
elasticsearch-data:
|
elasticsearch-data:
|
||||||
|
|
@ -300,3 +320,5 @@ volumes:
|
||||||
driver: local
|
driver: local
|
||||||
metamigo-data:
|
metamigo-data:
|
||||||
driver: local
|
driver: local
|
||||||
|
opensearch-data:
|
||||||
|
driver: local
|
||||||
|
|
|
||||||
1
docker/opensearch-dashboards/Dockerfile
Normal file
1
docker/opensearch-dashboards/Dockerfile
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
FROM opensearchproject/opensearch-dashboards:2.8.0
|
||||||
1
docker/opensearch/Dockerfile
Normal file
1
docker/opensearch/Dockerfile
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
FROM opensearchproject/opensearch:2.8.0
|
||||||
8071
package-lock.json
generated
8071
package-lock.json
generated
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue