improve ssh_command and add ssh key display at user details page
+ 301
- 77
@@ -86,15 +86,19 @@ COPY ./.gitstamp    /app/.gitstamp
 RUN rm -rf /app/.git
 
 # Install required dependencies
-RUN apt-get update -y && apt-get install git-core openssh-server gnupg sudo curl jq ca-certificates -y
+RUN apt-get update -y && apt-get install git-core openssh-server gnupg sudo curl jq ca-certificates wget -y
 RUN mkdir -p /etc/apt/keyrings && \
-    curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \
+    wget --output-document - --quiet https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \
         | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \
     echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" \
         | tee /etc/apt/sources.list.d/nodesource.list && \
+    wget --output-document - --quiet https://dl.yarnpkg.com/debian/pubkey.gpg \
+        | gpg --dearmor -o /etc/apt/keyrings/yarn-archive-keyring.gpg && \
+    echo "deb [signed-by=/etc/apt/keyrings/yarn-archive-keyring.gpg] https://dl.yarnpkg.com/debian/ stable main" \
+        | tee /etc/apt/sources.list.d/yarn.list && \
     apt-get -qq update && \
     apt-get -qq -y install --no-install-recommends \
-      nodejs=$(apt-cache show nodejs | grep -F 'Version: 20.0.0' | cut -f 2 -d ' ')
+      yarn nodejs=$(apt-cache show nodejs | grep -F 'Version: 20.0.0' | cut -f 2 -d ' ')
 
 # Add git-shell to system shells
 RUN echo "/usr/bin/git-shell" >> /etc/shells

...
@@ -117,6 +121,7 @@ RUN usermod --home /home/git/repos git
 RUN sed -i -E 's/auth       required   pam_shells.so/auth       sufficient   pam_shells.so/' /etc/pam.d/chsh
 # Enable Password-less SSH Authentication (private-keys only)
 RUN sed -i -E 's/#?PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
+RUN sed -i -E 's/#LogLevel INFO/LogLevel ERROR/' /etc/ssh/sshd_config
 # RUN echo "ForceCommand /usr/bin/ssh_command" >> /etc/ssh/sshd_config
 RUN echo "AllowUsers root git" >> /etc/ssh/sshd_config
 RUN echo "AuthorizedKeysFile .ssh/authorized_keys /home/git/.ssh/authorized_keys" >> /etc/ssh/sshd_config

@@ -1,5 +1,5 @@
 {
-  "_generatedAtUnix": 1702820538258,
+  "_generatedAtUnix": 1712449693623,
   "_hashAlgorithm": "sha1",
   "_version": 2,
   "assets": {

...
@@ -86,6 +86,12 @@
       "pathSource": "./app/islands/RepositoryTreeView.tsx",
       "pathBundle": "./public/.islands/RepositoryTreeView.bundle.js",
       "pathSourceMap": "./public/.islands/RepositoryTreeView.bundle.js.map"
+    },
+    "SSHKeyItem": {
+      "hash": "8bf0cef0ee6fefe102e3d99e23fd764d6f06bd22",
+      "pathSource": "./app/islands/SSHKeyItem.tsx",
+      "pathBundle": "./public/.islands/SSHKeyItem.bundle.js",
+      "pathSourceMap": "./public/.islands/SSHKeyItem.bundle.js.map"
     }
   },
   "views": {

...
@@ -158,7 +164,7 @@
       "pathSource": "./app/views/user/UserDashboardView.tsx"
     },
     "UserDetailsView": {
-      "hash": "29c0fbd75974f8568b3aabb9e5ef72fc08b1f628",
+      "hash": "ce954e6fe9d3b11e5f822c6ba426df7ea2501758",
       "pathSource": "./app/views/user/UserDetailsView.tsx"
     }
   }

@@ -1,8 +1,15 @@
 import React, { PropsWithChildren } from "react";
+import RootAppRouter from "./routes";
 
 // import { AppRoute } from "./routes.defs";
 // import HomeView from "./views/HomeView";
 
 export function App({ children }: PropsWithChildren<{}>) {
+  return (
+    <>
+      <RootAppRouter server={null as any} />
+      {children}
+    </>
+  );
   return <>{children}</>;
 }

app/components/Button.styled.ts
@@ -52,7 +52,7 @@ const baseButtonCss = css<ButtonProps>`
   }
 
   &:hover:not(:disabled) {
-    background-color: ${Colors.PURPLE_01};
+    background-color: ${Colors.CYAN_01};
     border-color: ${Colors.WHITE_01};
     transform: translateY(-2px) scale(1.015);
   }

...
@@ -64,8 +64,8 @@ const baseButtonCss = css<ButtonProps>`
   }
 
   &:active:not(:disabled) {
-    outline: ${Colors.PURPLE_01} none medium;
-    background-color: ${Colors.PURPLE_01};
+    outline: ${Colors.CYAN_01} none medium;
+    background-color: ${Colors.CYAN_01};
     border-color: ${Colors.WHITE_01};
     transform: translateY(0px);
     padding: 18px 24px 16px 24px;

...
@@ -82,9 +82,13 @@ const baseButtonCss = css<ButtonProps>`
 export const Button = styled.button<ButtonProps>`
   ${baseButtonCss};
 `;
+
 export const ButtonAnchor = styled.a<ButtonProps>`
   ${baseButtonCss};
   text-decoration: none;
+  &:hover {
+    text-decoration: none;
+  }
   &:visited {
     color: ${Colors.WHITE_01};
   }

app/controllers/user/getUserDetailsView.ts
@@ -32,8 +32,9 @@ const getUserDetailsView: ReqHandler<
   const reqHandler = reply.makeRequestHandler(request, reply);
   return reqHandler<UserDetailsViewProps>(UserDetailsView.name, {
     currentUser,
-    repositories: await usersService.getUserRepositories(user),
     user,
+    repositories: await usersService.getUserRepositories(user),
+    sshKeys: await usersService.getUserSSHKeys(user),
   });
 };
 

app/controllers/user/index.ts
@@ -1,7 +1,12 @@
 import { default as getUserDashboardView } from "./getUserDashboardView";
 import { default as getUserDetailsView } from "./getUserDetailsView";
 
+const getUserSSHKeyAddView = async () => {}; // Todo: implement
+const postUserSSHKeyAddAction = async () => {}; // Todo: implement
+
 export const UserController = {
   getUserDashboardView,
   getUserDetailsView,
+  getUserSSHKeyAddView,
+  postUserSSHKeyAddAction,
 };

new file
app/islands/SSHKeyItem.tsx
@@ -0,0 +1,52 @@
+// 1st-party
+import { ReactIsland } from "@ethicdevs/react-monolith";
+// 3rd-party
+import React, { useState } from "react";
+import { UserSSHKey } from "@prisma/client";
+// generated via script[generate:prisma]
+// import styled, { css } from "styled-components";
+// app
+import type { WithThemeSchemeProp } from "../types";
+import { Card, Grid } from "../components";
+import Code from "./Code";
+
+interface SSHKeyItemProps {
+  sshKey: UserSSHKey;
+}
+
+const SSHKeyItem: ReactIsland<SSHKeyItemProps & WithThemeSchemeProp> = ({
+  sshKey: { id, createdAt, name, key },
+  themeScheme,
+}) => {
+  const [show, setShow] = useState<boolean>(false);
+  return (
+    <Card
+      key={id}
+      themeScheme={themeScheme}
+      // style={{ marginTop: idx >= 1 ? 16 : 32 }}
+    >
+      <Grid.Col fluid nowrap gap={16}>
+        <Grid.Row
+          fluid
+          gap={8}
+          justifyContent="space-between"
+          alignItems="center"
+          onClick={() => setShow((prev) => !prev)}
+        >
+          <div>{name}</div>
+          <div>{new Date(createdAt).toDateString()}</div>
+        </Grid.Row>
+        {show && (
+          <Code
+            language="plain"
+            code={key}
+            themeScheme={themeScheme}
+            style={{ whiteSpace: "break-spaces", wordBreak: "break-all" }}
+          />
+        )}
+      </Grid.Col>
+    </Card>
+  );
+};
+
+export default SSHKeyItem;

@@ -43,6 +43,8 @@ export enum AppRoute {
   SYNTAX_HIGHLIGHT_HIGHLIGHT_CODE_ACTION = "syntax_highlight.highlight_code.action",
   USER_DASHBOARD = "user.dashboard",
   USER_DETAILS = "user.details",
+  USER_SSH_KEY_ADD = "user.ssh_key.add",
+  USER_SSH_KEY_ADD_ACTION = "user.ssh_key.add.action",
 }
 
 export const AppRoutePaths: Record<AppRoute, string> = {

...
@@ -92,6 +94,8 @@ export const AppRoutePaths: Record<AppRoute, string> = {
     "/api/syntax/highlight/:outputFormat",
   [AppRoute.USER_DASHBOARD]: "/dashboard",
   [AppRoute.USER_DETAILS]: "/@:username",
+  [AppRoute.USER_SSH_KEY_ADD]: "/@:username/keys/add",
+  [AppRoute.USER_SSH_KEY_ADD_ACTION]: "/@:username/keys/add",
 };
 
 export interface AppRouteParams {

...
@@ -338,6 +342,20 @@ export interface AppRouteParams {
       username: string;
     };
   };
+  [AppRoute.USER_SSH_KEY_ADD]: {
+    params: {
+      username: string;
+    };
+  };
+  [AppRoute.USER_SSH_KEY_ADD_ACTION]: {
+    params: {
+      username: string;
+    };
+    body: {
+      name: string;
+      key: string;
+    };
+  };
 }
 
 export const AppRouteSchemas: Record<AppRoute, undefined | FastifySchema> = {

...
@@ -979,4 +997,41 @@ export const AppRouteSchemas: Record<AppRoute, undefined | FastifySchema> = {
       },
     },
   },
+  [AppRoute.USER_SSH_KEY_ADD]: {
+    params: {
+      type: "object",
+      required: ["username"],
+      additionalProperties: false,
+      properties: {
+        username: {
+          type: "string",
+        },
+      },
+    },
+  },
+  [AppRoute.USER_SSH_KEY_ADD_ACTION]: {
+    params: {
+      type: "object",
+      required: ["username"],
+      additionalProperties: false,
+      properties: {
+        username: {
+          type: "string",
+        },
+      },
+    },
+    body: {
+      type: "object",
+      required: ["name", "key"],
+      additionalProperties: false,
+      properties: {
+        username: {
+          type: "string",
+        },
+        key: {
+          type: "string",
+        },
+      },
+    },
+  },
 };

@@ -128,6 +128,20 @@ const RootAppRouter: AppRouter<AppRouteParams> = () => {
           path={AppRoutePaths[AppRoute.USER_DETAILS]}
           handler={UserController.getUserDetailsView}
         />
+        <Route
+          name={AppRoute.USER_SSH_KEY_ADD}
+          method={"GET"}
+          path={AppRoutePaths[AppRoute.USER_SSH_KEY_ADD]}
+          preHandler={loggedOrLoginRedirect}
+          handler={UserController.getUserSSHKeyAddView}
+        />
+        <Route
+          name={AppRoute.USER_SSH_KEY_ADD_ACTION}
+          method={"POST"}
+          path={AppRoutePaths[AppRoute.USER_SSH_KEY_ADD_ACTION]}
+          preHandler={loggedOrLoginRedirect}
+          handler={UserController.postUserSSHKeyAddAction}
+        />
         {/* --- */}
         <Route
           name={AppRoute.ORGANIZATION_DETAILS}

new file
app/services/user/getUserSSHKeys.ts
@@ -0,0 +1,27 @@
+// 1st-party
+import type { ServiceMethodFactory } from "@ethicdevs/react-monolith";
+// generated via script[generate:prisma]
+import { User, UserSSHKey } from "@prisma/client";
+// app
+import type { UsersServiceDeps } from "./types";
+
+const getUserSSHKeys: ServiceMethodFactory<
+  UsersServiceDeps,
+  [User],
+  Promise<UserSSHKey[]>
+> = ({ request }) => {
+  return async (user) => {
+    const keys = await request.prisma.userSSHKey.findMany({
+      where: {
+        user: { id: user.id },
+      },
+      orderBy: {
+        updatedAt: "desc",
+      },
+    });
+
+    return keys;
+  };
+};
+
+export default getUserSSHKeys;

app/services/user/index.ts
@@ -9,6 +9,7 @@ import { default as makeGetUserByUsername } from "./getUserByUsername";
 import { default as makeGetUserOrganizationMemberships } from "./getUserOrganizationMemberships";
 import { default as makeGetUserOrganizations } from "./getUserOrganizations";
 import { default as makeGetUserRepositories } from "./getUserRepositories";
+import { default as makeGetUserSSHKeys } from "./getUserSSHKeys";
 
 export const makeUsersService = makeService<UsersServiceAPI, UsersServiceDeps>({
   getUserByEmailAddress: makeGetUserByEmailAddress,

...
@@ -17,4 +18,5 @@ export const makeUsersService = makeService<UsersServiceAPI, UsersServiceDeps>({
   getUserOrganizationMemberships: makeGetUserOrganizationMemberships,
   getUserOrganizations: makeGetUserOrganizations,
   getUserRepositories: makeGetUserRepositories,
+  getUserSSHKeys: makeGetUserSSHKeys,
 });

app/services/user/types.ts
@@ -9,6 +9,7 @@ import {
   OrganizationMembership,
   Repository,
   User,
+  UserSSHKey,
 } from "@prisma/client";
 
 export interface UsersServiceAPI extends ServiceApiContract {

...
@@ -26,6 +27,7 @@ export interface UsersServiceAPI extends ServiceApiContract {
     user: User,
     where?: Prisma.RepositoryWhereInput
   ): Promise<(Repository & { parentOrg: Organization })[]>;
+  getUserSSHKeys(user: User): Promise<UserSSHKey[]>;
 }
 
 export interface UsersServiceDeps {

app/views/user/UserDetailsView.tsx
@@ -3,17 +3,32 @@ import type { ReactView } from "@ethicdevs/react-monolith";
 // 3rd-party
 import React from "react";
 // generated via script[generate:prisma]
-import type { Organization, Repository, User } from "@prisma/client";
+import type {
+  Organization,
+  Repository,
+  User,
+  UserSSHKey,
+} from "@prisma/client";
 // app
 import type { CommonProps } from "../../types";
-import { IslandWrapper, Layout, PageWrapper } from "../../components";
+import { buildRouteLink } from "../../utils/shared";
+import { AppRoute } from "../../routes.defs";
+import {
+  ButtonAnchor,
+  Grid,
+  IslandWrapper,
+  Layout,
+  PageWrapper,
+} from "../../components";
 // app islands
 import RepositoriesList from "../../islands/RepositoriesList";
+import SSHKeyItem from "../../islands/SSHKeyItem";
 
 export interface UserDetailsViewProps extends CommonProps {
   user: User;
   currentUser: User | null;
   repositories: (Repository & { parentOrg: Organization })[];
+  sshKeys: UserSSHKey[];
 }
 
 const UserDetailsView: ReactView<UserDetailsViewProps> = ({

...
@@ -21,6 +36,7 @@ const UserDetailsView: ReactView<UserDetailsViewProps> = ({
   currentUser,
   user,
   repositories,
+  sshKeys,
 }) => {
   return (
     <Layout {...commonProps}>

...
@@ -37,6 +53,38 @@ const UserDetailsView: ReactView<UserDetailsViewProps> = ({
             themeScheme={commonProps.themeScheme}
           />
         </IslandWrapper>
+        {currentUser != null && (
+          <>
+            <Grid.Row
+              fluid
+              nowrap
+              gap={24}
+              justifyContent="space-between"
+              alignItems="center"
+            >
+              <h2 style={{ marginBottom: 16 }}>Your SSH key</h2>
+              <ButtonAnchor
+                style={{ fontSize: 16, minHeight: 40, padding: "0 16px" }}
+                href={buildRouteLink(AppRoute.USER_SSH_KEY_ADD, {
+                  username: currentUser.username,
+                })}
+              >
+                Add
+              </ButtonAnchor>
+            </Grid.Row>
+            {sshKeys.map((key, idx) => (
+              <IslandWrapper
+                data-islandid={`${SSHKeyItem.name}$$${idx}`}
+                key={key.id}
+              >
+                <SSHKeyItem
+                  sshKey={key}
+                  themeScheme={commonProps.themeScheme}
+                />
+              </IslandWrapper>
+            ))}
+          </>
+        )}
       </PageWrapper>
     </Layout>
   );

@@ -1,11 +1,14 @@
 #!/bin/sh
-
-# set -u # exit on undefined variable
 SSH_ORIGINAL_COMMAND=${SSH_ORIGINAL_COMMAND}
 USERNAME=$1
 
-# If SSH_ORIGINAL_COMMAND is unset, simply kill term.
-if [ -z ${SSH_ORIGINAL_COMMAND+x} ]; then
+USERNAME=$1
+
+# If SSH_ORIGINAL_COMMAND is unset, or empty, this was not invoked by ssh ForceCommand, kill now.
+# If USERNAME is unset, this was not invoked by ssh ForceCommand, kill now.
+if [ -z ${SSH_ORIGINAL_COMMAND+x} ] 2>/dev/null ||
+  [ -n ${SSH_ORIGINAL_COMMAND} ] 2>/dev/null ||
+  [ -z ${USERNAME+x} ] 2>/dev/null; then
   printf '%s\n' "Hi $USER! You've successfully authenticated, but I do not"
   printf '%s\n' "provide interactive shell access."
   exit 128

...
@@ -20,7 +23,6 @@ GIT_REPO_DIR=$(echo "$RES_JSON" | jq -r '.gitRepositoryDir')
 
 echo "AUTH_MODE: ${AUTH_MODE}" >> /home/git/ssh_commands.log
 echo "GIT_REPO_DIR: ${GIT_REPO_DIR}" >> /home/git/ssh_commands.log
-
 echo "ssh_command_node stdout: ${RES_JSON}" >> /home/git/ssh_commands.log
 echo "ssh_command_node exit code: ${EXIT}" >> /home/git/ssh_commands.log
 

...
@@ -31,19 +33,6 @@ if [ "$EXIT" = "0" ]; then
   echo "result => ${RESULT}" >> /home/git/ssh_commands.log
   exit $?
 else
-  echo "C: 0017x03Forbidden access.\n"
+  echo "Forbidden access.\n"
   exit 1
 fi
-
-# If we should reject:
-
-
-# Assuming bash will only execute the first command in the string
-# TODO See this https://unix.stackexchange.com/a/444949/309572
-# {
-#   $SSH_ORIGINAL_COMMAND
-#   exit $?
-# } || { # catch
-#   echo "Could not complete request."
-#   exit 1
-# }

@@ -4,10 +4,10 @@ services:
     container_name: gitfoss_db
     image: postgres:14
     healthcheck:
-      test: ["CMD-SHELL", "pg_isready -U postgres"]
+      test: ["CMD-SHELL", "pg_isready -U postgres -d gitfoss_local"]
       interval: 5s
-      timeout: 5s
-      retries: 5
+      timeout: 3s
+      retries: 3
     environment:
       - POSTGRES_PASSWORD=postgres
       - POSTGRES_DB=postgres

@@ -19,7 +19,7 @@
     "migrate:reset": "dotenv -e ./.env.local -- prisma migrate reset",
     "bundle:islands": "NODE_ENV=production bundle-islands",
     "build:ts": "NODE_ENV=production tsc",
-    "build": "run-s clean generate build:ts bundle:islands",
+    "build": "run-s clean migrate:deploy generate build:ts bundle:islands",
     "dev": "NODE_ENV=development ts-node-dev --respawn ./app/server.ts",
     "deploy:exoframe": "exoframe deploy --update",
     "deploy:scp": "./scripts/docker-build-scp-deploy.sh",

...
@@ -31,10 +31,10 @@
     "@ethicdevs/fastify-custom-session": "^0.6.0",
     "@ethicdevs/fastify-git-server": "^1.6.1",
     "@ethicdevs/fastify-stream-react-views": "^1.11.3",
-    "@ethicdevs/react-monolith": "^1.9.12",
+    "@ethicdevs/react-monolith": "^1.10.0-dev.05",
     "@fastify/cookie": "6.0.0",
     "@fastify/formbody": "6.0.0",
-    "@prisma/client": "^4.9.0",
+    "@prisma/client": "^5.7.1",
     "color": "^4.2.3",
     "cross-fetch": "^3.1.5",
     "cuid": "^2.1.8",

...
@@ -81,14 +81,14 @@
     "exoframe": "^6.2.0",
     "jest": "^27.5.1",
     "npm-run-all": "^4.1.5",
-    "prisma": "^4.9.0",
+    "prisma": "^5.7.1",
     "ts-jest": "^27.1.5",
     "ts-node-dev": "^2.0.0",
     "tslib": "^2.4.0",
     "typescript": "^4.6.2"
   },
   "resolutions": {
-    "@ethicdevs/fastify-stream-react-views": "^1.10.2"
+    "@ethicdevs/fastify-stream-react-views": "^1.11.3"
   },
   "eslintConfig": {
     "extends": [

@@ -311,23 +311,6 @@
     fastify-plugin "^3.0.1"
     git-side-band-message "^0.0.3"
 
-"@ethicdevs/fastify-stream-react-views@^1.10.2":
-  version "1.10.2"
-  resolved "https://registry.yarnpkg.com/@ethicdevs/fastify-stream-react-views/-/fastify-stream-react-views-1.10.2.tgz#55cf85233abfc39edc261db028d6cc823a60554a"
-  dependencies:
-    "@babel/plugin-transform-modules-umd" "^7.18.0"
-    "@babel/plugin-transform-react-display-name" "^7.18.6"
-    babel-plugin-styled-components "^2.0.7"
-    concat-stream "^2.0.0"
-    dree "^3.3.7"
-    esbuild "^0.14.50"
-    esbuild-node-externals "^1.4.1"
-    escape-string-regexp "^4.0.0"
-    fastify-plugin "^3.0.1"
-    react-ssr-prepass "^1.5.0"
-    terser "^5.14.1"
-    transform-modules-eumd "^1.0.0"
-
 "@ethicdevs/fastify-stream-react-views@^1.11.3":
   version "1.11.3"
   resolved "https://registry.yarnpkg.com/@ethicdevs/fastify-stream-react-views/-/fastify-stream-react-views-1.11.3.tgz#1c1634c2d1ee19bacd342f3cf0fdb3f979a4392a"

...
@@ -346,10 +329,10 @@
     terser "^5.14.1"
     transform-modules-eumd "^1.0.0"
 
-"@ethicdevs/react-monolith@^1.9.12":
-  version "1.9.12"
-  resolved "https://registry.yarnpkg.com/@ethicdevs/react-monolith/-/react-monolith-1.9.12.tgz#4239336cbd000c0bf8e53be2a9dbc6b8b01b7f56"
-  integrity sha512-pdnbTWsuX/0JjKMorVFArFDMclx0YrpQ0kcUDWaKsKjrr6NQDdmEU/8JkWWWf8wW/VBmnHiOOWR6a8lMiHPTWg==
+"@ethicdevs/react-monolith@^1.10.0-dev.05":
+  version "1.10.0-dev.5"
+  resolved "https://registry.yarnpkg.com/@ethicdevs/react-monolith/-/react-monolith-1.10.0-dev.5.tgz#74ae158e92604d993d62778ed22784a008a4699f"
+  integrity sha512-Bi3sQaguS98mwScdg0gS/fJfC+CBUE+7SeL2qHoT+qgFW+7W29p5QNQapD4YlvSrW+whDWf9bmoN3G0MnvW2ug==
   dependencies:
     deepest-merge "^1.0.0"
     react-ssr-prepass "^1.5.0"

...
@@ -762,22 +745,46 @@
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/@panva/asn1.js/-/asn1.js-1.0.0.tgz#dd55ae7b8129e02049f009408b97c61ccf9032f6"
 
-"@prisma/client@^4.9.0":
-  version "4.16.2"
-  resolved "https://registry.yarnpkg.com/@prisma/client/-/client-4.16.2.tgz#3bb9ebd49b35c8236b3d468d0215192267016e2b"
-  integrity sha512-qCoEyxv1ZrQ4bKy39GnylE8Zq31IRmm8bNhNbZx7bF2cU5aiCCnSa93J2imF88MBjn7J9eUQneNxUQVJdl/rPQ==
+"@prisma/client@^5.7.1":
+  version "5.7.1"
+  resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.7.1.tgz#a124afd05663267f7255a639a81d28303684a063"
+  integrity sha512-TUSa4nUcC4nf/e7X3jyO1pEd6XcI/TLRCA0KjkA46RDIpxUaRsBYEOqITwXRW2c0bMFyKcCRXrH4f7h4q9oOlg==
+
+"@prisma/debug@5.7.1":
+  version "5.7.1"
+  resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-5.7.1.tgz#064177066e630beb43492ffa608acc21a118e2ce"
+  integrity sha512-yrVSO/YZOxdeIxcBtZ5BaNqUfPrZkNsAKQIQg36cJKMxj/VYK3Vk5jMKkI+gQLl0KReo1YvX8GWKfV788SELjw==
+
+"@prisma/engines-version@5.7.1-1.0ca5ccbcfa6bdc81c003cf549abe4269f59c41e5":
+  version "5.7.1-1.0ca5ccbcfa6bdc81c003cf549abe4269f59c41e5"
+  resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.7.1-1.0ca5ccbcfa6bdc81c003cf549abe4269f59c41e5.tgz#b7845425313e5395a3a3e64f3e0d04c1f320fa92"
+  integrity sha512-dIR5IQK/ZxEoWRBDOHF87r1Jy+m2ih3Joi4vzJRP+FOj5yxCwS2pS5SBR3TWoVnEK1zxtLI/3N7BjHyGF84fgw==
+
+"@prisma/engines@5.7.1":
+  version "5.7.1"
+  resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.7.1.tgz#631c27daa326bbacd5d7119446e0d3f15c0f274c"
+  integrity sha512-R+Pqbra8tpLP2cvyiUpx+SIKglav3nTCpA+rn6826CThviQ8yvbNG0s8jNpo51vS9FuZO3pOkARqG062vKX7uA==
   dependencies:
-    "@prisma/engines-version" "4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81"
+    "@prisma/debug" "5.7.1"
+    "@prisma/engines-version" "5.7.1-1.0ca5ccbcfa6bdc81c003cf549abe4269f59c41e5"
+    "@prisma/fetch-engine" "5.7.1"
+    "@prisma/get-platform" "5.7.1"
 
-"@prisma/engines-version@4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81":
-  version "4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81"
-  resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81.tgz#d3b5dcf95b6d220e258cbf6ae19b06d30a7e9f14"
-  integrity sha512-q617EUWfRIDTriWADZ4YiWRZXCa/WuhNgLTVd+HqWLffjMSPzyM5uOWoauX91wvQClSKZU4pzI4JJLQ9Kl62Qg==
+"@prisma/fetch-engine@5.7.1":
+  version "5.7.1"
+  resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-5.7.1.tgz#d7baa3493867c6f7cedfc41df477cfd0963059ca"
+  integrity sha512-9ELauIEBkIaEUpMIYPRlh5QELfoC6pyHolHVQgbNxglaINikZ9w9X7r1TIePAcm05pCNp2XPY1ObQIJW5nYfBQ==
+  dependencies:
+    "@prisma/debug" "5.7.1"
+    "@prisma/engines-version" "5.7.1-1.0ca5ccbcfa6bdc81c003cf549abe4269f59c41e5"
+    "@prisma/get-platform" "5.7.1"
 
-"@prisma/engines@4.16.2":
-  version "4.16.2"
-  resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-4.16.2.tgz#5ec8dd672c2173d597e469194916ad4826ce2e5f"
-  integrity sha512-vx1nxVvN4QeT/cepQce68deh/Turxy5Mr+4L4zClFuK1GlxN3+ivxfuv+ej/gvidWn1cE1uAhW7ALLNlYbRUAw==
+"@prisma/get-platform@5.7.1":
+  version "5.7.1"
+  resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-5.7.1.tgz#bc2fe43838c7d47b321aa4728a0f60990d02bc9e"
+  integrity sha512-eDlswr3a1m5z9D/55Iyt/nZqS5UpD+DZ9MooBB3hvrcPhDQrcf9m4Tl7buy4mvAtrubQ626ECtb8c6L/f7rGSQ==
+  dependencies:
+    "@prisma/debug" "5.7.1"
 
 "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
   version "1.1.2"

...
@@ -4220,12 +4227,12 @@ pretty-format@^28.0.0, pretty-format@^28.1.3:
     ansi-styles "^5.0.0"
     react-is "^18.0.0"
 
-prisma@^4.9.0:
-  version "4.16.2"
-  resolved "https://registry.yarnpkg.com/prisma/-/prisma-4.16.2.tgz#469e0a0991c6ae5bcde289401726bb012253339e"
-  integrity sha512-SYCsBvDf0/7XSJyf2cHTLjLeTLVXYfqp7pG5eEVafFLeT0u/hLFz/9W196nDRGUOo1JfPatAEb+uEnTQImQC1g==
+prisma@^5.7.1:
+  version "5.7.1"
+  resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.7.1.tgz#af60ed90531adc0ab8a683c9b1fc86d841c39864"
+  integrity sha512-ekho7ziH0WEJvC4AxuJz+ewRTMskrebPcrKuBwcNzVDniYxx+dXOGcorNeIb9VEMO5vrKzwNYvhD271Ui2jnNw==
   dependencies:
-    "@prisma/engines" "4.16.2"
+    "@prisma/engines" "5.7.1"
 
 prismjs@^1.29.0:
   version "1.29.0"

GitFOSS - v0.2.0 (#48b426e) - MIT License