diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..1a91fcf --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,9 @@ +### 2025暑培-网站作业提交 +#### 基本信息 +- **姓名**: +- **班级**: +- **学号**: + +#### 提交说明 + +- [ ] 已阅读并理解本次作业要求 diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100644 index 0000000..d517e37 --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1,14 @@ +# Github 和 Docker Hub 相关网页设置 + +### 前端(Github Pages) + +在你复刻的仓库中,进入设置标签页(https://github.com/[username]/web-workshop/settings),点击左边栏的 Pages,在 Build and deployment 下方的 Source,选择 Github Actions。意思是通过自定义的 action 来部署静态 Github Pages(与之相对的是根据仓库中的 markdown 文件自动部署) + +### 后端(Docker) + +1. 注册 Dockers Hub 账号([Signup | Docker](https://app.docker.com/signup)),建议使用 Github 注册。如果使用其他方式注册,请将用户名与 Github 保持一致(大小写不敏感) +2. 在 Docker Hub 设置界面的 Personal access tokens(个人访问 Token)([Personal access tokens | Docker](https://app.docker.com/settings/personal-access-tokens)),新增一个 token(至少要有写权限)并复制下来 +3. 在 Github 上复刻仓库的设置页,点击左边栏的 Secrets and variables -> Actions,添加一个 Secret(即密钥,加密防护)和两个 Variables(即变量,明文显示)如下: + - [Secret] `DOCKERHUB_TOKEN`,值为之前复制的个人访问 Token + - [Variable] `DOCKERHUB_USERNAME`,值为你的 Docker Hub 账号名 + - [Variable] `DOCKER_TAG`,值为你的 Docker 容器标识名,形如`:latest`,其中`repo-name`任意,不需要与仓库同名 diff --git a/.github/workflows/backend.yml b/.github/workflows/backend.yml new file mode 100644 index 0000000..91a74e9 --- /dev/null +++ b/.github/workflows/backend.yml @@ -0,0 +1,72 @@ +name: backend + +on: + push: + branches: [ main ] + +permissions: + packages: write + contents: read + id-token: write + +defaults: + run: + working-directory: backend + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: yarn + cache-dependency-path: ./backend/yarn.lock + + - name: Install dependencies + run: | + yarn install --frozen-lockfile + + - name: Check grammar + run: | + yarn typecheck + + build: + needs: test + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ vars.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Downcase GitHub username + run: echo "USERNAME_LC=${USERNAME@L}" >> $GITHUB_ENV + env: + USERNAME: ${{ github.repository_owner }} + + - name: Build and push docker image + uses: docker/build-push-action@v6 + with: + context: ./backend + push: true + tags: | + ghcr.io/${{ env.USERNAME_LC }}/${{ vars.DOCKER_TAG }} + ${{ vars.DOCKERHUB_USERNAME }}/${{ vars.DOCKER_TAG }} diff --git a/.github/workflows/electron.yml b/.github/workflows/electron.yml new file mode 100644 index 0000000..da0b11f --- /dev/null +++ b/.github/workflows/electron.yml @@ -0,0 +1,83 @@ +name: electron + +on: + push: + tags: + - v* + +permissions: + contents: write + +defaults: + run: + working-directory: frontend + +jobs: + build: + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + include: + - os: ubuntu-latest + output-file: | + ./frontend/electron/*.AppImage + ./frontend/electron/*.deb + ./frontend/electron/*.rpm + ./frontend/electron/*.tar.gz + - os: windows-latest + output-file: | + ./frontend/electron/*.exe + - os: macos-latest + output-file: | + ./frontend/electron/*.dmg + ./frontend/electron/*.zip + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: yarn + cache-dependency-path: ./frontend/yarn.lock + + - name: Install dependencies + run: | + yarn install --frozen-lockfile + yarn add electron electron-builder --dev + + - name: Build + run: | + yarn build + yarn electron:build + + - name: Upload executables for publish + uses: actions/upload-artifact@v4 + with: + name: my-artifact-${{ matrix.os }} + path: ${{ matrix.output-file }} + + release: + runs-on: ubuntu-latest + needs: build + + steps: + - name: Download executables + uses: actions/download-artifact@v4 + with: + pattern: my-artifact-* + merge-multiple: true + path: dist + + - name: Deploy to GitHub Releases + uses: softprops/action-gh-release@v2 + with: + files: ./dist/* + name: Release ${{ github.ref_name }} + generate_release_notes: true + prerelease: true diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml new file mode 100644 index 0000000..ae54fbf --- /dev/null +++ b/.github/workflows/frontend.yml @@ -0,0 +1,74 @@ +# Simple workflow for deploying static content to GitHub Pages +name: frontend + +on: + # Runs on pushes targeting the default branch + push: + branches: ["main"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +defaults: + run: + working-directory: frontend + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: yarn + cache-dependency-path: ./frontend/yarn.lock + + - name: Install dependencies + run: | + yarn install --frozen-lockfile + + - name: Check grammar + run: | + yarn typecheck + yarn lint + + - name: Build + run: | + yarn build + + - name: Setup Pages + uses: actions/configure-pages@v5 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: "./frontend/build" + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.mergify.yml b/.mergify.yml new file mode 100644 index 0000000..e7befa9 --- /dev/null +++ b/.mergify.yml @@ -0,0 +1,9 @@ +pull_request_rules: + - name: 🏷️ Label homework + description: Label a homework with 'homework' label by detecting keyword + conditions: + - body~=作业提交 + actions: + label: + add: + - homework diff --git a/README.md b/README.md index 9dc0172..1daaaac 100644 --- a/README.md +++ b/README.md @@ -131,3 +131,43 @@ git clone <先前复制的仓库URI> ```bash git push --all ``` + +### 作业提交 +每一讲的作业提交采用如下流程: +- 本地修改对应分支 +- 提交修改到对应分支 +- 向本仓库对应分支提交PR +- 关联PR到对应Issue +- 查看作业批改结果 +##### 本地修改对应分支 +fork本仓库所有分支后,根据[Issue](https://github.com/eesast/web-workshop/issues)对应讲作业要求,在本地切换到对应分支进行修改 +``` +git checkout "01-HTML&CSS" +``` +##### 提交修改到对应分支 +完成修改后,将改动提交到本地并推送到云端 Fork 仓库 +``` +git push origin "01-HTML&CSS" +``` +##### 向本仓库对应分支提交PR +打开在 GitHub 上 Fork 的仓库页面后,切换到刚刚推送的 对应分支(如 lesson1) + +点击 "Compare & pull request" 按钮,并在 PR 创建页面填写相关信息 +##### 关联PR到对应Issue +在PR模板填写界面,需手动关联PR到对应Issue + +你可以在PR正文中手动关联对应Issue,方法是添加#ISSUE-NUMBER到正文后。例如,需要链接的Issue对应的id是4,则添加一行#4 + +你也可以在PR编辑界面点击右上方的'Reference',选择需要链接的PR,最终效果与上述方法相同 +image + +[示例PR](https://github.com/eesast/web-workshop/pull/12) + +关联完成后,提交PR,则作业提交完毕 +##### 查看作业批改结果 +作业由讲师批改后,对应 PR 会被打上标签: +- accepted ✅:作业通过,PR 会被关闭。 +- require revision 🔄:需要修改,PR 保持 open 状态。 + + +若需修改,按 PR 下方的评论提示进行更改,然后重复 步骤 2 → 步骤 3 提交更新。 diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..fb68e0c --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1 @@ +upload diff --git a/backend/.local.env.template b/backend/.local.env.template new file mode 100644 index 0000000..3c70b36 --- /dev/null +++ b/backend/.local.env.template @@ -0,0 +1,16 @@ +HASURA_GRAPHQL_ENDPOINT=
:/v1/graphql +HASURA_GRAPHQL_ADMIN_SECRET= + +JWT_SECRET= + +EMAIL_HOST=smtp.163.com +EMAIL_PORT=465 +EMAIL_SECURE=true +EMAIL_ADDRESS= +EMAIL_PASSWORD= + +(以下备注请在生产环境中删除) +注:不同邮箱提供商的配置方法不同,请参考对应的文档或邮箱设置; +- 部分邮箱需要手动开启 SMTP 服务; +- 部分邮箱的密码(包括清华邮箱)填的是设备授权码,而不是账号密码; +- 部分邮箱需要 OAuth2 或其他身份验证,请参考 https://nodemailer.com/smtp/oauth2/ diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..3ae5208 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,30 @@ +# Builder stage +FROM node:20 AS builder +WORKDIR /home/node/app + +# Install Dependencies +COPY package.json yarn.lock ./ +RUN yarn install --frozen-lockfile --no-cache + +# Copy source code +COPY . . + +# Build +RUN yarn build + + +# Runner stage +FROM node:20-alpine AS runner +WORKDIR /home/node/app +ENV NODE_ENV=production + +# Install Production Dependencies +COPY package.json yarn.lock ./ +RUN yarn install --frozen-lockfile --no-cache --production + +# Copy build files +COPY --from=builder /home/node/app/build ./build + +# Expose port and run +EXPOSE 8888 +CMD yarn serve diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 0000000..6b6f66f --- /dev/null +++ b/backend/package.json @@ -0,0 +1,29 @@ +{ + "dependencies": { + "dotenv": "16.4.5", + "express": "4.19.2", + "graphql": "16.9.0", + "graphql-request": "6.1.0", + "jsonwebtoken": "9.0.2", + "morgan": "1.10.0", + "multer": "1.4.5-lts.1", + "nodemailer": "6.9.14" + }, + "devDependencies": { + "@types/express": "4.17.21", + "@types/jsonwebtoken": "9.0.6", + "@types/morgan": "1.9.9", + "@types/multer": "1.4.11", + "@types/node": "22.1.0", + "@types/nodemailer": "6.4.15", + "nodemon": "3.1.4", + "ts-node": "10.9.2", + "typescript": "5.5.4" + }, + "scripts": { + "start": "nodemon src/index.ts", + "build": "tsc", + "serve": "node build/index.js", + "typecheck": "tsc --noEmit" + } +} diff --git a/backend/src/authenticate.ts b/backend/src/authenticate.ts new file mode 100644 index 0000000..676643c --- /dev/null +++ b/backend/src/authenticate.ts @@ -0,0 +1,19 @@ +import { Request, Response, NextFunction } from "express"; +import jwt from "jsonwebtoken"; + +const authenticate: (req: Request, res: Response, next: NextFunction) => Response | void = + (req, res, next) => { + const authHeader = req.get("Authorization"); + if (!authHeader) { + return res.status(401).send("401 Unauthorized: Missing Token"); + } + const token = authHeader.substring(7); + return jwt.verify(token, process.env.JWT_SECRET!, async (err, decoded) => { + if (err || !decoded) { + return res.status(401).send("401 Unauthorized: Token expired or invalid"); + } + return next(); + }); + }; + +export default authenticate; diff --git a/backend/src/email.ts b/backend/src/email.ts new file mode 100644 index 0000000..4c19628 --- /dev/null +++ b/backend/src/email.ts @@ -0,0 +1,47 @@ +import express from "express"; +import nodemailer from "nodemailer"; + +const router = express.Router(); + +const sendEmail = async (to: string, subject: string, text: string) => { + const transporter = nodemailer.createTransport({ + host: process.env.EMAIL_HOST!, + port: Number(process.env.EMAIL_PORT!), + secure: process.env.EMAIL_SECURE! === "true", + auth: { + user: process.env.EMAIL_ADDRESS!, + pass: process.env.EMAIL_PASSWORD!, + }, + tls: { rejectUnauthorized: false }, + }); + try { + await transporter.verify(); + return await transporter.sendMail({ from: process.env.EMAIL_ADDRESS!, to, subject, text }); + } catch (err) { + throw err; + } +} + +router.post("/contact-us", async (req, res) => { + const { email, name, message } = req.body; + if (!email || !name || !message) { + return res.status(422).send("422 Unprocessable Entity: Missing email, name, or message"); + } + try { + const result = await sendEmail( + process.env.EMAIL_ADDRESS!, + "Web Workshop Contact Us Form", + `Message from ${name} <${email}>:\n\n${message}`, + ); + if (result.accepted.length > 0) { + return res.send("Message sent successfully"); + } else { + throw new Error("Failed to send message for unknown reason"); + } + } catch (err) { + console.error(err); + return res.sendStatus(500); + } +}); + +export default router; diff --git a/backend/src/file.ts b/backend/src/file.ts new file mode 100644 index 0000000..7e77582 --- /dev/null +++ b/backend/src/file.ts @@ -0,0 +1,77 @@ +import express from "express"; +import multer from "multer"; +import fs from "fs"; +import path from "path"; +import authenticate from "./authenticate"; + +const router = express.Router(); + +const baseDir = process.env.FILE_DIR || path.resolve(process.cwd(), "upload"); + +const limits = { + parts: 2, // 1 file and 0 fields + fileSize: 10 * 1024 * 1024, // 10 MB +}; +const storage = multer.diskStorage({ + destination: (req, file, cb) => { + try { + const room = req.params.room; + const dir = path.resolve(baseDir, room); + fs.mkdirSync(dir, { recursive: true }); + return cb(null, dir); + } catch (err) { + return cb(err as Error, ""); + } + }, + filename: (req, file, cb) => { + return cb(null, file.originalname); + } +}) +const upload = multer({ storage, limits }); + +router.post("/upload/:room", authenticate, upload.single("file"), (req, res) => { + const file = req.file; + if (!file) { + return res.status(422).send("422 Unprocessable Entity: Missing file"); + } + return res.send("File uploaded successfully"); +}); + +router.get("/list", authenticate, (req, res) => { + const room = req.query.room; + if (!room) { + return res.status(422).send("422 Unprocessable Entity: Missing room"); + } + const dir = path.resolve(baseDir, room as string); + try { + let fileList: string[] = []; + if (fs.existsSync(dir)) { + fileList = fs.readdirSync(dir); + } + return res.json({ fileList }); + } catch (err) { + console.error(err); + return res.sendStatus(500); + } +}); + +router.get("/download", authenticate, (req, res) => { + const room = req.query.room; + const filename = req.query.filename; + if (!room || !filename) { + return res.status(422).send("422 Unprocessable Entity: Missing room or filename"); + } + const dir = path.resolve(baseDir, room as string, filename as string); + try { + if (fs.existsSync(dir)) { + return res.download(dir); + } else { + return res.status(404).send("404 Not Found: File does not exist"); + } + } catch (err) { + console.error(err); + return res.sendStatus(500); + } +}); + +export default router; diff --git a/backend/src/graphql.ts b/backend/src/graphql.ts new file mode 100644 index 0000000..8e65cb6 --- /dev/null +++ b/backend/src/graphql.ts @@ -0,0 +1,1664 @@ +import { GraphQLClient, RequestOptions } from 'graphql-request'; +import { gql } from 'graphql-request'; +export type Maybe = T | null; +export type InputMaybe = Maybe; +export type Exact = { [K in keyof T]: T[K] }; +export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; +export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; +export type MakeEmpty = { [_ in K]?: never }; +export type Incremental = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never }; +type GraphQLClientRequestHeaders = RequestOptions['requestHeaders']; +/** All built-in and custom scalars, mapped to their actual values */ +export type Scalars = { + ID: { input: string; output: string; } + String: { input: string; output: string; } + Boolean: { input: boolean; output: boolean; } + Int: { input: number; output: number; } + Float: { input: number; output: number; } + timestamp: { input: any; output: any; } + uuid: { input: any; output: any; } +}; + +/** Boolean expression to compare columns of type "Int". All fields are combined with logical 'AND'. */ +export type Int_Comparison_Exp = { + _eq?: InputMaybe; + _gt?: InputMaybe; + _gte?: InputMaybe; + _in?: InputMaybe>; + _is_null?: InputMaybe; + _lt?: InputMaybe; + _lte?: InputMaybe; + _neq?: InputMaybe; + _nin?: InputMaybe>; +}; + +/** Boolean expression to compare columns of type "String". All fields are combined with logical 'AND'. */ +export type String_Comparison_Exp = { + _eq?: InputMaybe; + _gt?: InputMaybe; + _gte?: InputMaybe; + /** does the column match the given case-insensitive pattern */ + _ilike?: InputMaybe; + _in?: InputMaybe>; + /** does the column match the given POSIX regular expression, case insensitive */ + _iregex?: InputMaybe; + _is_null?: InputMaybe; + /** does the column match the given pattern */ + _like?: InputMaybe; + _lt?: InputMaybe; + _lte?: InputMaybe; + _neq?: InputMaybe; + /** does the column NOT match the given case-insensitive pattern */ + _nilike?: InputMaybe; + _nin?: InputMaybe>; + /** does the column NOT match the given POSIX regular expression, case insensitive */ + _niregex?: InputMaybe; + /** does the column NOT match the given pattern */ + _nlike?: InputMaybe; + /** does the column NOT match the given POSIX regular expression, case sensitive */ + _nregex?: InputMaybe; + /** does the column NOT match the given SQL regular expression */ + _nsimilar?: InputMaybe; + /** does the column match the given POSIX regular expression, case sensitive */ + _regex?: InputMaybe; + /** does the column match the given SQL regular expression */ + _similar?: InputMaybe; +}; + +/** ordering argument of a cursor */ +export enum Cursor_Ordering { + /** ascending ordering of the cursor */ + Asc = 'ASC', + /** descending ordering of the cursor */ + Desc = 'DESC' +} + +/** columns and relationships of "message" */ +export type Message = { + __typename?: 'message'; + content: Scalars['String']['output']; + created_at: Scalars['timestamp']['output']; + /** An object relationship */ + room: Room; + room_uuid: Scalars['uuid']['output']; + /** An object relationship */ + user: User; + user_uuid: Scalars['uuid']['output']; + uuid: Scalars['uuid']['output']; +}; + +/** aggregated selection of "message" */ +export type Message_Aggregate = { + __typename?: 'message_aggregate'; + aggregate?: Maybe; + nodes: Array; +}; + +export type Message_Aggregate_Bool_Exp = { + count?: InputMaybe; +}; + +export type Message_Aggregate_Bool_Exp_Count = { + arguments?: InputMaybe>; + distinct?: InputMaybe; + filter?: InputMaybe; + predicate: Int_Comparison_Exp; +}; + +/** aggregate fields of "message" */ +export type Message_Aggregate_Fields = { + __typename?: 'message_aggregate_fields'; + count: Scalars['Int']['output']; + max?: Maybe; + min?: Maybe; +}; + + +/** aggregate fields of "message" */ +export type Message_Aggregate_FieldsCountArgs = { + columns?: InputMaybe>; + distinct?: InputMaybe; +}; + +/** order by aggregate values of table "message" */ +export type Message_Aggregate_Order_By = { + count?: InputMaybe; + max?: InputMaybe; + min?: InputMaybe; +}; + +/** input type for inserting array relation for remote table "message" */ +export type Message_Arr_Rel_Insert_Input = { + data: Array; + /** upsert condition */ + on_conflict?: InputMaybe; +}; + +/** Boolean expression to filter rows from the table "message". All fields are combined with a logical 'AND'. */ +export type Message_Bool_Exp = { + _and?: InputMaybe>; + _not?: InputMaybe; + _or?: InputMaybe>; + content?: InputMaybe; + created_at?: InputMaybe; + room?: InputMaybe; + room_uuid?: InputMaybe; + user?: InputMaybe; + user_uuid?: InputMaybe; + uuid?: InputMaybe; +}; + +/** unique or primary key constraints on table "message" */ +export enum Message_Constraint { + /** unique or primary key constraint on columns "uuid" */ + MessagePkey = 'message_pkey' +} + +/** input type for inserting data into table "message" */ +export type Message_Insert_Input = { + content?: InputMaybe; + created_at?: InputMaybe; + room?: InputMaybe; + room_uuid?: InputMaybe; + user?: InputMaybe; + user_uuid?: InputMaybe; + uuid?: InputMaybe; +}; + +/** aggregate max on columns */ +export type Message_Max_Fields = { + __typename?: 'message_max_fields'; + content?: Maybe; + created_at?: Maybe; + room_uuid?: Maybe; + user_uuid?: Maybe; + uuid?: Maybe; +}; + +/** order by max() on columns of table "message" */ +export type Message_Max_Order_By = { + content?: InputMaybe; + created_at?: InputMaybe; + room_uuid?: InputMaybe; + user_uuid?: InputMaybe; + uuid?: InputMaybe; +}; + +/** aggregate min on columns */ +export type Message_Min_Fields = { + __typename?: 'message_min_fields'; + content?: Maybe; + created_at?: Maybe; + room_uuid?: Maybe; + user_uuid?: Maybe; + uuid?: Maybe; +}; + +/** order by min() on columns of table "message" */ +export type Message_Min_Order_By = { + content?: InputMaybe; + created_at?: InputMaybe; + room_uuid?: InputMaybe; + user_uuid?: InputMaybe; + uuid?: InputMaybe; +}; + +/** response of any mutation on the table "message" */ +export type Message_Mutation_Response = { + __typename?: 'message_mutation_response'; + /** number of rows affected by the mutation */ + affected_rows: Scalars['Int']['output']; + /** data from the rows affected by the mutation */ + returning: Array; +}; + +/** on_conflict condition type for table "message" */ +export type Message_On_Conflict = { + constraint: Message_Constraint; + update_columns?: Array; + where?: InputMaybe; +}; + +/** Ordering options when selecting data from "message". */ +export type Message_Order_By = { + content?: InputMaybe; + created_at?: InputMaybe; + room?: InputMaybe; + room_uuid?: InputMaybe; + user?: InputMaybe; + user_uuid?: InputMaybe; + uuid?: InputMaybe; +}; + +/** primary key columns input for table: message */ +export type Message_Pk_Columns_Input = { + uuid: Scalars['uuid']['input']; +}; + +/** select columns of table "message" */ +export enum Message_Select_Column { + /** column name */ + Content = 'content', + /** column name */ + CreatedAt = 'created_at', + /** column name */ + RoomUuid = 'room_uuid', + /** column name */ + UserUuid = 'user_uuid', + /** column name */ + Uuid = 'uuid' +} + +/** input type for updating data in table "message" */ +export type Message_Set_Input = { + content?: InputMaybe; + created_at?: InputMaybe; + room_uuid?: InputMaybe; + user_uuid?: InputMaybe; + uuid?: InputMaybe; +}; + +/** Streaming cursor of the table "message" */ +export type Message_Stream_Cursor_Input = { + /** Stream column input with initial value */ + initial_value: Message_Stream_Cursor_Value_Input; + /** cursor ordering */ + ordering?: InputMaybe; +}; + +/** Initial value of the column from where the streaming should start */ +export type Message_Stream_Cursor_Value_Input = { + content?: InputMaybe; + created_at?: InputMaybe; + room_uuid?: InputMaybe; + user_uuid?: InputMaybe; + uuid?: InputMaybe; +}; + +/** update columns of table "message" */ +export enum Message_Update_Column { + /** column name */ + Content = 'content', + /** column name */ + CreatedAt = 'created_at', + /** column name */ + RoomUuid = 'room_uuid', + /** column name */ + UserUuid = 'user_uuid', + /** column name */ + Uuid = 'uuid' +} + +export type Message_Updates = { + /** sets the columns of the filtered rows to the given values */ + _set?: InputMaybe; + /** filter the rows which have to be updated */ + where: Message_Bool_Exp; +}; + +/** mutation root */ +export type Mutation_Root = { + __typename?: 'mutation_root'; + /** delete data from the table: "message" */ + delete_message?: Maybe; + /** delete single row from the table: "message" */ + delete_message_by_pk?: Maybe; + /** delete data from the table: "room" */ + delete_room?: Maybe; + /** delete single row from the table: "room" */ + delete_room_by_pk?: Maybe; + /** delete data from the table: "user" */ + delete_user?: Maybe; + /** delete single row from the table: "user" */ + delete_user_by_pk?: Maybe; + /** delete data from the table: "user_room" */ + delete_user_room?: Maybe; + /** delete single row from the table: "user_room" */ + delete_user_room_by_pk?: Maybe; + /** insert data into the table: "message" */ + insert_message?: Maybe; + /** insert a single row into the table: "message" */ + insert_message_one?: Maybe; + /** insert data into the table: "room" */ + insert_room?: Maybe; + /** insert a single row into the table: "room" */ + insert_room_one?: Maybe; + /** insert data into the table: "user" */ + insert_user?: Maybe; + /** insert a single row into the table: "user" */ + insert_user_one?: Maybe; + /** insert data into the table: "user_room" */ + insert_user_room?: Maybe; + /** insert a single row into the table: "user_room" */ + insert_user_room_one?: Maybe; + /** update data of the table: "message" */ + update_message?: Maybe; + /** update single row of the table: "message" */ + update_message_by_pk?: Maybe; + /** update multiples rows of table: "message" */ + update_message_many?: Maybe>>; + /** update data of the table: "room" */ + update_room?: Maybe; + /** update single row of the table: "room" */ + update_room_by_pk?: Maybe; + /** update multiples rows of table: "room" */ + update_room_many?: Maybe>>; + /** update data of the table: "user" */ + update_user?: Maybe; + /** update single row of the table: "user" */ + update_user_by_pk?: Maybe; + /** update multiples rows of table: "user" */ + update_user_many?: Maybe>>; + /** update data of the table: "user_room" */ + update_user_room?: Maybe; + /** update single row of the table: "user_room" */ + update_user_room_by_pk?: Maybe; + /** update multiples rows of table: "user_room" */ + update_user_room_many?: Maybe>>; +}; + + +/** mutation root */ +export type Mutation_RootDelete_MessageArgs = { + where: Message_Bool_Exp; +}; + + +/** mutation root */ +export type Mutation_RootDelete_Message_By_PkArgs = { + uuid: Scalars['uuid']['input']; +}; + + +/** mutation root */ +export type Mutation_RootDelete_RoomArgs = { + where: Room_Bool_Exp; +}; + + +/** mutation root */ +export type Mutation_RootDelete_Room_By_PkArgs = { + uuid: Scalars['uuid']['input']; +}; + + +/** mutation root */ +export type Mutation_RootDelete_UserArgs = { + where: User_Bool_Exp; +}; + + +/** mutation root */ +export type Mutation_RootDelete_User_By_PkArgs = { + uuid: Scalars['uuid']['input']; +}; + + +/** mutation root */ +export type Mutation_RootDelete_User_RoomArgs = { + where: User_Room_Bool_Exp; +}; + + +/** mutation root */ +export type Mutation_RootDelete_User_Room_By_PkArgs = { + room_uuid: Scalars['uuid']['input']; + user_uuid: Scalars['uuid']['input']; +}; + + +/** mutation root */ +export type Mutation_RootInsert_MessageArgs = { + objects: Array; + on_conflict?: InputMaybe; +}; + + +/** mutation root */ +export type Mutation_RootInsert_Message_OneArgs = { + object: Message_Insert_Input; + on_conflict?: InputMaybe; +}; + + +/** mutation root */ +export type Mutation_RootInsert_RoomArgs = { + objects: Array; + on_conflict?: InputMaybe; +}; + + +/** mutation root */ +export type Mutation_RootInsert_Room_OneArgs = { + object: Room_Insert_Input; + on_conflict?: InputMaybe; +}; + + +/** mutation root */ +export type Mutation_RootInsert_UserArgs = { + objects: Array; + on_conflict?: InputMaybe; +}; + + +/** mutation root */ +export type Mutation_RootInsert_User_OneArgs = { + object: User_Insert_Input; + on_conflict?: InputMaybe; +}; + + +/** mutation root */ +export type Mutation_RootInsert_User_RoomArgs = { + objects: Array; + on_conflict?: InputMaybe; +}; + + +/** mutation root */ +export type Mutation_RootInsert_User_Room_OneArgs = { + object: User_Room_Insert_Input; + on_conflict?: InputMaybe; +}; + + +/** mutation root */ +export type Mutation_RootUpdate_MessageArgs = { + _set?: InputMaybe; + where: Message_Bool_Exp; +}; + + +/** mutation root */ +export type Mutation_RootUpdate_Message_By_PkArgs = { + _set?: InputMaybe; + pk_columns: Message_Pk_Columns_Input; +}; + + +/** mutation root */ +export type Mutation_RootUpdate_Message_ManyArgs = { + updates: Array; +}; + + +/** mutation root */ +export type Mutation_RootUpdate_RoomArgs = { + _set?: InputMaybe; + where: Room_Bool_Exp; +}; + + +/** mutation root */ +export type Mutation_RootUpdate_Room_By_PkArgs = { + _set?: InputMaybe; + pk_columns: Room_Pk_Columns_Input; +}; + + +/** mutation root */ +export type Mutation_RootUpdate_Room_ManyArgs = { + updates: Array; +}; + + +/** mutation root */ +export type Mutation_RootUpdate_UserArgs = { + _set?: InputMaybe; + where: User_Bool_Exp; +}; + + +/** mutation root */ +export type Mutation_RootUpdate_User_By_PkArgs = { + _set?: InputMaybe; + pk_columns: User_Pk_Columns_Input; +}; + + +/** mutation root */ +export type Mutation_RootUpdate_User_ManyArgs = { + updates: Array; +}; + + +/** mutation root */ +export type Mutation_RootUpdate_User_RoomArgs = { + _set?: InputMaybe; + where: User_Room_Bool_Exp; +}; + + +/** mutation root */ +export type Mutation_RootUpdate_User_Room_By_PkArgs = { + _set?: InputMaybe; + pk_columns: User_Room_Pk_Columns_Input; +}; + + +/** mutation root */ +export type Mutation_RootUpdate_User_Room_ManyArgs = { + updates: Array; +}; + +/** column ordering options */ +export enum Order_By { + /** in ascending order, nulls last */ + Asc = 'asc', + /** in ascending order, nulls first */ + AscNullsFirst = 'asc_nulls_first', + /** in ascending order, nulls last */ + AscNullsLast = 'asc_nulls_last', + /** in descending order, nulls first */ + Desc = 'desc', + /** in descending order, nulls first */ + DescNullsFirst = 'desc_nulls_first', + /** in descending order, nulls last */ + DescNullsLast = 'desc_nulls_last' +} + +export type Query_Root = { + __typename?: 'query_root'; + /** fetch data from the table: "message" */ + message: Array; + /** fetch aggregated fields from the table: "message" */ + message_aggregate: Message_Aggregate; + /** fetch data from the table: "message" using primary key columns */ + message_by_pk?: Maybe; + /** fetch data from the table: "room" */ + room: Array; + /** fetch aggregated fields from the table: "room" */ + room_aggregate: Room_Aggregate; + /** fetch data from the table: "room" using primary key columns */ + room_by_pk?: Maybe; + /** fetch data from the table: "user" */ + user: Array; + /** fetch aggregated fields from the table: "user" */ + user_aggregate: User_Aggregate; + /** fetch data from the table: "user" using primary key columns */ + user_by_pk?: Maybe; + /** fetch data from the table: "user_room" */ + user_room: Array; + /** fetch aggregated fields from the table: "user_room" */ + user_room_aggregate: User_Room_Aggregate; + /** fetch data from the table: "user_room" using primary key columns */ + user_room_by_pk?: Maybe; +}; + + +export type Query_RootMessageArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +export type Query_RootMessage_AggregateArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +export type Query_RootMessage_By_PkArgs = { + uuid: Scalars['uuid']['input']; +}; + + +export type Query_RootRoomArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +export type Query_RootRoom_AggregateArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +export type Query_RootRoom_By_PkArgs = { + uuid: Scalars['uuid']['input']; +}; + + +export type Query_RootUserArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +export type Query_RootUser_AggregateArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +export type Query_RootUser_By_PkArgs = { + uuid: Scalars['uuid']['input']; +}; + + +export type Query_RootUser_RoomArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +export type Query_RootUser_Room_AggregateArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +export type Query_RootUser_Room_By_PkArgs = { + room_uuid: Scalars['uuid']['input']; + user_uuid: Scalars['uuid']['input']; +}; + +/** columns and relationships of "room" */ +export type Room = { + __typename?: 'room'; + created_at: Scalars['timestamp']['output']; + intro: Scalars['String']['output']; + invite_code: Scalars['String']['output']; + /** An array relationship */ + messages: Array; + /** An aggregate relationship */ + messages_aggregate: Message_Aggregate; + name: Scalars['String']['output']; + /** An array relationship */ + user_rooms: Array; + /** An aggregate relationship */ + user_rooms_aggregate: User_Room_Aggregate; + uuid: Scalars['uuid']['output']; +}; + + +/** columns and relationships of "room" */ +export type RoomMessagesArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +/** columns and relationships of "room" */ +export type RoomMessages_AggregateArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +/** columns and relationships of "room" */ +export type RoomUser_RoomsArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +/** columns and relationships of "room" */ +export type RoomUser_Rooms_AggregateArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + +/** aggregated selection of "room" */ +export type Room_Aggregate = { + __typename?: 'room_aggregate'; + aggregate?: Maybe; + nodes: Array; +}; + +/** aggregate fields of "room" */ +export type Room_Aggregate_Fields = { + __typename?: 'room_aggregate_fields'; + count: Scalars['Int']['output']; + max?: Maybe; + min?: Maybe; +}; + + +/** aggregate fields of "room" */ +export type Room_Aggregate_FieldsCountArgs = { + columns?: InputMaybe>; + distinct?: InputMaybe; +}; + +/** Boolean expression to filter rows from the table "room". All fields are combined with a logical 'AND'. */ +export type Room_Bool_Exp = { + _and?: InputMaybe>; + _not?: InputMaybe; + _or?: InputMaybe>; + created_at?: InputMaybe; + intro?: InputMaybe; + invite_code?: InputMaybe; + messages?: InputMaybe; + messages_aggregate?: InputMaybe; + name?: InputMaybe; + user_rooms?: InputMaybe; + user_rooms_aggregate?: InputMaybe; + uuid?: InputMaybe; +}; + +/** unique or primary key constraints on table "room" */ +export enum Room_Constraint { + /** unique or primary key constraint on columns "invite_code" */ + RoomInviteCodeKey = 'room_invite_code_key', + /** unique or primary key constraint on columns "name" */ + RoomNameKey = 'room_name_key', + /** unique or primary key constraint on columns "uuid" */ + RoomPkey = 'room_pkey' +} + +/** input type for inserting data into table "room" */ +export type Room_Insert_Input = { + created_at?: InputMaybe; + intro?: InputMaybe; + invite_code?: InputMaybe; + messages?: InputMaybe; + name?: InputMaybe; + user_rooms?: InputMaybe; + uuid?: InputMaybe; +}; + +/** aggregate max on columns */ +export type Room_Max_Fields = { + __typename?: 'room_max_fields'; + created_at?: Maybe; + intro?: Maybe; + invite_code?: Maybe; + name?: Maybe; + uuid?: Maybe; +}; + +/** aggregate min on columns */ +export type Room_Min_Fields = { + __typename?: 'room_min_fields'; + created_at?: Maybe; + intro?: Maybe; + invite_code?: Maybe; + name?: Maybe; + uuid?: Maybe; +}; + +/** response of any mutation on the table "room" */ +export type Room_Mutation_Response = { + __typename?: 'room_mutation_response'; + /** number of rows affected by the mutation */ + affected_rows: Scalars['Int']['output']; + /** data from the rows affected by the mutation */ + returning: Array; +}; + +/** input type for inserting object relation for remote table "room" */ +export type Room_Obj_Rel_Insert_Input = { + data: Room_Insert_Input; + /** upsert condition */ + on_conflict?: InputMaybe; +}; + +/** on_conflict condition type for table "room" */ +export type Room_On_Conflict = { + constraint: Room_Constraint; + update_columns?: Array; + where?: InputMaybe; +}; + +/** Ordering options when selecting data from "room". */ +export type Room_Order_By = { + created_at?: InputMaybe; + intro?: InputMaybe; + invite_code?: InputMaybe; + messages_aggregate?: InputMaybe; + name?: InputMaybe; + user_rooms_aggregate?: InputMaybe; + uuid?: InputMaybe; +}; + +/** primary key columns input for table: room */ +export type Room_Pk_Columns_Input = { + uuid: Scalars['uuid']['input']; +}; + +/** select columns of table "room" */ +export enum Room_Select_Column { + /** column name */ + CreatedAt = 'created_at', + /** column name */ + Intro = 'intro', + /** column name */ + InviteCode = 'invite_code', + /** column name */ + Name = 'name', + /** column name */ + Uuid = 'uuid' +} + +/** input type for updating data in table "room" */ +export type Room_Set_Input = { + created_at?: InputMaybe; + intro?: InputMaybe; + invite_code?: InputMaybe; + name?: InputMaybe; + uuid?: InputMaybe; +}; + +/** Streaming cursor of the table "room" */ +export type Room_Stream_Cursor_Input = { + /** Stream column input with initial value */ + initial_value: Room_Stream_Cursor_Value_Input; + /** cursor ordering */ + ordering?: InputMaybe; +}; + +/** Initial value of the column from where the streaming should start */ +export type Room_Stream_Cursor_Value_Input = { + created_at?: InputMaybe; + intro?: InputMaybe; + invite_code?: InputMaybe; + name?: InputMaybe; + uuid?: InputMaybe; +}; + +/** update columns of table "room" */ +export enum Room_Update_Column { + /** column name */ + CreatedAt = 'created_at', + /** column name */ + Intro = 'intro', + /** column name */ + InviteCode = 'invite_code', + /** column name */ + Name = 'name', + /** column name */ + Uuid = 'uuid' +} + +export type Room_Updates = { + /** sets the columns of the filtered rows to the given values */ + _set?: InputMaybe; + /** filter the rows which have to be updated */ + where: Room_Bool_Exp; +}; + +export type Subscription_Root = { + __typename?: 'subscription_root'; + /** fetch data from the table: "message" */ + message: Array; + /** fetch aggregated fields from the table: "message" */ + message_aggregate: Message_Aggregate; + /** fetch data from the table: "message" using primary key columns */ + message_by_pk?: Maybe; + /** fetch data from the table in a streaming manner: "message" */ + message_stream: Array; + /** fetch data from the table: "room" */ + room: Array; + /** fetch aggregated fields from the table: "room" */ + room_aggregate: Room_Aggregate; + /** fetch data from the table: "room" using primary key columns */ + room_by_pk?: Maybe; + /** fetch data from the table in a streaming manner: "room" */ + room_stream: Array; + /** fetch data from the table: "user" */ + user: Array; + /** fetch aggregated fields from the table: "user" */ + user_aggregate: User_Aggregate; + /** fetch data from the table: "user" using primary key columns */ + user_by_pk?: Maybe; + /** fetch data from the table: "user_room" */ + user_room: Array; + /** fetch aggregated fields from the table: "user_room" */ + user_room_aggregate: User_Room_Aggregate; + /** fetch data from the table: "user_room" using primary key columns */ + user_room_by_pk?: Maybe; + /** fetch data from the table in a streaming manner: "user_room" */ + user_room_stream: Array; + /** fetch data from the table in a streaming manner: "user" */ + user_stream: Array; +}; + + +export type Subscription_RootMessageArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +export type Subscription_RootMessage_AggregateArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +export type Subscription_RootMessage_By_PkArgs = { + uuid: Scalars['uuid']['input']; +}; + + +export type Subscription_RootMessage_StreamArgs = { + batch_size: Scalars['Int']['input']; + cursor: Array>; + where?: InputMaybe; +}; + + +export type Subscription_RootRoomArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +export type Subscription_RootRoom_AggregateArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +export type Subscription_RootRoom_By_PkArgs = { + uuid: Scalars['uuid']['input']; +}; + + +export type Subscription_RootRoom_StreamArgs = { + batch_size: Scalars['Int']['input']; + cursor: Array>; + where?: InputMaybe; +}; + + +export type Subscription_RootUserArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +export type Subscription_RootUser_AggregateArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +export type Subscription_RootUser_By_PkArgs = { + uuid: Scalars['uuid']['input']; +}; + + +export type Subscription_RootUser_RoomArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +export type Subscription_RootUser_Room_AggregateArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +export type Subscription_RootUser_Room_By_PkArgs = { + room_uuid: Scalars['uuid']['input']; + user_uuid: Scalars['uuid']['input']; +}; + + +export type Subscription_RootUser_Room_StreamArgs = { + batch_size: Scalars['Int']['input']; + cursor: Array>; + where?: InputMaybe; +}; + + +export type Subscription_RootUser_StreamArgs = { + batch_size: Scalars['Int']['input']; + cursor: Array>; + where?: InputMaybe; +}; + +/** Boolean expression to compare columns of type "timestamp". All fields are combined with logical 'AND'. */ +export type Timestamp_Comparison_Exp = { + _eq?: InputMaybe; + _gt?: InputMaybe; + _gte?: InputMaybe; + _in?: InputMaybe>; + _is_null?: InputMaybe; + _lt?: InputMaybe; + _lte?: InputMaybe; + _neq?: InputMaybe; + _nin?: InputMaybe>; +}; + +/** columns and relationships of "user" */ +export type User = { + __typename?: 'user'; + /** An array relationship */ + messages: Array; + /** An aggregate relationship */ + messages_aggregate: Message_Aggregate; + password: Scalars['String']['output']; + /** An array relationship */ + user_rooms: Array; + /** An aggregate relationship */ + user_rooms_aggregate: User_Room_Aggregate; + username: Scalars['String']['output']; + uuid: Scalars['uuid']['output']; +}; + + +/** columns and relationships of "user" */ +export type UserMessagesArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +/** columns and relationships of "user" */ +export type UserMessages_AggregateArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +/** columns and relationships of "user" */ +export type UserUser_RoomsArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +/** columns and relationships of "user" */ +export type UserUser_Rooms_AggregateArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + +/** aggregated selection of "user" */ +export type User_Aggregate = { + __typename?: 'user_aggregate'; + aggregate?: Maybe; + nodes: Array; +}; + +/** aggregate fields of "user" */ +export type User_Aggregate_Fields = { + __typename?: 'user_aggregate_fields'; + count: Scalars['Int']['output']; + max?: Maybe; + min?: Maybe; +}; + + +/** aggregate fields of "user" */ +export type User_Aggregate_FieldsCountArgs = { + columns?: InputMaybe>; + distinct?: InputMaybe; +}; + +/** Boolean expression to filter rows from the table "user". All fields are combined with a logical 'AND'. */ +export type User_Bool_Exp = { + _and?: InputMaybe>; + _not?: InputMaybe; + _or?: InputMaybe>; + messages?: InputMaybe; + messages_aggregate?: InputMaybe; + password?: InputMaybe; + user_rooms?: InputMaybe; + user_rooms_aggregate?: InputMaybe; + username?: InputMaybe; + uuid?: InputMaybe; +}; + +/** unique or primary key constraints on table "user" */ +export enum User_Constraint { + /** unique or primary key constraint on columns "uuid" */ + UserPkey = 'user_pkey', + /** unique or primary key constraint on columns "username" */ + UserUsernameKey = 'user_username_key' +} + +/** input type for inserting data into table "user" */ +export type User_Insert_Input = { + messages?: InputMaybe; + password?: InputMaybe; + user_rooms?: InputMaybe; + username?: InputMaybe; + uuid?: InputMaybe; +}; + +/** aggregate max on columns */ +export type User_Max_Fields = { + __typename?: 'user_max_fields'; + password?: Maybe; + username?: Maybe; + uuid?: Maybe; +}; + +/** aggregate min on columns */ +export type User_Min_Fields = { + __typename?: 'user_min_fields'; + password?: Maybe; + username?: Maybe; + uuid?: Maybe; +}; + +/** response of any mutation on the table "user" */ +export type User_Mutation_Response = { + __typename?: 'user_mutation_response'; + /** number of rows affected by the mutation */ + affected_rows: Scalars['Int']['output']; + /** data from the rows affected by the mutation */ + returning: Array; +}; + +/** input type for inserting object relation for remote table "user" */ +export type User_Obj_Rel_Insert_Input = { + data: User_Insert_Input; + /** upsert condition */ + on_conflict?: InputMaybe; +}; + +/** on_conflict condition type for table "user" */ +export type User_On_Conflict = { + constraint: User_Constraint; + update_columns?: Array; + where?: InputMaybe; +}; + +/** Ordering options when selecting data from "user". */ +export type User_Order_By = { + messages_aggregate?: InputMaybe; + password?: InputMaybe; + user_rooms_aggregate?: InputMaybe; + username?: InputMaybe; + uuid?: InputMaybe; +}; + +/** primary key columns input for table: user */ +export type User_Pk_Columns_Input = { + uuid: Scalars['uuid']['input']; +}; + +/** columns and relationships of "user_room" */ +export type User_Room = { + __typename?: 'user_room'; + /** An object relationship */ + room: Room; + room_uuid: Scalars['uuid']['output']; + /** An object relationship */ + user: User; + user_uuid: Scalars['uuid']['output']; +}; + +/** aggregated selection of "user_room" */ +export type User_Room_Aggregate = { + __typename?: 'user_room_aggregate'; + aggregate?: Maybe; + nodes: Array; +}; + +export type User_Room_Aggregate_Bool_Exp = { + count?: InputMaybe; +}; + +export type User_Room_Aggregate_Bool_Exp_Count = { + arguments?: InputMaybe>; + distinct?: InputMaybe; + filter?: InputMaybe; + predicate: Int_Comparison_Exp; +}; + +/** aggregate fields of "user_room" */ +export type User_Room_Aggregate_Fields = { + __typename?: 'user_room_aggregate_fields'; + count: Scalars['Int']['output']; + max?: Maybe; + min?: Maybe; +}; + + +/** aggregate fields of "user_room" */ +export type User_Room_Aggregate_FieldsCountArgs = { + columns?: InputMaybe>; + distinct?: InputMaybe; +}; + +/** order by aggregate values of table "user_room" */ +export type User_Room_Aggregate_Order_By = { + count?: InputMaybe; + max?: InputMaybe; + min?: InputMaybe; +}; + +/** input type for inserting array relation for remote table "user_room" */ +export type User_Room_Arr_Rel_Insert_Input = { + data: Array; + /** upsert condition */ + on_conflict?: InputMaybe; +}; + +/** Boolean expression to filter rows from the table "user_room". All fields are combined with a logical 'AND'. */ +export type User_Room_Bool_Exp = { + _and?: InputMaybe>; + _not?: InputMaybe; + _or?: InputMaybe>; + room?: InputMaybe; + room_uuid?: InputMaybe; + user?: InputMaybe; + user_uuid?: InputMaybe; +}; + +/** unique or primary key constraints on table "user_room" */ +export enum User_Room_Constraint { + /** unique or primary key constraint on columns "user_uuid", "room_uuid" */ + UserRoomPkey = 'user_room_pkey' +} + +/** input type for inserting data into table "user_room" */ +export type User_Room_Insert_Input = { + room?: InputMaybe; + room_uuid?: InputMaybe; + user?: InputMaybe; + user_uuid?: InputMaybe; +}; + +/** aggregate max on columns */ +export type User_Room_Max_Fields = { + __typename?: 'user_room_max_fields'; + room_uuid?: Maybe; + user_uuid?: Maybe; +}; + +/** order by max() on columns of table "user_room" */ +export type User_Room_Max_Order_By = { + room_uuid?: InputMaybe; + user_uuid?: InputMaybe; +}; + +/** aggregate min on columns */ +export type User_Room_Min_Fields = { + __typename?: 'user_room_min_fields'; + room_uuid?: Maybe; + user_uuid?: Maybe; +}; + +/** order by min() on columns of table "user_room" */ +export type User_Room_Min_Order_By = { + room_uuid?: InputMaybe; + user_uuid?: InputMaybe; +}; + +/** response of any mutation on the table "user_room" */ +export type User_Room_Mutation_Response = { + __typename?: 'user_room_mutation_response'; + /** number of rows affected by the mutation */ + affected_rows: Scalars['Int']['output']; + /** data from the rows affected by the mutation */ + returning: Array; +}; + +/** on_conflict condition type for table "user_room" */ +export type User_Room_On_Conflict = { + constraint: User_Room_Constraint; + update_columns?: Array; + where?: InputMaybe; +}; + +/** Ordering options when selecting data from "user_room". */ +export type User_Room_Order_By = { + room?: InputMaybe; + room_uuid?: InputMaybe; + user?: InputMaybe; + user_uuid?: InputMaybe; +}; + +/** primary key columns input for table: user_room */ +export type User_Room_Pk_Columns_Input = { + room_uuid: Scalars['uuid']['input']; + user_uuid: Scalars['uuid']['input']; +}; + +/** select columns of table "user_room" */ +export enum User_Room_Select_Column { + /** column name */ + RoomUuid = 'room_uuid', + /** column name */ + UserUuid = 'user_uuid' +} + +/** input type for updating data in table "user_room" */ +export type User_Room_Set_Input = { + room_uuid?: InputMaybe; + user_uuid?: InputMaybe; +}; + +/** Streaming cursor of the table "user_room" */ +export type User_Room_Stream_Cursor_Input = { + /** Stream column input with initial value */ + initial_value: User_Room_Stream_Cursor_Value_Input; + /** cursor ordering */ + ordering?: InputMaybe; +}; + +/** Initial value of the column from where the streaming should start */ +export type User_Room_Stream_Cursor_Value_Input = { + room_uuid?: InputMaybe; + user_uuid?: InputMaybe; +}; + +/** update columns of table "user_room" */ +export enum User_Room_Update_Column { + /** column name */ + RoomUuid = 'room_uuid', + /** column name */ + UserUuid = 'user_uuid' +} + +export type User_Room_Updates = { + /** sets the columns of the filtered rows to the given values */ + _set?: InputMaybe; + /** filter the rows which have to be updated */ + where: User_Room_Bool_Exp; +}; + +/** select columns of table "user" */ +export enum User_Select_Column { + /** column name */ + Password = 'password', + /** column name */ + Username = 'username', + /** column name */ + Uuid = 'uuid' +} + +/** input type for updating data in table "user" */ +export type User_Set_Input = { + password?: InputMaybe; + username?: InputMaybe; + uuid?: InputMaybe; +}; + +/** Streaming cursor of the table "user" */ +export type User_Stream_Cursor_Input = { + /** Stream column input with initial value */ + initial_value: User_Stream_Cursor_Value_Input; + /** cursor ordering */ + ordering?: InputMaybe; +}; + +/** Initial value of the column from where the streaming should start */ +export type User_Stream_Cursor_Value_Input = { + password?: InputMaybe; + username?: InputMaybe; + uuid?: InputMaybe; +}; + +/** update columns of table "user" */ +export enum User_Update_Column { + /** column name */ + Password = 'password', + /** column name */ + Username = 'username', + /** column name */ + Uuid = 'uuid' +} + +export type User_Updates = { + /** sets the columns of the filtered rows to the given values */ + _set?: InputMaybe; + /** filter the rows which have to be updated */ + where: User_Bool_Exp; +}; + +/** Boolean expression to compare columns of type "uuid". All fields are combined with logical 'AND'. */ +export type Uuid_Comparison_Exp = { + _eq?: InputMaybe; + _gt?: InputMaybe; + _gte?: InputMaybe; + _in?: InputMaybe>; + _is_null?: InputMaybe; + _lt?: InputMaybe; + _lte?: InputMaybe; + _neq?: InputMaybe; + _nin?: InputMaybe>; +}; + +export type AddMessageMutationVariables = Exact<{ + user_uuid: Scalars['uuid']['input']; + room_uuid: Scalars['uuid']['input']; + content: Scalars['String']['input']; +}>; + + +export type AddMessageMutation = { __typename?: 'mutation_root', insert_message_one?: { __typename?: 'message', uuid: any } | null }; + +export type GetMessagesByRoomSubscriptionVariables = Exact<{ + room_uuid: Scalars['uuid']['input']; +}>; + + +export type GetMessagesByRoomSubscription = { __typename?: 'subscription_root', message: Array<{ __typename?: 'message', uuid: any, content: string, created_at: any, user: { __typename?: 'user', uuid: any, username: string } }> }; + +export type AddRoomMutationVariables = Exact<{ + name: Scalars['String']['input']; + intro: Scalars['String']['input']; + invite_code: Scalars['String']['input']; +}>; + + +export type AddRoomMutation = { __typename?: 'mutation_root', insert_room_one?: { __typename?: 'room', uuid: any } | null }; + +export type GetJoinedRoomsQueryVariables = Exact<{ + user_uuid: Scalars['uuid']['input']; +}>; + + +export type GetJoinedRoomsQuery = { __typename?: 'query_root', user_room: Array<{ __typename?: 'user_room', room: { __typename?: 'room', uuid: any, name: string, intro: string, invite_code: string, created_at: any } }> }; + +export type GetRoomByInviteCodeQueryVariables = Exact<{ + invite_code: Scalars['String']['input']; +}>; + + +export type GetRoomByInviteCodeQuery = { __typename?: 'query_root', room: Array<{ __typename?: 'room', uuid: any }> }; + +export type JoinRoomMutationVariables = Exact<{ + user_uuid: Scalars['uuid']['input']; + room_uuid: Scalars['uuid']['input']; +}>; + + +export type JoinRoomMutation = { __typename?: 'mutation_root', insert_user_room_one?: { __typename?: 'user_room', user_uuid: any, room_uuid: any } | null }; + +export type AddUserMutationVariables = Exact<{ + username: Scalars['String']['input']; + password: Scalars['String']['input']; +}>; + + +export type AddUserMutation = { __typename?: 'mutation_root', insert_user_one?: { __typename?: 'user', uuid: any } | null }; + +export type GetUsersByUsernameQueryVariables = Exact<{ + username: Scalars['String']['input']; +}>; + + +export type GetUsersByUsernameQuery = { __typename?: 'query_root', user: Array<{ __typename?: 'user', uuid: any, password: string }> }; + + +export const AddMessageDocument = gql` + mutation addMessage($user_uuid: uuid!, $room_uuid: uuid!, $content: String!) { + insert_message_one( + object: {user_uuid: $user_uuid, room_uuid: $room_uuid, content: $content} + ) { + uuid + } +} + `; +export const GetMessagesByRoomDocument = gql` + subscription getMessagesByRoom($room_uuid: uuid!) { + message(where: {room_uuid: {_eq: $room_uuid}}) { + uuid + user { + uuid + username + } + content + created_at + } +} + `; +export const AddRoomDocument = gql` + mutation addRoom($name: String!, $intro: String!, $invite_code: String!) { + insert_room_one(object: {name: $name, intro: $intro, invite_code: $invite_code}) { + uuid + } +} + `; +export const GetJoinedRoomsDocument = gql` + query getJoinedRooms($user_uuid: uuid!) { + user_room(where: {user_uuid: {_eq: $user_uuid}}) { + room { + uuid + name + intro + invite_code + created_at + } + } +} + `; +export const GetRoomByInviteCodeDocument = gql` + query getRoomByInviteCode($invite_code: String!) { + room(where: {invite_code: {_eq: $invite_code}}) { + uuid + } +} + `; +export const JoinRoomDocument = gql` + mutation joinRoom($user_uuid: uuid!, $room_uuid: uuid!) { + insert_user_room_one(object: {user_uuid: $user_uuid, room_uuid: $room_uuid}) { + user_uuid + room_uuid + } +} + `; +export const AddUserDocument = gql` + mutation addUser($username: String!, $password: String!) { + insert_user_one(object: {username: $username, password: $password}) { + uuid + } +} + `; +export const GetUsersByUsernameDocument = gql` + query getUsersByUsername($username: String!) { + user(where: {username: {_eq: $username}}) { + uuid + password + } +} + `; + +export type SdkFunctionWrapper = (action: (requestHeaders?:Record) => Promise, operationName: string, operationType?: string, variables?: any) => Promise; + + +const defaultWrapper: SdkFunctionWrapper = (action, _operationName, _operationType, _variables) => action(); + +export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper = defaultWrapper) { + return { + addMessage(variables: AddMessageMutationVariables, requestHeaders?: GraphQLClientRequestHeaders): Promise { + return withWrapper((wrappedRequestHeaders) => client.request(AddMessageDocument, variables, {...requestHeaders, ...wrappedRequestHeaders}), 'addMessage', 'mutation', variables); + }, + getMessagesByRoom(variables: GetMessagesByRoomSubscriptionVariables, requestHeaders?: GraphQLClientRequestHeaders): Promise { + return withWrapper((wrappedRequestHeaders) => client.request(GetMessagesByRoomDocument, variables, {...requestHeaders, ...wrappedRequestHeaders}), 'getMessagesByRoom', 'subscription', variables); + }, + addRoom(variables: AddRoomMutationVariables, requestHeaders?: GraphQLClientRequestHeaders): Promise { + return withWrapper((wrappedRequestHeaders) => client.request(AddRoomDocument, variables, {...requestHeaders, ...wrappedRequestHeaders}), 'addRoom', 'mutation', variables); + }, + getJoinedRooms(variables: GetJoinedRoomsQueryVariables, requestHeaders?: GraphQLClientRequestHeaders): Promise { + return withWrapper((wrappedRequestHeaders) => client.request(GetJoinedRoomsDocument, variables, {...requestHeaders, ...wrappedRequestHeaders}), 'getJoinedRooms', 'query', variables); + }, + getRoomByInviteCode(variables: GetRoomByInviteCodeQueryVariables, requestHeaders?: GraphQLClientRequestHeaders): Promise { + return withWrapper((wrappedRequestHeaders) => client.request(GetRoomByInviteCodeDocument, variables, {...requestHeaders, ...wrappedRequestHeaders}), 'getRoomByInviteCode', 'query', variables); + }, + joinRoom(variables: JoinRoomMutationVariables, requestHeaders?: GraphQLClientRequestHeaders): Promise { + return withWrapper((wrappedRequestHeaders) => client.request(JoinRoomDocument, variables, {...requestHeaders, ...wrappedRequestHeaders}), 'joinRoom', 'mutation', variables); + }, + addUser(variables: AddUserMutationVariables, requestHeaders?: GraphQLClientRequestHeaders): Promise { + return withWrapper((wrappedRequestHeaders) => client.request(AddUserDocument, variables, {...requestHeaders, ...wrappedRequestHeaders}), 'addUser', 'mutation', variables); + }, + getUsersByUsername(variables: GetUsersByUsernameQueryVariables, requestHeaders?: GraphQLClientRequestHeaders): Promise { + return withWrapper((wrappedRequestHeaders) => client.request(GetUsersByUsernameDocument, variables, {...requestHeaders, ...wrappedRequestHeaders}), 'getUsersByUsername', 'query', variables); + } + }; +} +export type Sdk = ReturnType; diff --git a/backend/src/index.ts b/backend/src/index.ts new file mode 100644 index 0000000..068cc71 --- /dev/null +++ b/backend/src/index.ts @@ -0,0 +1,79 @@ +import express from "express"; +import morgan from "morgan"; +import dotenv from "dotenv"; +import path from "path"; +import { GraphQLClient } from "graphql-request"; +import { getSdk } from "./graphql"; +import userRouter from "./user"; +import fileRouter from "./file"; +import emailRouter from "./email"; + +const app = express(); +const address = "http://localhost"; +const port = 8888; + +dotenv.config({ + path: path.resolve(process.cwd(), ".local.env"), +}); + +const client = new GraphQLClient( + process.env.HASURA_GRAPHQL_ENDPOINT!, + { + headers: { + "Content-Type": "application/json", + "x-hasura-admin-secret": process.env.HASURA_GRAPHQL_ADMIN_SECRET!, + }, + } +); +export const sdk = getSdk(client); + +// Log all requests to the console, optional. +app.use(morgan(process.env.NODE_ENV === "production" ? "combined" : "dev")); + +app.all("*", (req, res, next) => { + res.header("Access-Control-Allow-Origin", "*"); + res.header("Access-Control-Allow-Headers", "*"); + next(); +}); + +app.use(express.json()); + +app.use("/user", userRouter); +app.use("/file", fileRouter); +app.use("/email", emailRouter); + +// 要添加的新接口 +app.post("/sendMessage", async (req, res) => { + // 从 Hasura Action 的请求体中获取参数 + const { user_uuid, room_uuid, content } = req.body.input; + + // 输入验证 + if (!content || content.trim() === "") { + return res.status(400).json({ + message: "Content cannot be empty", + }); + } + + // 调用 Hasura GraphQL API 来插入数据 + try { + const data = await sdk.addMessage({ + user_uuid: user_uuid, + room_uuid: room_uuid, + content: content, + }); + + // 返回成功响应 + return res.json({ + uuid: data.insert_message_one?.uuid, + }); + } catch (err) { + console.error(err); + return res.status(500).json({ + message: "Failed to send message", + }); + } +}); + +app.listen(port, () => { + console.log(`Server running at ${address}:${port}/`); +}); diff --git a/backend/src/user.ts b/backend/src/user.ts new file mode 100644 index 0000000..237b6a5 --- /dev/null +++ b/backend/src/user.ts @@ -0,0 +1,83 @@ +import express from "express"; +import jwt from "jsonwebtoken"; +import { sdk as graphql } from "./index"; + +interface userJWTPayload { + uuid: string; + "https://hasura.io/jwt/claims": { + "x-hasura-allowed-roles": string[]; + "x-hasura-default-role": string; + }; +} + +const router = express.Router(); + +router.post("/login", async (req, res) => { + const { username, password } = req.body; + if (username.length < 3 || username.length > 20) { + return res.status(400).json({ + message: "Username must be between 3 and 20 characters", + extensions: { + code: "USER_INPUT_ERROR", + path: "$.args.username" + } + }); + } + if (!username || !password) { + return res.status(422).send("422 Unprocessable Entity: Missing username or password"); + } + try { + const queryResult = await graphql.getUsersByUsername({ username: username }); + if (queryResult.user.length === 0) { + return res.status(404).send("404 Not Found: User does not exist"); + } + const user = queryResult.user[0]; + if (user.password !== password) { + return res.status(401).send("401 Unauthorized: Password does not match"); + } + const payload: userJWTPayload = { + uuid: user.uuid, + "https://hasura.io/jwt/claims": { + "x-hasura-allowed-roles": ["admin", "user"], + "x-hasura-default-role": "user", + }, + }; + const token = jwt.sign(payload, process.env.JWT_SECRET!, { + expiresIn: "24h", + }); + return res.status(200).json({ token }); + } catch (err) { + console.error(err); + return res.sendStatus(500); + } +}); + +router.post("/register", async (req, res) => { + const { username, password } = req.body; + if (!username || !password) { + return res.status(422).send("422 Unprocessable Entity: Missing username or password"); + } + try { + const queryResult = await graphql.getUsersByUsername({ username: username }); + if (queryResult.user.length !== 0) { + return res.status(409).send("409 Conflict: User already exists"); + } + const mutationResult = await graphql.addUser({ username: username, password: password }); + const payload: userJWTPayload = { + uuid: mutationResult.insert_user_one?.uuid, + "https://hasura.io/jwt/claims": { + "x-hasura-allowed-roles": ["admin", "user"], + "x-hasura-default-role": "user", + }, + }; + const token = jwt.sign(payload, process.env.JWT_SECRET!, { + expiresIn: "24h", + }); + return res.status(200).json({ token }); + } catch (err) { + console.error(err); + return res.sendStatus(500); + } +}); + +export default router; diff --git a/backend/tsconfig.json b/backend/tsconfig.json new file mode 100644 index 0000000..790742b --- /dev/null +++ b/backend/tsconfig.json @@ -0,0 +1,109 @@ +{ + "include": ["./src"], + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "CommonJS", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./build", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} diff --git a/backend/yarn.lock b/backend/yarn.lock new file mode 100644 index 0000000..04728d2 --- /dev/null +++ b/backend/yarn.lock @@ -0,0 +1,1172 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.npmmirror.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@graphql-typed-document-node/core@^3.2.0": + version "3.2.0" + resolved "https://registry.npmmirror.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" + integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== + +"@jridgewell/resolve-uri@^3.0.3": + version "3.1.2" + resolved "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.5.0" + resolved "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@tsconfig/node10@^1.0.7": + version "1.0.11" + resolved "https://registry.npmmirror.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" + integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.npmmirror.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.npmmirror.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.4" + resolved "https://registry.npmmirror.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + +"@types/body-parser@*": + version "1.19.5" + resolved "https://registry.npmmirror.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" + integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.38" + resolved "https://registry.npmmirror.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" + integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== + dependencies: + "@types/node" "*" + +"@types/express-serve-static-core@^4.17.33": + version "4.19.5" + resolved "https://registry.npmmirror.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz#218064e321126fcf9048d1ca25dd2465da55d9c6" + integrity sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express@*", "@types/express@4.17.21": + version "4.17.21" + resolved "https://registry.npmmirror.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" + integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/http-errors@*": + version "2.0.4" + resolved "https://registry.npmmirror.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" + integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== + +"@types/jsonwebtoken@9.0.6": + version "9.0.6" + resolved "https://registry.npmmirror.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.6.tgz#d1af3544d99ad992fb6681bbe60676e06b032bd3" + integrity sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw== + dependencies: + "@types/node" "*" + +"@types/mime@^1": + version "1.3.5" + resolved "https://registry.npmmirror.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" + integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== + +"@types/morgan@1.9.9": + version "1.9.9" + resolved "https://registry.npmmirror.com/@types/morgan/-/morgan-1.9.9.tgz#d60dec3979e16c203a000159daa07d3fb7270d7f" + integrity sha512-iRYSDKVaC6FkGSpEVVIvrRGw0DfJMiQzIn3qr2G5B3C//AWkulhXgaBd7tS9/J79GWSYMTHGs7PfI5b3Y8m+RQ== + dependencies: + "@types/node" "*" + +"@types/multer@1.4.11": + version "1.4.11" + resolved "https://registry.npmmirror.com/@types/multer/-/multer-1.4.11.tgz#c70792670513b4af1159a2b60bf48cc932af55c5" + integrity sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w== + dependencies: + "@types/express" "*" + +"@types/node@*", "@types/node@22.1.0": + version "22.1.0" + resolved "https://registry.npmmirror.com/@types/node/-/node-22.1.0.tgz#6d6adc648b5e03f0e83c78dc788c2b037d0ad94b" + integrity sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw== + dependencies: + undici-types "~6.13.0" + +"@types/nodemailer@6.4.15": + version "6.4.15" + resolved "https://registry.npmmirror.com/@types/nodemailer/-/nodemailer-6.4.15.tgz#494be695e11c438f7f5df738fb4ab740312a6ed2" + integrity sha512-0EBJxawVNjPkng1zm2vopRctuWVCxk34JcIlRuXSf54habUWdz1FB7wHDqOqvDa8Mtpt0Q3LTXQkAs2LNyK5jQ== + dependencies: + "@types/node" "*" + +"@types/qs@*": + version "6.9.15" + resolved "https://registry.npmmirror.com/@types/qs/-/qs-6.9.15.tgz#adde8a060ec9c305a82de1babc1056e73bd64dce" + integrity sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg== + +"@types/range-parser@*": + version "1.2.7" + resolved "https://registry.npmmirror.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" + integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== + +"@types/send@*": + version "0.17.4" + resolved "https://registry.npmmirror.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" + integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-static@*": + version "1.15.7" + resolved "https://registry.npmmirror.com/@types/serve-static/-/serve-static-1.15.7.tgz#22174bbd74fb97fe303109738e9b5c2f3064f714" + integrity sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw== + dependencies: + "@types/http-errors" "*" + "@types/node" "*" + "@types/send" "*" + +accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.npmmirror.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-walk@^8.1.1: + version "8.3.3" + resolved "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-8.3.3.tgz#9caeac29eefaa0c41e3d4c65137de4d6f34df43e" + integrity sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw== + dependencies: + acorn "^8.11.0" + +acorn@^8.11.0, acorn@^8.4.1: + version "8.12.1" + resolved "https://registry.npmmirror.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" + integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +append-field@^1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" + integrity sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw== + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.npmmirror.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.npmmirror.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +basic-auth@~2.0.1: + version "2.0.1" + resolved "https://registry.npmmirror.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" + integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== + dependencies: + safe-buffer "5.1.2" + +binary-extensions@^2.0.0: + version "2.3.0" + resolved "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + +body-parser@1.20.2: + version "1.20.2" + resolved "https://registry.npmmirror.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" + integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== + dependencies: + bytes "3.1.2" + content-type "~1.0.5" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.2" + type-is "~1.6.18" + unpipe "1.0.0" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@~3.0.2: + version "3.0.3" + resolved "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +busboy@^1.0.0: + version "1.6.0" + resolved "https://registry.npmmirror.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" + integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== + dependencies: + streamsearch "^1.1.0" + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + +chokidar@^3.5.2: + version "3.6.0" + resolved "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +concat-stream@^1.5.2: + version "1.6.2" + resolved "https://registry.npmmirror.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.npmmirror.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4, content-type@~1.0.5: + version "1.0.5" + resolved "https://registry.npmmirror.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.6.0: + version "0.6.0" + resolved "https://registry.npmmirror.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.npmmirror.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +cross-fetch@^3.1.5: + version "3.1.8" + resolved "https://registry.npmmirror.com/cross-fetch/-/cross-fetch-3.1.8.tgz#0327eba65fd68a7d119f8fb2bf9334a1a7956f82" + integrity sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg== + dependencies: + node-fetch "^2.6.12" + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^4: + version "4.3.6" + resolved "https://registry.npmmirror.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" + integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== + dependencies: + ms "2.1.2" + +define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +depd@2.0.0, depd@~2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.npmmirror.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.npmmirror.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +dotenv@16.4.5: + version "16.4.5" + resolved "https://registry.npmmirror.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" + integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== + +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.npmmirror.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.npmmirror.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +express@4.19.2: + version "4.19.2" + resolved "https://registry.npmmirror.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465" + integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.2" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.6.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.npmmirror.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.npmmirror.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graphql-request@6.1.0: + version "6.1.0" + resolved "https://registry.npmmirror.com/graphql-request/-/graphql-request-6.1.0.tgz#f4eb2107967af3c7a5907eb3131c671eac89be4f" + integrity sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw== + dependencies: + "@graphql-typed-document-node/core" "^3.2.0" + cross-fetch "^3.1.5" + +graphql@16.9.0: + version "16.9.0" + resolved "https://registry.npmmirror.com/graphql/-/graphql-16.9.0.tgz#1c310e63f16a49ce1fbb230bd0a000e99f6f115f" + integrity sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1: + version "1.0.3" + resolved "https://registry.npmmirror.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +hasown@^2.0.0: + version "2.0.2" + resolved "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ignore-by-default@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" + integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA== + +inherits@2.0.4, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +jsonwebtoken@9.0.2: + version "9.0.2" + resolved "https://registry.npmmirror.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3" + integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^7.5.4" + +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.npmmirror.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.npmmirror.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.npmmirror.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.npmmirror.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.npmmirror.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.npmmirror.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.npmmirror.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.npmmirror.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.npmmirror.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== + +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.npmmirror.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.npmmirror.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +mkdirp@^0.5.4: + version "0.5.6" + resolved "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +morgan@1.10.0: + version "1.10.0" + resolved "https://registry.npmmirror.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" + integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ== + dependencies: + basic-auth "~2.0.1" + debug "2.6.9" + depd "~2.0.0" + on-finished "~2.3.0" + on-headers "~1.0.2" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3, ms@^2.1.1: + version "2.1.3" + resolved "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +multer@1.4.5-lts.1: + version "1.4.5-lts.1" + resolved "https://registry.npmmirror.com/multer/-/multer-1.4.5-lts.1.tgz#803e24ad1984f58edffbc79f56e305aec5cfd1ac" + integrity sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ== + dependencies: + append-field "^1.0.0" + busboy "^1.0.0" + concat-stream "^1.5.2" + mkdirp "^0.5.4" + object-assign "^4.1.1" + type-is "^1.6.4" + xtend "^4.0.0" + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +node-fetch@^2.6.12: + version "2.7.0" + resolved "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + +nodemailer@6.9.14: + version "6.9.14" + resolved "https://registry.npmmirror.com/nodemailer/-/nodemailer-6.9.14.tgz#845fda981f9fd5ac264f4446af908a7c78027f75" + integrity sha512-Dobp/ebDKBvz91sbtRKhcznLThrKxKt97GI2FAlAyy+fk19j73Uz3sBXolVtmcXjaorivqsbbbjDY+Jkt4/bQA== + +nodemon@3.1.4: + version "3.1.4" + resolved "https://registry.npmmirror.com/nodemon/-/nodemon-3.1.4.tgz#c34dcd8eb46a05723ccde60cbdd25addcc8725e4" + integrity sha512-wjPBbFhtpJwmIeY2yP7QF+UKzPfltVGtfce1g/bB15/8vCGZj8uxD62b/b9M9/WVgme0NZudpownKN+c0plXlQ== + dependencies: + chokidar "^3.5.2" + debug "^4" + ignore-by-default "^1.0.1" + minimatch "^3.1.2" + pstree.remy "^1.1.8" + semver "^7.5.3" + simple-update-notifier "^2.0.0" + supports-color "^5.5.0" + touch "^3.1.0" + undefsafe "^2.0.5" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.13.1: + version "1.13.2" + resolved "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" + integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.npmmirror.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.npmmirror.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.npmmirror.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.1" + resolved "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +pstree.remy@^1.1.8: + version "1.1.8" + resolved "https://registry.npmmirror.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" + integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== + +qs@6.11.0: + version "6.11.0" + resolved "https://registry.npmmirror.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.2: + version "2.5.2" + resolved "https://registry.npmmirror.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +readable-stream@^2.2.2: + version "2.3.8" + resolved "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@5.2.1, safe-buffer@^5.0.1: + version "5.2.1" + resolved "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +semver@^7.5.3, semver@^7.5.4: + version "7.6.3" + resolved "https://registry.npmmirror.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + +send@0.18.0: + version "0.18.0" + resolved "https://registry.npmmirror.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.npmmirror.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +side-channel@^1.0.4: + version "1.0.6" + resolved "https://registry.npmmirror.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + +simple-update-notifier@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz#d70b92bdab7d6d90dfd73931195a30b6e3d7cebb" + integrity sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w== + dependencies: + semver "^7.5.3" + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +streamsearch@^1.1.0: + version "1.1.0" + resolved "https://registry.npmmirror.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" + integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +supports-color@^5.5.0: + version "5.5.0" + resolved "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +touch@^3.1.0: + version "3.1.1" + resolved "https://registry.npmmirror.com/touch/-/touch-3.1.1.tgz#097a23d7b161476435e5c1344a95c0f75b4a5694" + integrity sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA== + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +ts-node@10.9.2: + version "10.9.2" + resolved "https://registry.npmmirror.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + +type-is@^1.6.4, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.npmmirror.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.npmmirror.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== + +typescript@5.5.4: + version "5.5.4" + resolved "https://registry.npmmirror.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba" + integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== + +undefsafe@^2.0.5: + version "2.0.5" + resolved "https://registry.npmmirror.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" + integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== + +undici-types@~6.13.0: + version "6.13.0" + resolved "https://registry.npmmirror.com/undici-types/-/undici-types-6.13.0.tgz#e3e79220ab8c81ed1496b5812471afd7cf075ea5" + integrity sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.npmmirror.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +xtend@^4.0.0: + version "4.0.2" + resolved "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.npmmirror.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== diff --git a/database/graphql/message.graphql b/database/graphql/message.graphql index 994647c..3b24b6a 100644 --- a/database/graphql/message.graphql +++ b/database/graphql/message.graphql @@ -15,3 +15,14 @@ subscription getMessagesByRoom($room_uuid: uuid!) { created_at } } + +query getMessagesByUser($user_uuid: uuid!) { + message(where: {user_uuid: {_eq: $user_uuid}}, order_by: {created_at: desc}) { + uuid + content + created_at + room { + name + } + } +} diff --git a/database/graphql/room.graphql b/database/graphql/room.graphql index 4f37de0..bc9cfb5 100644 --- a/database/graphql/room.graphql +++ b/database/graphql/room.graphql @@ -28,3 +28,14 @@ mutation joinRoom($user_uuid: uuid!, $room_uuid: uuid!) { room_uuid } } + +mutation quitRoom($user_uuid: uuid!, $room_uuid: uuid!) { + delete_user_room( + where: {user_uuid: {_eq: $user_uuid}, room_uuid: {_eq: $room_uuid}} + ) { + returning { + user_uuid + room_uuid + } + } +} diff --git a/database/graphql/user.graphql b/database/graphql/user.graphql index d7780cd..3391efa 100644 --- a/database/graphql/user.graphql +++ b/database/graphql/user.graphql @@ -10,3 +10,13 @@ query getUsersByUsername($username: String!) { password } } + +mutation deleteUser($uuid: uuid!) { + delete_user(where: {uuid: {_eq: $uuid}}) { + returning { + uuid + username + } + } +} + diff --git a/database/hasura/docker-compose.yaml b/database/hasura/docker-compose.yaml new file mode 100644 index 0000000..39be6ca --- /dev/null +++ b/database/hasura/docker-compose.yaml @@ -0,0 +1,24 @@ +version: "3.6" +services: + postgres: + image: postgres:latest + container_name: postgres + restart: always + volumes: + - ~/data/postgres:/var/lib/postgresql/data + ports: + - "5432:5432" + environment: + POSTGRES_PASSWORD: postgrespassword + graphql-engine: + image: hasura/graphql-engine:latest + container_name: hasura + ports: + - "23333:8080" + depends_on: + - "postgres" + restart: always + environment: + HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:postgrespassword@postgres:5432/postgres + HASURA_GRAPHQL_ENABLE_CONSOLE: "true" + HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log \ No newline at end of file diff --git a/frontend/.env b/frontend/.env new file mode 100644 index 0000000..6b25edb --- /dev/null +++ b/frontend/.env @@ -0,0 +1,3 @@ +REACT_APP_BACKEND_URL=https://workshop.eesast.com +REACT_APP_HASURA_HTTPLINK=https://workshop.eesast.com/v1/graphql +REACT_APP_HASURA_WSLINK=wss://workshop.eesast.com/v1/graphql diff --git a/frontend/craco.config.js b/frontend/craco.config.js new file mode 100644 index 0000000..1ae0534 --- /dev/null +++ b/frontend/craco.config.js @@ -0,0 +1,40 @@ +const HtmlWebpackPlugin = require("html-webpack-plugin"); +const WebpackBar = require("webpackbar"); +// const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer"); + +module.exports = { + devServer: { + historyApiFallback: { + rewrites: [ + { from: /^\/main/, to: "/main.html" }, + { from: /^\/about/, to: "/about.html" }, + { from: /^\/about-me/, to: "/about-me.html" }, + ], + }, + }, + webpack: { + plugins: { + remove: [ + "HtmlWebpackPlugin", + ], + add: [ + new HtmlWebpackPlugin( + { + inject: false, + filename: "index.html", + template: "public/index.html", + } + ), + new HtmlWebpackPlugin( + { + inject: true, + filename: "main.html", + template: "public/main.html", + } + ), + new WebpackBar(), + // new BundleAnalyzerPlugin(), + ] + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..5dbb77b --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,97 @@ +{ + "homepage": "./", + "dependencies": { + "@ant-design/pro-components": "2.7.15", + "@apollo/client": "3.11.4", + "antd": "5.20.2", + "axios": "1.7.4", + "graphql": "16.9.0", + "graphql-ws": "5.16.0", + "jwt-decode": "4.0.0", + "md5": "2.3.0", + "react": "18.3.1", + "react-dom": "18.3.1", + "react-draggable": "4.4.6", + "react-router-dom": "6.26.1" + }, + "devDependencies": { + "@craco/craco": "7.1.0", + "@types/md5": "2.3.5", + "@types/react": "18.3.3", + "@types/react-dom": "18.3.0", + "react-scripts": "5.0.1", + "typescript": "5.5.4", + "webpack-bundle-analyzer": "4.10.2", + "webpackbar": "6.0.1" + }, + "resolutions": { + "@babel/plugin-proposal-private-property-in-object": "7.21.11" + }, + "scripts": { + "start": "craco start", + "build": "craco build", + "typecheck": "tsc --noEmit", + "lint": "eslint src --ext .ts,.tsx", + "electron": "yarn build && electron .", + "electron:build": "electron-builder" + }, + "eslintConfig": { + "extends": [ + "react-app" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "name": "web-workshop", + "version": "2024.0.0", + "main": "build/electron.js", + "build": { + "productName": "EESAST Web Workshop", + "appId": "web-workshop", + "icon": "build/logo.png", + "directories": { + "output": "electron" + }, + "win": { + "target": [ + "nsis", + "portable" + ] + }, + "nsis": { + "shortcutName": "EESAST", + "oneClick": false, + "perMachine": true, + "allowElevation": true, + "allowToChangeInstallationDirectory": true, + "createDesktopShortcut": true, + "createStartMenuShortcut": true + }, + "mac": { + "target": [ + "dmg", + "zip" + ] + }, + "linux": { + "category": "Utility", + "maintainer": "EESAST", + "target": [ + "AppImage", + "deb", + "rpm", + "tar.gz" + ] + } + } +} diff --git a/frontend/public/about-me.html b/frontend/public/about-me.html new file mode 100644 index 0000000..36b6dfe --- /dev/null +++ b/frontend/public/about-me.html @@ -0,0 +1,89 @@ + + + + + 关于我 + + + +

关于我

+

你好!我叫懒人,是电子系一名学生。

+ +

我的爱好

+
    +
  • 睡懒觉!
  • +
  • 睡懒觉!
  • +
  • 睡懒觉!
  • +
+ +

一件趣事

+

前天有个会要开,完全忘记了!

+ +

个人照片

+ 我的照片 + +

想回首页?点这里

+ + +
+

GitHub 实时数据

+

加载中…

+
+ + + + diff --git a/frontend/public/about.html b/frontend/public/about.html index 6e2a043..07f3702 100644 --- a/frontend/public/about.html +++ b/frontend/public/about.html @@ -2,6 +2,7 @@ + 关于这个工程 + + +
+

欢迎来到我的主页

+

点击这里查看 关于我

+
+ + \ No newline at end of file diff --git a/frontend/public/index.css b/frontend/public/index.css index 1cb6bf9..2f457c2 100644 --- a/frontend/public/index.css +++ b/frontend/public/index.css @@ -6,9 +6,10 @@ } #main-background-music { - position: absolute; + position: fixed; right: 12px; bottom: 12px; + z-index: 100; } #about-main-frame { diff --git a/frontend/public/index.html b/frontend/public/index.html index fe08636..2f21068 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -2,6 +2,7 @@ + 2024科协暑培网站Demo