├── .gitignore ├── server-django ├── .gitignore ├── server │ ├── __init__.py │ ├── wsgi.py │ ├── urls.py │ ├── views.py │ └── settings.py ├── db.sqlite3 ├── requirements.txt ├── manage.py └── README.md ├── server-fastapi ├── .gitignore ├── requirements.txt ├── README.md └── main.py ├── server-nodejs ├── .gitignore ├── README.md ├── package.json ├── index.js └── package-lock.json ├── client-vue └── README.md ├── server-express ├── .gitignore ├── package.json ├── README.md └── index.js ├── server-spring ├── src │ └── main │ │ ├── resources │ │ └── application.properties │ │ └── java │ │ └── com │ │ └── example │ │ └── chatengine │ │ └── serverspring │ │ ├── ServerSpringApplication.java │ │ └── UserController.java ├── README.md ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── .gitignore ├── pom.xml ├── mvnw.cmd └── mvnw ├── .DS_Store ├── client-react ├── .env ├── public │ ├── robots.txt │ ├── favicon.ico │ ├── logo192.png │ ├── manifest.json │ └── index.html ├── src │ ├── index.js │ ├── app.js │ ├── index.css │ ├── chatsPage.js │ └── authPage.js ├── .gitignore ├── package.json └── README.md ├── server-flask ├── README.md ├── .env ├── requirements.txt └── app.py ├── server-firebase ├── functions │ ├── tsconfig.dev.json │ ├── .gitignore │ ├── tsconfig.json │ ├── src │ │ └── index.ts │ ├── .eslintrc.js │ └── package.json ├── .firebaserc ├── frontend │ ├── public │ │ ├── favicon.ico │ │ ├── vercel.svg │ │ ├── thirteen.svg │ │ └── next.svg │ ├── next.config.js │ ├── pages │ │ ├── _app.tsx │ │ ├── Loading.tsx │ │ ├── _document.tsx │ │ ├── index.tsx │ │ ├── ChatsPage.tsx │ │ └── AuthPage.tsx │ ├── .gitignore │ ├── package.json │ ├── firebase.ts │ ├── tsconfig.json │ ├── styles │ │ └── globals.css │ └── README.md ├── firebase.json ├── .gitignore └── README.md ├── client-html ├── css │ ├── valley.jpeg │ └── style.css ├── chats.html └── index.html ├── serverless-vue ├── src │ ├── style.css │ ├── main.js │ ├── assets │ │ └── vue.svg │ ├── App.vue │ └── pages │ │ ├── AuthPage │ │ ├── api.js │ │ └── index.vue │ │ └── ChatsPage │ │ └── index.vue ├── .vscode │ └── extensions.json ├── vite.config.js ├── .gitignore ├── index.html ├── package.json ├── README.md └── public │ └── vite.svg ├── README.md └── requests.rest /.gitignore: -------------------------------------------------------------------------------- 1 | venv/ -------------------------------------------------------------------------------- /server-django/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /server-django/server/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server-fastapi/.gitignore: -------------------------------------------------------------------------------- 1 | venv/ 2 | -------------------------------------------------------------------------------- /server-nodejs/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /client-vue/README.md: -------------------------------------------------------------------------------- 1 | # Coming soon! 2 | -------------------------------------------------------------------------------- /server-express/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .env -------------------------------------------------------------------------------- /server-spring/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=3001 -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alamorre/fullstack-chat/HEAD/.DS_Store -------------------------------------------------------------------------------- /client-react/.env: -------------------------------------------------------------------------------- 1 | REACT_APP_CHAT_ENGINE_PROJECT_ID=794653df-052a-4ad2-b0aa-d6c251a10aef -------------------------------------------------------------------------------- /server-spring/README.md: -------------------------------------------------------------------------------- 1 | Install `mvn clean install` 2 | Run `mvn spring-boot:run` 3 | -------------------------------------------------------------------------------- /server-flask/README.md: -------------------------------------------------------------------------------- 1 | # Coming soon! 2 | 3 | flask --app app run --host=0.0.0.0 --port=3001 4 | -------------------------------------------------------------------------------- /server-django/db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alamorre/fullstack-chat/HEAD/server-django/db.sqlite3 -------------------------------------------------------------------------------- /server-firebase/functions/tsconfig.dev.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | ".eslintrc.js" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /client-html/css/valley.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alamorre/fullstack-chat/HEAD/client-html/css/valley.jpeg -------------------------------------------------------------------------------- /client-react/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /server-firebase/.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "fullstack-chat-server" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /serverless-vue/src/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-family: Avenir; 3 | } 4 | 5 | body { 6 | margin: 0px; 7 | } -------------------------------------------------------------------------------- /client-react/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alamorre/fullstack-chat/HEAD/client-react/public/favicon.ico -------------------------------------------------------------------------------- /client-react/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alamorre/fullstack-chat/HEAD/client-react/public/logo192.png -------------------------------------------------------------------------------- /serverless-vue/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] 3 | } 4 | -------------------------------------------------------------------------------- /server-flask/.env: -------------------------------------------------------------------------------- 1 | CHAT_ENGINE_PROJECT_ID=5d498a31-cd23-42b7-b367-4fcc9463bd2f 2 | CHAT_ENGINE_PRIVATE_KEY=49a46286-91c3-4f9c-92bf-284ae51b7628 -------------------------------------------------------------------------------- /server-firebase/frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alamorre/fullstack-chat/HEAD/server-firebase/frontend/public/favicon.ico -------------------------------------------------------------------------------- /server-spring/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alamorre/fullstack-chat/HEAD/server-spring/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /serverless-vue/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from "vue"; 2 | import "./style.css"; 3 | import App from "./App.vue"; 4 | 5 | createApp(App).mount("#app"); 6 | -------------------------------------------------------------------------------- /server-firebase/frontend/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: false, 4 | }; 5 | 6 | module.exports = nextConfig; 7 | -------------------------------------------------------------------------------- /serverless-vue/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [vue()], 7 | }) 8 | -------------------------------------------------------------------------------- /server-firebase/functions/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled JavaScript files 2 | lib/**/*.js 3 | lib/**/*.js.map 4 | 5 | # TypeScript v1 declaration files 6 | typings/ 7 | 8 | # Node.js dependency directory 9 | node_modules/ 10 | -------------------------------------------------------------------------------- /server-firebase/frontend/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import '@/styles/globals.css' 2 | import type { AppProps } from 'next/app' 3 | 4 | export default function App({ Component, pageProps }: AppProps) { 5 | return 6 | } 7 | -------------------------------------------------------------------------------- /server-firebase/frontend/pages/Loading.tsx: -------------------------------------------------------------------------------- 1 | export default function Loading() { 2 | return ( 3 |
4 |
☝️
5 |
Loading your info...
6 |
7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fullstack Chat project 2 | 3 | This project allows to setup fullstack chat for any frontend/backend combination! 4 | 5 | Connect React, Vue, or Angular and any backend you wish! 6 | 7 | This should fully support all your fullstack chat needs! 8 | -------------------------------------------------------------------------------- /server-django/requirements.txt: -------------------------------------------------------------------------------- 1 | asgiref==3.5.2 2 | certifi==2022.9.24 3 | charset-normalizer==2.1.1 4 | Django==4.1.3 5 | django-environ==0.9.0 6 | djangorestframework==3.14.0 7 | idna==3.4 8 | pytz==2022.6 9 | requests==2.28.1 10 | sqlparse==0.4.3 11 | urllib3==1.26.13 12 | -------------------------------------------------------------------------------- /server-spring/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar 3 | -------------------------------------------------------------------------------- /client-react/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import "./index.css"; 4 | import App from "./app"; 5 | 6 | const root = ReactDOM.createRoot(document.getElementById("root")); 7 | root.render( 8 | // 9 | 10 | // 11 | ); 12 | -------------------------------------------------------------------------------- /server-firebase/frontend/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import { Html, Head, Main, NextScript } from 'next/document' 2 | 3 | export default function Document() { 4 | return ( 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /server-nodejs/README.md: -------------------------------------------------------------------------------- 1 | ## NodeJS Server Setup 2 | 3 | ``` 4 | npm init 5 | npm install express axios cors 6 | npm install --save-dev nodemon 7 | # Add: "scripts": { "start": "nodemon index.js" }, 8 | echo node_modules/ > .gitignore 9 | touch index.js 10 | # Add the code 11 | npm run start 12 | ``` 13 | 14 | Edit the package.json file 15 | -------------------------------------------------------------------------------- /server-firebase/functions/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "noImplicitReturns": true, 5 | "noUnusedLocals": true, 6 | "outDir": "lib", 7 | "sourceMap": true, 8 | "strict": true, 9 | "target": "es2017" 10 | }, 11 | "compileOnSave": true, 12 | "include": [ 13 | "src" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /server-flask/requirements.txt: -------------------------------------------------------------------------------- 1 | certifi==2022.9.24 2 | charset-normalizer==2.1.1 3 | click==8.1.3 4 | Flask==2.2.2 5 | Flask-Cors==3.0.10 6 | idna==3.4 7 | importlib-metadata==5.1.0 8 | itsdangerous==2.1.2 9 | Jinja2==3.1.2 10 | MarkupSafe==2.1.1 11 | python-dotenv==0.21.0 12 | requests==2.28.1 13 | six==1.16.0 14 | urllib3==1.26.13 15 | Werkzeug==2.2.2 16 | zipp==3.11.0 17 | -------------------------------------------------------------------------------- /serverless-vue/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /client-react/src/app.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | import AuthPage from "./authPage"; 4 | import ChatsPage from "./chatsPage"; 5 | 6 | function App() { 7 | const [user, setUser] = useState(); 8 | 9 | if (!user) { 10 | return setUser(user)} />; 11 | } else { 12 | return ; 13 | } 14 | } 15 | 16 | export default App; 17 | -------------------------------------------------------------------------------- /requests.rest: -------------------------------------------------------------------------------- 1 | 2 | POST http://localhost:3001/login 3 | Content-Type: application/json 4 | 5 | { 6 | "username": "adam", 7 | "secret": "pass1234" 8 | } 9 | 10 | ### 11 | 12 | POST http://localhost:3001/signup 13 | Content-Type: application/json 14 | 15 | { 16 | "username": "zack", 17 | "secret": "pass1234", 18 | "email": "zack@gmail.com", 19 | "first_name": "Zack", 20 | "last_name": "Engine" 21 | } 22 | -------------------------------------------------------------------------------- /serverless-vue/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + Vue 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /client-react/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /client-react/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /server-fastapi/requirements.txt: -------------------------------------------------------------------------------- 1 | anyio==3.6.2 2 | certifi==2022.12.7 3 | charset-normalizer==2.1.1 4 | click==8.1.3 5 | fastapi==0.88.0 6 | h11==0.14.0 7 | httptools==0.5.0 8 | idna==3.4 9 | pydantic==1.10.2 10 | python-dotenv==0.21.0 11 | PyYAML==6.0 12 | requests==2.28.1 13 | sniffio==1.3.0 14 | starlette==0.22.0 15 | typing_extensions==4.4.0 16 | urllib3==1.26.13 17 | uvicorn==0.20.0 18 | uvloop==0.17.0 19 | watchfiles==0.18.1 20 | websockets==10.4 21 | -------------------------------------------------------------------------------- /server-nodejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server-node", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon index.js" 8 | }, 9 | "author": "Adam La Morre", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "nodemon": "^2.0.20" 13 | }, 14 | "dependencies": { 15 | "axios": "^1.2.0", 16 | "cors": "^2.8.5", 17 | "express": "^4.18.2" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server-firebase/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "functions": [ 3 | { 4 | "source": "functions", 5 | "codebase": "default", 6 | "ignore": [ 7 | "node_modules", 8 | ".git", 9 | "firebase-debug.log", 10 | "firebase-debug.*.log" 11 | ], 12 | "predeploy": [ 13 | "npm --prefix \"$RESOURCE_DIR\" run lint", 14 | "npm --prefix \"$RESOURCE_DIR\" run build" 15 | ] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /server-django/server/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for server project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'server.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /server-express/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server-express", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon index.js" 8 | }, 9 | "author": "Adam La Morre", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "nodemon": "^2.0.20" 13 | }, 14 | "dependencies": { 15 | "axios": "^1.2.0", 16 | "cors": "^2.8.5", 17 | "dotenv": "^16.0.3", 18 | "express": "^4.18.2" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server-spring/src/main/java/com/example/chatengine/serverspring/ServerSpringApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.chatengine.serverspring; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class ServerSpringApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(ServerSpringApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /client-html/chats.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Your Chats 9 | 10 | 11 |
12 |
chats...
13 |
14 | 15 | -------------------------------------------------------------------------------- /serverless-vue/src/assets/vue.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client-react/src/chatsPage.js: -------------------------------------------------------------------------------- 1 | import { PrettyChatWindow } from "react-chat-engine-pretty"; 2 | 3 | const ChatsPage = (props) => { 4 | return ( 5 |
6 | 12 |
13 | ); 14 | }; 15 | 16 | export default ChatsPage; 17 | -------------------------------------------------------------------------------- /serverless-vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-vue", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "axios": "^1.2.2", 13 | "react-chat-engine-pretty": "^0.1.5", 14 | "veaury": "^2.3.11", 15 | "vue": "^3.2.45" 16 | }, 17 | "devDependencies": { 18 | "@vitejs/plugin-vue": "^4.0.0", 19 | "vite": "^4.0.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server-spring/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /serverless-vue/README.md: -------------------------------------------------------------------------------- 1 | # Vue 3 + Vite 2 | 3 | This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 ` 5 | 6 | 10 | 11 | 25 | -------------------------------------------------------------------------------- /client-react/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /server-firebase/frontend/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | import AuthPage from "./AuthPage"; 4 | import ChatPage from "./ChatsPage"; 5 | import Loading from "./Loading"; 6 | import { auth } from "@/firebase"; 7 | import { User } from "firebase/auth"; 8 | 9 | export default function Home() { 10 | const [user, setUser] = useState(); 11 | auth.onAuthStateChanged((user) => setUser(user)); 12 | 13 | if (user === undefined) { 14 | return ; 15 | } else if (user === null) { 16 | return ; 17 | } else { 18 | return ; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server-firebase/frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@next/font": "13.1.6", 13 | "@types/node": "18.13.0", 14 | "@types/react": "18.0.27", 15 | "@types/react-dom": "18.0.10", 16 | "firebase": "^9.17.1", 17 | "next": "13.1.6", 18 | "react": "18.2.0", 19 | "react-chat-engine-pretty": "^0.1.8", 20 | "react-dom": "18.2.0", 21 | "typescript": "4.9.5" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /server-firebase/frontend/firebase.ts: -------------------------------------------------------------------------------- 1 | import { initializeApp } from "firebase/app"; 2 | import { getAuth } from "firebase/auth"; 3 | 4 | const firebaseConfig = { 5 | apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, 6 | authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN, 7 | projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID, 8 | storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET, 9 | messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID, 10 | appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID, 11 | }; 12 | 13 | export const app = initializeApp(firebaseConfig); 14 | export const auth = getAuth(app); 15 | -------------------------------------------------------------------------------- /server-firebase/frontend/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server-firebase/frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true, 17 | "baseUrl": ".", 18 | "paths": { 19 | "@/*": ["./*"] 20 | } 21 | }, 22 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 23 | "exclude": ["node_modules"] 24 | } 25 | -------------------------------------------------------------------------------- /serverless-vue/src/pages/AuthPage/api.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | const loginRest = async (username, secret) => { 4 | return await axios.get("https://api.chatengine.io/users/me/", { 5 | headers: { 6 | "Project-ID": import.meta.env.VITE_CHAT_ENGINE_PROJECT_ID, 7 | "User-Name": username, 8 | "User-Secret": secret, 9 | }, 10 | }); 11 | }; 12 | 13 | const signupRest = async (username, secret, email, first_name, last_name) => { 14 | return await axios.post( 15 | "https://api.chatengine.io/users/", 16 | { username, secret, email, first_name, last_name }, 17 | { headers: { "Private-Key": import.meta.env.VITE_CHAT_ENGINE_PRIVATE_KEY } } 18 | ); 19 | }; 20 | 21 | export { loginRest, signupRest }; 22 | -------------------------------------------------------------------------------- /server-django/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'server.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /server-firebase/frontend/pages/ChatsPage.tsx: -------------------------------------------------------------------------------- 1 | import { auth } from "@/firebase"; 2 | import { signOut, User } from "firebase/auth"; 3 | import { PrettyChatWindow } from "react-chat-engine-pretty"; 4 | interface ChatProps { 5 | user: User; 6 | } 7 | 8 | export default function Page(props: ChatProps) { 9 | return ( 10 |
11 | 17 | 23 |
24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /server-firebase/frontend/pages/AuthPage.tsx: -------------------------------------------------------------------------------- 1 | import { auth } from "@/firebase"; 2 | import { signInWithRedirect, GoogleAuthProvider } from "firebase/auth"; 3 | 4 | export default function AuthPage() { 5 | const onClick = () => { 6 | signInWithRedirect(auth, new GoogleAuthProvider()); 7 | }; 8 | 9 | return ( 10 |
11 |
👋 🔥 💬
12 |
Welcome to Firebase Next Chat
13 |
14 | Log in with your account to continue 15 |
16 | {" "} 19 | 22 |
23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /server-firebase/functions/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as functions from "firebase-functions"; 2 | 3 | const axios = require("axios"); 4 | 5 | exports.createChatEngineUser = functions.auth.user().onCreate((user) => { 6 | axios.post( 7 | "https://api.chatengine.io/users/", 8 | { 9 | username: user.email, 10 | secret: user.uid, 11 | email: user.email, 12 | first_name: user.displayName, 13 | }, 14 | { headers: { "Private-Key": "8c63dbce-80a7-455a-890b-9368d16e1dcb" } } 15 | ); 16 | }); 17 | 18 | exports.deleteChatEngineUser = functions.auth.user().onDelete((user) => { 19 | axios.delete("https://api.chatengine.io/users/me/", { 20 | headers: { 21 | "Project-ID": "794653df-052a-4ad2-b0aa-d6c251a10aef", 22 | "User-Name": user.email, 23 | "User-Secret": user.uid, 24 | }, 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /server-firebase/frontend/styles/globals.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0px; 3 | font-family: Söhne,ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,Helvetica Neue,Arial,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji; 4 | font-size: .875rem; 5 | } 6 | .page { 7 | width: 100vw; 8 | height: 100vh; 9 | background-color: rgb(247,247,248); 10 | text-align: center; 11 | display: table-cell; 12 | vertical-align: middle; 13 | } 14 | 15 | /* Auth Page */ 16 | .logo { 17 | font-size: 32px; 18 | padding-bottom: 12px; 19 | } 20 | .text { 21 | padding-bottom: 12px; 22 | } 23 | .button { 24 | background-color: #10a37f; 25 | border: none; 26 | padding: .5rem .75rem; 27 | color: white; 28 | font-size: .875rem; 29 | border-radius: .25rem; 30 | cursor: pointer; 31 | } 32 | .button:hover { 33 | background-color: #10886a; 34 | } -------------------------------------------------------------------------------- /server-firebase/functions/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | es6: true, 5 | node: true, 6 | }, 7 | extends: [ 8 | "eslint:recommended", 9 | "plugin:import/errors", 10 | "plugin:import/warnings", 11 | "plugin:import/typescript", 12 | "google", 13 | "plugin:@typescript-eslint/recommended", 14 | ], 15 | parser: "@typescript-eslint/parser", 16 | parserOptions: { 17 | project: ["tsconfig.json", "tsconfig.dev.json"], 18 | sourceType: "module", 19 | }, 20 | ignorePatterns: [ 21 | "/lib/**/*", // Ignore built files. 22 | ], 23 | plugins: ["@typescript-eslint", "import"], 24 | rules: { 25 | // prettier-ignore 26 | "quotes": ["error", "double"], 27 | "import/no-unresolved": 0, 28 | // prettier-ignore 29 | "indent": ["off", "always"], 30 | "object-curly-spacing": ["off", "always"], 31 | "@typescript-eslint/no-var-requires": ["off", "always"], 32 | }, 33 | }; 34 | -------------------------------------------------------------------------------- /client-html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Welcome 9 | 10 | 11 |
12 |
13 |
14 | Welcome 👋 15 |
16 | 17 |
18 | Select your username to get started 19 |
20 | 21 |
22 |
Username
23 | 24 | 25 |
26 |
27 |
28 | 29 | -------------------------------------------------------------------------------- /server-django/server/urls.py: -------------------------------------------------------------------------------- 1 | """server URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/4.1/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import path 18 | from django.urls import re_path 19 | 20 | 21 | from . import views 22 | 23 | urlpatterns = [ 24 | path('admin/', admin.site.urls), 25 | # Custom endpoints 26 | re_path('login', views.login), 27 | re_path('signup', views.signup), 28 | ] 29 | -------------------------------------------------------------------------------- /server-firebase/functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "functions", 3 | "scripts": { 4 | "lint": "eslint --ext .js,.ts .", 5 | "build": "tsc", 6 | "build:watch": "tsc --watch", 7 | "serve": "npm run build && firebase emulators:start --only functions", 8 | "shell": "npm run build && firebase functions:shell", 9 | "start": "npm run shell", 10 | "deploy": "firebase deploy --only functions", 11 | "logs": "firebase functions:log" 12 | }, 13 | "engines": { 14 | "node": "16" 15 | }, 16 | "main": "lib/index.js", 17 | "dependencies": { 18 | "axios": "^1.3.2", 19 | "firebase-admin": "^10.0.2", 20 | "firebase-functions": "^3.18.0" 21 | }, 22 | "devDependencies": { 23 | "@typescript-eslint/eslint-plugin": "^5.12.0", 24 | "@typescript-eslint/parser": "^5.12.0", 25 | "eslint": "^8.9.0", 26 | "eslint-config-google": "^0.14.0", 27 | "eslint-plugin-import": "^2.25.4", 28 | "firebase-functions-test": "^0.2.0", 29 | "typescript": "^4.5.4" 30 | }, 31 | "private": true 32 | } 33 | -------------------------------------------------------------------------------- /serverless-vue/src/pages/ChatsPage/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | 19 | 44 | -------------------------------------------------------------------------------- /client-react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-pretty", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.5", 7 | "@testing-library/react": "^13.4.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "axios": "^1.2.0", 10 | "react": "^18.2.0", 11 | "react-chat-engine-advanced": "^0.1.25", 12 | "react-chat-engine-pretty": "^0.1.5", 13 | "react-dom": "^18.2.0", 14 | "react-scripts": "5.0.1", 15 | "web-vitals": "^2.1.4" 16 | }, 17 | "scripts": { 18 | "start": "react-scripts start", 19 | "build": "react-scripts build", 20 | "test": "react-scripts test", 21 | "eject": "react-scripts eject" 22 | }, 23 | "eslintConfig": { 24 | "extends": [ 25 | "react-app", 26 | "react-app/jest" 27 | ] 28 | }, 29 | "browserslist": { 30 | "production": [ 31 | ">0.2%", 32 | "not dead", 33 | "not op_mini all" 34 | ], 35 | "development": [ 36 | "last 1 chrome version", 37 | "last 1 firefox version", 38 | "last 1 safari version" 39 | ] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /server-firebase/frontend/public/thirteen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server-flask/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request 2 | from dotenv import load_dotenv 3 | from flask_cors import CORS 4 | import os, requests 5 | 6 | app = Flask(__name__) 7 | CORS(app) 8 | load_dotenv() 9 | 10 | @app.route('/login', methods=['POST']) 11 | def login(): 12 | response = requests.get('https://api.chatengine.io/users/me/', 13 | headers={ 14 | "Project-ID": os.environ['CHAT_ENGINE_PROJECT_ID'], 15 | "User-Name": request.get_json()['username'], 16 | "User-Secret": request.get_json()['secret'] 17 | } 18 | ) 19 | return response.json() 20 | 21 | @app.route('/signup', methods=['POST']) 22 | def signup(): 23 | response = requests.post('https://api.chatengine.io/users/', 24 | data={ 25 | "username": request.get_json()['username'], 26 | "secret": request.get_json()['secret'], 27 | "email": request.get_json()['email'], 28 | "first_name": request.get_json()['first_name'], 29 | "last_name": request.get_json()['last_name'], 30 | }, 31 | headers={ "Private-Key": os.environ['CHAT_ENGINE_PRIVATE_KEY'] } 32 | ) 33 | return response.json() -------------------------------------------------------------------------------- /server-firebase/frontend/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server-fastapi/README.md: -------------------------------------------------------------------------------- 1 | # Connect FastAPI to Chat Engine! 2 | 3 | This simple repo shows how to easily add chat functionality into an Express project with [Chat Engine](https://chatengine.io). 4 | 5 | To understand the code, please watch [this video]()! 6 | 7 | ## Setup Steps 8 | 9 | Make your FastAPI server support chat - in 3 steps: 10 | 11 | ### 1 - Setup a Chat Engine server 12 | 13 | Go to [Chat Engine](https://chatengine.io) to setup your own chat server. 14 | 15 | - Click "New Project" and follow the steps 16 | - Your `Project ID` and `Private Key` will be required for step 2 17 | 18 | ### 2 - Connect `main.py` to Chat Engine 19 | 20 | Replace the following variables with your own Project ID and Private Key. 21 | 22 | (Perferrably as environment variables.) 23 | 24 | ``` 25 | CHAT_ENGINE_PROJECT_ID=5d498a31-cd23-42b7-b367-4fcc9463bd2f 26 | CHAT_ENGINE_PRIVATE_KEY=49a46286-91c3-4f9c-92bf-284ae51b7628 27 | ``` 28 | 29 | ### 3 - Install & Start 30 | 31 | Run the following two lines of code in `server-express/`. 32 | 33 | ``` 34 | python3 -m venv venv 35 | source venv/bin/activate 36 | pip install -r requirements.txt 37 | uvicorn main:app --reload --port 3001 38 | ``` 39 | 40 | Done! Your Express server is on `localhost:3001` and connected to Chat Engine! 41 | 42 | All new `/signup` users are on Chat Engine, and their credentiuals are found upon `/login`. 43 | 44 | To understand the code, please watch [this video]()! 45 | -------------------------------------------------------------------------------- /server-express/README.md: -------------------------------------------------------------------------------- 1 | # Connect Express to Chat Engine! 2 | 3 | This simple repo shows how to easily add chat functionality into an Express project with [Chat Engine](https://chatengine.io). 4 | 5 | To understand the code, please watch [this video]()! 6 | 7 | ## Setup Steps 8 | 9 | Make your express server support chat - in 3 steps: 10 | 11 | ### 1 - Setup a Chat Engine server 12 | 13 | Go to [Chat Engine](https://chatengine.io) to setup your own chat server. 14 | 15 | - Click "New Project" and follow the steps 16 | - Your `Project ID` and `Private Key` will be required for step 2 17 | 18 | ### 2 - Connect `.env` to Chat Engine 19 | 20 | We will connect to your Chat Engine server with environment varibles. 21 | 22 | This allows you to connect to different chat-servers in local vs staging vs production. 23 | 24 | Replace the UUIDs below with your own. In `.env` write: 25 | 26 | ``` 27 | CHAT_ENGINE_PROJECT_ID=5d498a31-cd23-42b7-b367-4fcc9463bd2f 28 | CHAT_ENGINE_PRIVATE_KEY=49a46286-91c3-4f9c-92bf-284ae51b7628 29 | ``` 30 | 31 | ### 3 - Install & Start 32 | 33 | Run the following two lines of code in `server-express/`. 34 | 35 | ``` 36 | npm install 37 | npm run start 38 | ``` 39 | 40 | Done! Your Express server is on `localhost:3001` and connected to Chat Engine! 41 | 42 | All new `/signup` users are on Chat Engine, and their credentiuals are found upon `/login`. 43 | 44 | To understand the code, please watch [this video]()! 45 | -------------------------------------------------------------------------------- /server-firebase/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | firebase-debug.log* 8 | firebase-debug.*.log* 9 | 10 | # Firebase cache 11 | .firebase/ 12 | 13 | # Firebase config 14 | 15 | # Uncomment this if you'd like others to create their own Firebase project. 16 | # For a team working on the same Firebase project(s), it is recommended to leave 17 | # it commented so all members can deploy to the same project(s) in .firebaserc. 18 | # .firebaserc 19 | 20 | # Runtime data 21 | pids 22 | *.pid 23 | *.seed 24 | *.pid.lock 25 | 26 | # Directory for instrumented libs generated by jscoverage/JSCover 27 | lib-cov 28 | 29 | # Coverage directory used by tools like istanbul 30 | coverage 31 | 32 | # nyc test coverage 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 36 | .grunt 37 | 38 | # Bower dependency directory (https://bower.io/) 39 | bower_components 40 | 41 | # node-waf configuration 42 | .lock-wscript 43 | 44 | # Compiled binary addons (http://nodejs.org/api/addons.html) 45 | build/Release 46 | 47 | # Dependency directories 48 | node_modules/ 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Optional REPL history 57 | .node_repl_history 58 | 59 | # Output of 'npm pack' 60 | *.tgz 61 | 62 | # Yarn Integrity file 63 | .yarn-integrity 64 | 65 | # dotenv environment variables file 66 | .env 67 | -------------------------------------------------------------------------------- /serverless-vue/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client-react/README.md: -------------------------------------------------------------------------------- 1 | # Connect React to Chat Engine! 2 | 3 | This simple repo shows how to easily add chat functionality into a React project with [Chat Engine](https://chatengine.io). 4 | 5 | To understand the code, please watch [this video]()! 6 | 7 | ## Setup Steps 8 | 9 | Setup this chat client in 3 steps below. 10 | 11 | These steps assume you have already setup one of the server projects in `../server-*` (for example, `server-express`). 12 | 13 | ### 1 - Setup a Chat Engine server 14 | 15 | Go to [Chat Engine](https://chatengine.io) to setup your own chat server. 16 | 17 | - Click "New Project" and follow the steps 18 | - Your `Project ID` and `Private Key` will be required for step 2 19 | 20 | ### 2 - Connect `.env` to Chat Engine 21 | 22 | We will connect to your Chat Engine server with environment varibles. 23 | 24 | This allows you to connect to different chat-servers in local vs staging vs production. 25 | 26 | Replace the UUID below with your own. In `.env` write: 27 | 28 | ``` 29 | REACT_APP_CHAT_ENGINE_PROJECT_ID=5d498a31-cd23-42b7-b367-4fcc9463bd2f 30 | ``` 31 | 32 | ### 3 - Install & Start 33 | 34 | Run the following two lines of code in `client-react/`. 35 | 36 | ``` 37 | npm install 38 | npm run start 39 | ``` 40 | 41 | Done! Your Express server is on `localhost:3000` and connected to Chat Engine! 42 | 43 | All new "Sign Up" users are on Chat Engine, and their credentiuals are found upon "Login". 44 | 45 | To understand the code, please watch [this video]()! 46 | -------------------------------------------------------------------------------- /server-django/README.md: -------------------------------------------------------------------------------- 1 | # Connect Django to Chat Engine! 2 | 3 | This simple repo shows how to easily add chat functionality into an Express project with [Chat Engine](https://chatengine.io). 4 | 5 | To understand the code, please watch [this video]()! 6 | 7 | ## Setup Steps 8 | 9 | Make your express server support chat - in 3 steps: 10 | 11 | ### 1 - Setup a Chat Engine server 12 | 13 | Go to [Chat Engine](https://chatengine.io) to setup your own chat server. 14 | 15 | - Click "New Project" and follow the steps 16 | - Your `Project ID` and `Private Key` will be required for step 2 17 | 18 | ### 2 - Connect `server/.env` to Chat Engine 19 | 20 | We will connect to your Chat Engine server with environment varibles. 21 | 22 | This allows you to connect to different chat-servers in local vs staging vs production. 23 | 24 | Replace the UUIDs below with your own. In `server/.env` write: 25 | 26 | ``` 27 | CHAT_ENGINE_PROJECT_ID=5d498a31-cd23-42b7-b367-4fcc9463bd2f 28 | CHAT_ENGINE_PRIVATE_KEY=49a46286-91c3-4f9c-92bf-284ae51b7628 29 | ``` 30 | 31 | ### 3 - Install & Start 32 | 33 | Run the following two lines of code in `server-django/`. 34 | 35 | ``` 36 | pip install -r requirements.txt 37 | ./manage.py runserver 0.0.0.0:3001 38 | ``` 39 | 40 | Done! Your Express server is on `localhost:3001` and connected to Chat Engine! 41 | 42 | All new `/signup` users are on Chat Engine, and their credentiuals are found upon `/login`. 43 | 44 | To understand the code, please watch [this video]()! 45 | -------------------------------------------------------------------------------- /server-nodejs/index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const cors = require("cors"); 3 | const axios = require("axios"); 4 | 5 | const app = express(); 6 | app.use(express.json()); 7 | app.use(cors({ origin: true })); 8 | 9 | const CHAT_ENGINE_PROJECT_ID = ""; 10 | const CHAT_ENGINE_PRIVATE_KEY = ""; 11 | 12 | app.post("/signup", async (req, res) => { 13 | const { username, secret, email, first_name, last_name } = req.body; 14 | 15 | // Store a user-copy on Chat Engine! 16 | // Docs at rest.chatengine.io 17 | try { 18 | const r = await axios.post( 19 | "https://api.chatengine.io/users/", 20 | { username, secret, email, first_name, last_name }, 21 | { headers: { "Private-Key": CHAT_ENGINE_PRIVATE_KEY } } 22 | ); 23 | return res.status(r.status).json(r.data); 24 | } catch (e) { 25 | return res.status(e.response.status).json(e.response.data); 26 | } 27 | }); 28 | 29 | app.post("/login", async (req, res) => { 30 | const { username, secret } = req.body; 31 | 32 | // Fetch this user from Chat Engine in this project! 33 | // Docs at rest.chatengine.io 34 | try { 35 | const r = await axios.get("https://api.chatengine.io/users/me/", { 36 | headers: { 37 | "Project-ID": CHAT_ENGINE_PROJECT_ID, 38 | "User-Name": username, 39 | "User-Secret": secret, 40 | }, 41 | }); 42 | return res.status(r.status).json(r.data); 43 | } catch (e) { 44 | return res.status(e.response.status).json(e.response.data); 45 | } 46 | }); 47 | 48 | // vvv On port 3001! 49 | app.listen(3001); 50 | -------------------------------------------------------------------------------- /server-express/index.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | 3 | const express = require("express"); 4 | const cors = require("cors"); 5 | const axios = require("axios"); 6 | 7 | const app = express(); 8 | app.use(express.json()); 9 | app.use(cors({ origin: true })); 10 | 11 | app.post("/signup", async (req, res) => { 12 | const { username, secret, email, first_name, last_name } = req.body; 13 | 14 | // console.log("Write user into DB."); 15 | // return res.json({ user: {} }); 16 | 17 | // Store a user-copy on Chat Engine! 18 | try { 19 | const r = await axios.post( 20 | "https://api.chatengine.io/users/", 21 | { username, secret, email, first_name, last_name }, 22 | { headers: { "Private-Key": process.env.CHAT_ENGINE_PRIVATE_KEY } } 23 | ); 24 | return res.status(r.status).json(r.data); 25 | } catch (e) { 26 | return res.status(e.response.status).json(e.response.data); 27 | } 28 | }); 29 | 30 | app.post("/login", async (req, res) => { 31 | const { username, secret } = req.body; 32 | 33 | // console.log("Fetch user from DB."); 34 | // return res.json({ user: {} }); 35 | 36 | // Fetch this user from Chat Engine in this project! 37 | try { 38 | const r = await axios.get("https://api.chatengine.io/users/me/", { 39 | headers: { 40 | "Project-ID": process.env.CHAT_ENGINE_PROJECT_ID, 41 | "User-Name": username, 42 | "User-Secret": secret, 43 | }, 44 | }); 45 | return res.status(r.status).json(r.data); 46 | } catch (e) { 47 | return res.status(e.response.status).json(e.response.data); 48 | } 49 | }); 50 | 51 | // Docs at rest.chatengine.io 52 | // vvv On port 3001! 53 | app.listen(3001); 54 | -------------------------------------------------------------------------------- /server-fastapi/main.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | from fastapi.middleware.cors import CORSMiddleware 3 | # pip install "uvicorn[standard]" 4 | 5 | from pydantic import BaseModel 6 | from typing import Union 7 | 8 | import requests 9 | 10 | app = FastAPI() 11 | app.add_middleware( 12 | CORSMiddleware, 13 | allow_origins=["*"], 14 | allow_credentials=True, 15 | allow_methods=["*"], 16 | allow_headers=["*"], 17 | ) 18 | 19 | PROJECT_ID = "5d498a31-cd23-42b7-b367-4fcc9463bd2f" 20 | PRIVATE_KEY = "49a46286-91c3-4f9c-92bf-284ae51b7628" 21 | 22 | class User(BaseModel): 23 | username: str 24 | secret: str 25 | email: Union[str, None] = None 26 | first_name: Union[str, None] = None 27 | last_name: Union[str, None] = None 28 | 29 | @app.post('/login/') 30 | async def root(user: User): 31 | response = requests.get('https://api.chatengine.io/users/me/', 32 | headers={ 33 | "Project-ID": PROJECT_ID, 34 | "User-Name": user.username, 35 | "User-Secret": user.secret 36 | } 37 | ) 38 | return response.json() 39 | 40 | @app.post('/signup/') 41 | async def root(user: User): 42 | response = requests.post('https://api.chatengine.io/users/', 43 | data={ 44 | "username": user.username, 45 | "secret": user.secret, 46 | "email": user.email, 47 | "first_name": user.first_name, 48 | "last_name": user.last_name, 49 | }, 50 | headers={ "Private-Key": PRIVATE_KEY } 51 | ) 52 | return response.json() 53 | 54 | # python3 -m venv venv 55 | # source venv/bin/activate 56 | # pip install --upgrade pip 57 | # pip install -r requirements.txt 58 | # uvicorn main:app --reload --port 3001 59 | -------------------------------------------------------------------------------- /server-spring/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.0.0 9 | 10 | 11 | com.example.chatengine 12 | server-spring 13 | 0.0.1-SNAPSHOT 14 | server-spring 15 | Demo chat project for Spring Boot 16 | 17 | 19 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-web 23 | 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-test 28 | test 29 | 30 | 31 | 32 | org.json 33 | json 34 | 20180130 35 | 36 | 37 | 38 | com.google.code.gson 39 | gson 40 | 2.8.9 41 | 42 | 43 | 44 | 45 | 46 | 47 | org.springframework.boot 48 | spring-boot-maven-plugin 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /server-django/server/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework import status 2 | from rest_framework.decorators import api_view 3 | from rest_framework.response import Response 4 | 5 | import requests 6 | 7 | import environ 8 | env = environ.Env() 9 | environ.Env.read_env() 10 | 11 | @api_view(['POST']) 12 | def signup(request): 13 | username = request.data['username'] 14 | secret = request.data['secret'] 15 | email = request.data['email'] 16 | first_name = request.data['first_name'] 17 | last_name = request.data['last_name'] 18 | 19 | # user = User.objects.create( 20 | # username=username, 21 | # email=email, 22 | # password=make_password(secret), 23 | # first_name=first_name, 24 | # last_name=last_name 25 | # ) 26 | 27 | response = requests.post('https://api.chatengine.io/users/', 28 | data={ 29 | "username": username, 30 | "secret": secret, 31 | "email": email, 32 | "first_name": first_name, 33 | "last_name": last_name, 34 | }, 35 | headers={ "Private-Key": env('CHAT_ENGINE_PRIVATE_KEY') } 36 | ) 37 | 38 | return Response(response.json(), status=response.status_code) 39 | 40 | @api_view(['POST']) 41 | def login(request): 42 | username = request.data['username'] 43 | secret = request.data['secret'] 44 | 45 | # user = get_object_or_404(User, username=username) 46 | # if not user.check_password(secret): 47 | # return Response({}, status=status.HTTP_404_NOT_FOUND) 48 | 49 | response = requests.get('https://api.chatengine.io/users/me/', 50 | headers={ 51 | "Project-ID": env('CHAT_ENGINE_PROJECT_ID'), 52 | "User-Name": username, 53 | "User-Secret": secret 54 | } 55 | ) 56 | 57 | return Response(response.json(), status=response.status_code) -------------------------------------------------------------------------------- /client-react/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /server-firebase/frontend/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | ``` 14 | 15 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 16 | 17 | You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file. 18 | 19 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. 20 | 21 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 22 | 23 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. 24 | 25 | ## Learn More 26 | 27 | To learn more about Next.js, take a look at the following resources: 28 | 29 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 30 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 31 | 32 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 33 | 34 | ## Deploy on Vercel 35 | 36 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 37 | 38 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 39 | -------------------------------------------------------------------------------- /client-html/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-image: url('valley.jpeg'); 3 | background-repeat: no-repeat; 4 | background-size: cover; 5 | height: 100vh; 6 | width: 100vw; 7 | margin: 0px; 8 | } 9 | 10 | .background { 11 | width: 100vw; 12 | height: 100vh; 13 | background: linear-gradient(75deg, rgb(40,43,54) 0%, rgb(40,43,54) 50%, rgba(40,43,54,0.8) 100%); 14 | /* Vertical center */ 15 | display: table-cell; 16 | vertical-align: middle; 17 | } 18 | 19 | .form-card { 20 | width: 50%; 21 | max-width: 350px; 22 | padding: 0% 25% 0% 25%; 23 | } 24 | 25 | .form-title { 26 | font-size: 42px; 27 | font-family: 'Avenir'; 28 | font-weight: 800; 29 | letter-spacing: 0.5px; 30 | color: #e8e8e8; 31 | padding-bottom: 12px; 32 | } 33 | 34 | .form-subtitle { 35 | font-size: 18px; 36 | font-family: 'Avenir'; 37 | letter-spacing: 0.5px; 38 | color: #afafaf; 39 | padding-bottom: 24px; 40 | } 41 | 42 | .auth { 43 | position: relative; 44 | display: inline-block; 45 | width: 100%; 46 | padding-bottom: 12px; 47 | } 48 | .auth-label { 49 | position: absolute; 50 | top: 8px; 51 | left: 18px; 52 | font-size: 11px; 53 | color: rgb(175, 175, 175); 54 | font-family: Avenir; 55 | width: 100px; 56 | } 57 | .auth-input { 58 | background-color: #3e404b; 59 | color: white; 60 | font-family: "Avenir"; 61 | outline: none; 62 | border: none; 63 | border-radius: 8px; 64 | padding: 24px 18px 12px 18px; 65 | width: calc(100% - 18px - 18px); 66 | margin-bottom: 12px; 67 | } 68 | .auth-button { 69 | width: 100%; 70 | height: 53px; 71 | color: white; 72 | background-color: #fa541c; 73 | border: none; 74 | outline: none; 75 | border-radius: 8px; 76 | font-family: "Avenir"; 77 | cursor: pointer; 78 | transition: all .44s ease; 79 | -webkit-transition: all .44s ease; 80 | -moz-transition: all .44s ease; 81 | } 82 | .auth-button:hover { filter: brightness(145%); } -------------------------------------------------------------------------------- /serverless-vue/src/pages/AuthPage/index.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 38 | 39 | -------------------------------------------------------------------------------- /client-react/src/authPage.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import axios from "axios"; 3 | 4 | const AuthPage = (props) => { 5 | const [username, setUsername] = useState(); 6 | const [secret, setSecret] = useState(); 7 | const [email, setEmail] = useState(); 8 | const [first_name, setFirstName] = useState(); 9 | const [last_name, setLastName] = useState(); 10 | 11 | const onLogin = (e) => { 12 | e.preventDefault(); 13 | axios 14 | .post("http://localhost:3001/login", { username, secret }) 15 | .then((r) => props.onAuth({ ...r.data, secret })) // NOTE: over-ride secret 16 | .catch((e) => console.log(JSON.stringify(e.response.data))); 17 | }; 18 | 19 | const onSignup = (e) => { 20 | e.preventDefault(); 21 | axios 22 | .post("http://localhost:3001/signup", { 23 | username, 24 | secret, 25 | email, 26 | first_name, 27 | last_name, 28 | }) 29 | .then((r) => props.onAuth({ ...r.data, secret })) // NOTE: over-ride secret 30 | .catch((e) => console.log(JSON.stringify(e.response.data))); 31 | }; 32 | 33 | return ( 34 |
35 |
36 | {/* Login Form */} 37 |
38 |
Login
39 | setUsername(e.target.value)} 44 | /> 45 | setSecret(e.target.value)} 50 | /> 51 | 52 |
53 | 54 | {/* Sign Up Form */} 55 |
56 |
or Sign Up
57 | setUsername(e.target.value)} 62 | /> 63 | setSecret(e.target.value)} 68 | /> 69 | setEmail(e.target.value)} 74 | /> 75 | setFirstName(e.target.value)} 80 | /> 81 | setLastName(e.target.value)} 86 | /> 87 | 88 |
89 |
90 | 91 | 98 |
99 | ); 100 | }; 101 | 102 | export default AuthPage; 103 | -------------------------------------------------------------------------------- /server-django/server/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for server project. 3 | 4 | Generated by 'django-admin startproject' using Django 4.1.3. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.1/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/4.1/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | 15 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 16 | BASE_DIR = Path(__file__).resolve().parent.parent 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = 'django-insecure-@3o&&4rq6kbmkh-6nfu_44(j8mobq(bg+-g(a6(^b(a21bdnys' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = ['*'] 29 | CORS_ORIGIN_ALLOW_ALL = True 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'corsheaders', 35 | 'django.contrib.admin', 36 | 'django.contrib.auth', 37 | 'django.contrib.contenttypes', 38 | 'django.contrib.sessions', 39 | 'django.contrib.messages', 40 | 'django.contrib.staticfiles', 41 | 'rest_framework', 42 | ] 43 | 44 | MIDDLEWARE = [ 45 | 'django.middleware.security.SecurityMiddleware', 46 | 'django.contrib.sessions.middleware.SessionMiddleware', 47 | 'corsheaders.middleware.CorsMiddleware', 48 | 'django.middleware.common.CommonMiddleware', 49 | 'django.middleware.csrf.CsrfViewMiddleware', 50 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 51 | 'django.contrib.messages.middleware.MessageMiddleware', 52 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 53 | ] 54 | 55 | ROOT_URLCONF = 'server.urls' 56 | 57 | TEMPLATES = [ 58 | { 59 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 60 | 'DIRS': [], 61 | 'APP_DIRS': True, 62 | 'OPTIONS': { 63 | 'context_processors': [ 64 | 'django.template.context_processors.debug', 65 | 'django.template.context_processors.request', 66 | 'django.contrib.auth.context_processors.auth', 67 | 'django.contrib.messages.context_processors.messages', 68 | ], 69 | }, 70 | }, 71 | ] 72 | 73 | WSGI_APPLICATION = 'server.wsgi.application' 74 | 75 | 76 | # Database 77 | # https://docs.djangoproject.com/en/4.1/ref/settings/#databases 78 | 79 | DATABASES = { 80 | 'default': { 81 | 'ENGINE': 'django.db.backends.sqlite3', 82 | 'NAME': BASE_DIR / 'db.sqlite3', 83 | } 84 | } 85 | 86 | # Password validation 87 | # https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators 88 | 89 | AUTH_PASSWORD_VALIDATORS = [ 90 | { 91 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 92 | }, 93 | { 94 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 95 | }, 96 | { 97 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 98 | }, 99 | { 100 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 101 | }, 102 | ] 103 | 104 | 105 | # Internationalization 106 | # https://docs.djangoproject.com/en/4.1/topics/i18n/ 107 | 108 | LANGUAGE_CODE = 'en-us' 109 | 110 | TIME_ZONE = 'UTC' 111 | 112 | USE_I18N = True 113 | 114 | USE_TZ = True 115 | 116 | 117 | # Static files (CSS, JavaScript, Images) 118 | # https://docs.djangoproject.com/en/4.1/howto/static-files/ 119 | 120 | STATIC_URL = 'static/' 121 | 122 | # Default primary key field type 123 | # https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field 124 | 125 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 126 | -------------------------------------------------------------------------------- /server-spring/src/main/java/com/example/chatengine/serverspring/UserController.java: -------------------------------------------------------------------------------- 1 | package com.example.chatengine.serverspring; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.InputStreamReader; 5 | import java.io.OutputStream; 6 | import java.net.HttpURLConnection; 7 | import java.net.URL; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | import org.json.JSONObject; 12 | import org.springframework.http.HttpStatus; 13 | import org.springframework.http.ResponseEntity; 14 | import org.springframework.web.bind.annotation.CrossOrigin; 15 | import org.springframework.web.bind.annotation.RequestBody; 16 | import org.springframework.web.bind.annotation.RequestMapping; 17 | import org.springframework.web.bind.annotation.RequestMethod; 18 | import org.springframework.web.bind.annotation.RestController; 19 | 20 | import com.google.gson.Gson; 21 | import com.google.gson.reflect.TypeToken; 22 | 23 | @RestController 24 | public class UserController { 25 | private static String CHAT_ENGINE_PROJECT_ID = "5d498a31-cd23-42b7-b367-4fcc9463bd2f"; 26 | private static String CHAT_ENGINE_PRIVATE_KEY = "49a46286-91c3-4f9c-92bf-284ae51b7628"; 27 | 28 | @CrossOrigin 29 | @RequestMapping(path = "/login", method = RequestMethod.POST) 30 | public ResponseEntity getLogin(@RequestBody HashMap request) { 31 | HttpURLConnection con = null; 32 | try { 33 | // Create GET request 34 | URL url = new URL("https://api.chatengine.io/users/me"); 35 | con = (HttpURLConnection) url.openConnection(); 36 | con.setRequestMethod("GET"); 37 | // Set headers 38 | con.setRequestProperty("Content-Type", "application/json"); 39 | con.setRequestProperty("Accept", "application/json"); 40 | con.setRequestProperty("Project-ID", CHAT_ENGINE_PROJECT_ID); 41 | con.setRequestProperty("User-Name", request.get("username")); 42 | con.setRequestProperty("User-Secret", request.get("secret")); 43 | // Generate response String 44 | StringBuilder responseStr = new StringBuilder(); 45 | try (BufferedReader br = new BufferedReader( 46 | new InputStreamReader(con.getInputStream(), "utf-8"))) { 47 | String responseLine = null; 48 | while ((responseLine = br.readLine()) != null) { 49 | responseStr.append(responseLine.trim()); 50 | } 51 | } 52 | // Jsonify + return response 53 | Map response = new Gson().fromJson( 54 | responseStr.toString(), new TypeToken>() { 55 | }.getType()); 56 | return new ResponseEntity<>(response, HttpStatus.OK); 57 | } catch (Exception e) { 58 | e.printStackTrace(); 59 | return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); 60 | } finally { 61 | if (con != null) { 62 | con.disconnect(); 63 | } 64 | } 65 | } 66 | 67 | @CrossOrigin 68 | @RequestMapping(path = "/signup", method = RequestMethod.POST) 69 | public ResponseEntity newSignup(@RequestBody HashMap request) { 70 | HttpURLConnection con = null; 71 | try { 72 | // Create POST request 73 | URL url = new URL("https://api.chatengine.io/users"); 74 | con = (HttpURLConnection) url.openConnection(); 75 | con.setRequestMethod("POST"); 76 | // Set headers 77 | con.setRequestProperty("Content-Type", "application/json"); 78 | con.setRequestProperty("Accept", "application/json"); 79 | con.setRequestProperty("Private-Key", CHAT_ENGINE_PRIVATE_KEY); 80 | // Add request body 81 | con.setDoOutput(true); 82 | Map body = new HashMap(); 83 | body.put("username", request.get("username")); 84 | body.put("secret", request.get("secret")); 85 | body.put("email", request.get("email")); 86 | body.put("first_name", request.get("first_name")); 87 | body.put("last_name", request.get("last_name")); 88 | String jsonInputString = new JSONObject(body).toString(); 89 | try (OutputStream os = con.getOutputStream()) { 90 | byte[] input = jsonInputString.getBytes("utf-8"); 91 | os.write(input, 0, input.length); 92 | } 93 | // Generate response String 94 | StringBuilder responseStr = new StringBuilder(); 95 | try (BufferedReader br = new BufferedReader( 96 | new InputStreamReader(con.getInputStream(), "utf-8"))) { 97 | String responseLine = null; 98 | while ((responseLine = br.readLine()) != null) { 99 | responseStr.append(responseLine.trim()); 100 | } 101 | } 102 | // Jsonify + return response 103 | Map response = new Gson().fromJson( 104 | responseStr.toString(), new TypeToken>() { 105 | }.getType()); 106 | return new ResponseEntity<>(response, HttpStatus.OK); 107 | } catch (Exception e) { 108 | e.printStackTrace(); 109 | return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST); 110 | } finally { 111 | if (con != null) { 112 | con.disconnect(); 113 | } 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /server-spring/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* 50 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 124 | 125 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% ^ 162 | %JVM_CONFIG_MAVEN_PROPS% ^ 163 | %MAVEN_OPTS% ^ 164 | %MAVEN_DEBUG_OPTS% ^ 165 | -classpath %WRAPPER_JAR% ^ 166 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ 167 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 168 | if ERRORLEVEL 1 goto error 169 | goto end 170 | 171 | :error 172 | set ERROR_CODE=1 173 | 174 | :end 175 | @endlocal & set ERROR_CODE=%ERROR_CODE% 176 | 177 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost 178 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 179 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" 180 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" 181 | :skipRcPost 182 | 183 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 184 | if "%MAVEN_BATCH_PAUSE%"=="on" pause 185 | 186 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% 187 | 188 | cmd /C exit /B %ERROR_CODE% 189 | -------------------------------------------------------------------------------- /server-spring/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /usr/local/etc/mavenrc ] ; then 40 | . /usr/local/etc/mavenrc 41 | fi 42 | 43 | if [ -f /etc/mavenrc ] ; then 44 | . /etc/mavenrc 45 | fi 46 | 47 | if [ -f "$HOME/.mavenrc" ] ; then 48 | . "$HOME/.mavenrc" 49 | fi 50 | 51 | fi 52 | 53 | # OS specific support. $var _must_ be set to either true or false. 54 | cygwin=false; 55 | darwin=false; 56 | mingw=false 57 | case "`uname`" in 58 | CYGWIN*) cygwin=true ;; 59 | MINGW*) mingw=true;; 60 | Darwin*) darwin=true 61 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 62 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 63 | if [ -z "$JAVA_HOME" ]; then 64 | if [ -x "/usr/libexec/java_home" ]; then 65 | export JAVA_HOME="`/usr/libexec/java_home`" 66 | else 67 | export JAVA_HOME="/Library/Java/Home" 68 | fi 69 | fi 70 | ;; 71 | esac 72 | 73 | if [ -z "$JAVA_HOME" ] ; then 74 | if [ -r /etc/gentoo-release ] ; then 75 | JAVA_HOME=`java-config --jre-home` 76 | fi 77 | fi 78 | 79 | if [ -z "$M2_HOME" ] ; then 80 | ## resolve links - $0 may be a link to maven's home 81 | PRG="$0" 82 | 83 | # need this for relative symlinks 84 | while [ -h "$PRG" ] ; do 85 | ls=`ls -ld "$PRG"` 86 | link=`expr "$ls" : '.*-> \(.*\)$'` 87 | if expr "$link" : '/.*' > /dev/null; then 88 | PRG="$link" 89 | else 90 | PRG="`dirname "$PRG"`/$link" 91 | fi 92 | done 93 | 94 | saveddir=`pwd` 95 | 96 | M2_HOME=`dirname "$PRG"`/.. 97 | 98 | # make it fully qualified 99 | M2_HOME=`cd "$M2_HOME" && pwd` 100 | 101 | cd "$saveddir" 102 | # echo Using m2 at $M2_HOME 103 | fi 104 | 105 | # For Cygwin, ensure paths are in UNIX format before anything is touched 106 | if $cygwin ; then 107 | [ -n "$M2_HOME" ] && 108 | M2_HOME=`cygpath --unix "$M2_HOME"` 109 | [ -n "$JAVA_HOME" ] && 110 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 111 | [ -n "$CLASSPATH" ] && 112 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 113 | fi 114 | 115 | # For Mingw, ensure paths are in UNIX format before anything is touched 116 | if $mingw ; then 117 | [ -n "$M2_HOME" ] && 118 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 119 | [ -n "$JAVA_HOME" ] && 120 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 121 | fi 122 | 123 | if [ -z "$JAVA_HOME" ]; then 124 | javaExecutable="`which javac`" 125 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 126 | # readlink(1) is not available as standard on Solaris 10. 127 | readLink=`which readlink` 128 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 129 | if $darwin ; then 130 | javaHome="`dirname \"$javaExecutable\"`" 131 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 132 | else 133 | javaExecutable="`readlink -f \"$javaExecutable\"`" 134 | fi 135 | javaHome="`dirname \"$javaExecutable\"`" 136 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 137 | JAVA_HOME="$javaHome" 138 | export JAVA_HOME 139 | fi 140 | fi 141 | fi 142 | 143 | if [ -z "$JAVACMD" ] ; then 144 | if [ -n "$JAVA_HOME" ] ; then 145 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 146 | # IBM's JDK on AIX uses strange locations for the executables 147 | JAVACMD="$JAVA_HOME/jre/sh/java" 148 | else 149 | JAVACMD="$JAVA_HOME/bin/java" 150 | fi 151 | else 152 | JAVACMD="`\\unset -f command; \\command -v java`" 153 | fi 154 | fi 155 | 156 | if [ ! -x "$JAVACMD" ] ; then 157 | echo "Error: JAVA_HOME is not defined correctly." >&2 158 | echo " We cannot execute $JAVACMD" >&2 159 | exit 1 160 | fi 161 | 162 | if [ -z "$JAVA_HOME" ] ; then 163 | echo "Warning: JAVA_HOME environment variable is not set." 164 | fi 165 | 166 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 167 | 168 | # traverses directory structure from process work directory to filesystem root 169 | # first directory with .mvn subdirectory is considered project base directory 170 | find_maven_basedir() { 171 | 172 | if [ -z "$1" ] 173 | then 174 | echo "Path not specified to find_maven_basedir" 175 | return 1 176 | fi 177 | 178 | basedir="$1" 179 | wdir="$1" 180 | while [ "$wdir" != '/' ] ; do 181 | if [ -d "$wdir"/.mvn ] ; then 182 | basedir=$wdir 183 | break 184 | fi 185 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 186 | if [ -d "${wdir}" ]; then 187 | wdir=`cd "$wdir/.."; pwd` 188 | fi 189 | # end of workaround 190 | done 191 | echo "${basedir}" 192 | } 193 | 194 | # concatenates all lines of a file 195 | concat_lines() { 196 | if [ -f "$1" ]; then 197 | echo "$(tr -s '\n' ' ' < "$1")" 198 | fi 199 | } 200 | 201 | BASE_DIR=`find_maven_basedir "$(pwd)"` 202 | if [ -z "$BASE_DIR" ]; then 203 | exit 1; 204 | fi 205 | 206 | ########################################################################################## 207 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 208 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 209 | ########################################################################################## 210 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 211 | if [ "$MVNW_VERBOSE" = true ]; then 212 | echo "Found .mvn/wrapper/maven-wrapper.jar" 213 | fi 214 | else 215 | if [ "$MVNW_VERBOSE" = true ]; then 216 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 217 | fi 218 | if [ -n "$MVNW_REPOURL" ]; then 219 | jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 220 | else 221 | jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 222 | fi 223 | while IFS="=" read key value; do 224 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 225 | esac 226 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 227 | if [ "$MVNW_VERBOSE" = true ]; then 228 | echo "Downloading from: $jarUrl" 229 | fi 230 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 231 | if $cygwin; then 232 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` 233 | fi 234 | 235 | if command -v wget > /dev/null; then 236 | if [ "$MVNW_VERBOSE" = true ]; then 237 | echo "Found wget ... using wget" 238 | fi 239 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 240 | wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 241 | else 242 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 243 | fi 244 | elif command -v curl > /dev/null; then 245 | if [ "$MVNW_VERBOSE" = true ]; then 246 | echo "Found curl ... using curl" 247 | fi 248 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 249 | curl -o "$wrapperJarPath" "$jarUrl" -f 250 | else 251 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f 252 | fi 253 | 254 | else 255 | if [ "$MVNW_VERBOSE" = true ]; then 256 | echo "Falling back to using Java to download" 257 | fi 258 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 259 | # For Cygwin, switch paths to Windows format before running javac 260 | if $cygwin; then 261 | javaClass=`cygpath --path --windows "$javaClass"` 262 | fi 263 | if [ -e "$javaClass" ]; then 264 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 265 | if [ "$MVNW_VERBOSE" = true ]; then 266 | echo " - Compiling MavenWrapperDownloader.java ..." 267 | fi 268 | # Compiling the Java class 269 | ("$JAVA_HOME/bin/javac" "$javaClass") 270 | fi 271 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 272 | # Running the downloader 273 | if [ "$MVNW_VERBOSE" = true ]; then 274 | echo " - Running MavenWrapperDownloader.java ..." 275 | fi 276 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 277 | fi 278 | fi 279 | fi 280 | fi 281 | ########################################################################################## 282 | # End of extension 283 | ########################################################################################## 284 | 285 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 286 | if [ "$MVNW_VERBOSE" = true ]; then 287 | echo $MAVEN_PROJECTBASEDIR 288 | fi 289 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 290 | 291 | # For Cygwin, switch paths to Windows format before running java 292 | if $cygwin; then 293 | [ -n "$M2_HOME" ] && 294 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 295 | [ -n "$JAVA_HOME" ] && 296 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 297 | [ -n "$CLASSPATH" ] && 298 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 299 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 300 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 301 | fi 302 | 303 | # Provide a "standardized" way to retrieve the CLI args that will 304 | # work with both Windows and non-Windows executions. 305 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 306 | export MAVEN_CMD_LINE_ARGS 307 | 308 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 309 | 310 | exec "$JAVACMD" \ 311 | $MAVEN_OPTS \ 312 | $MAVEN_DEBUG_OPTS \ 313 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 314 | "-Dmaven.home=${M2_HOME}" \ 315 | "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 316 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 317 | -------------------------------------------------------------------------------- /server-firebase/README.md: -------------------------------------------------------------------------------- 1 | # Build a scalable chat app with Firebase, Next JS, and ChatEngine.io 2 | 3 | In this post, we'll be building (and deploying) a scalable, full-stack chat application using Firebase, NextJS, and ChatEngine.io! 4 | 5 | - Firebase is a Backend as a Service (BaaS) plaform which is made by Google. 6 | - NextJS is a Server Side Rendering Framework (SSRF) in React JS 7 | - ChatEngine.io provides chat APIs and components for building chat apps easily. 8 | 9 | Here, we will connect the three to make the amazing chat app above - in about 15 minutes of work! 10 | 11 | ## 1. Setting up a firebase project 12 | 13 | Taking a step back, let's make the folder we'll run this project inside: 14 | 15 | ```bash 16 | mkdir chat-firebase-next 17 | cd chat-firebase-next 18 | code . 19 | ``` 20 | 21 | To setup a firebase project, make sure you have the [Firebase CLI](https://firebase.google.com/docs/cli) installed and a [Firebase account](https://firebase.google.com/). 22 | 23 | Now, open a terminal in this new folder, and start a firebase project with `firebase init` command. Make sure you pick the following options below: 24 | 25 | - Select / enable `functions` 26 | - Select "Create a new project" 27 | - I named my project and ID "fullstack-chat-server" 28 | - I used Javascript, but TypeScript works too 29 | 30 | Now, when you run a free command you should see the following: 31 | 32 | ```bash 33 | . 34 | ├── .firebaserc 35 | ├── .gitignore 36 | ├── firebase.json 37 | └── functions/ 38 | ``` 39 | 40 | I'll list each item below: 41 | 42 | - `.firebaserc` links this folder to your new Firebase project 43 | - `.gitignore` ignores certain files and folders for your repo 44 | - `firebase.json` hosts settings for your local Firebase project 45 | - `functions` is where we'll be writing our backend code ;) 46 | 47 | On that note, let's setup an Express JS server in Cloud Functions. 48 | 49 | ## 2. Coding your backend 50 | 51 | We'll be connect all our Users to Chat Engine so they can use Chat Engine's chat infrastructure. The Chat Engine backend will handle all our web-sockets, chat storage, and everything else. 52 | 53 | We need to make Rest API calls to Chat Engine whenever a new user Signs Up, and delete their account whenever they leave. 54 | 55 | To start, hop in `functions` to install axios and hop back out: 56 | 57 | ```bash 58 | cd functions 59 | npm i axios 60 | cd .. 61 | ``` 62 | 63 | The dependency `axios` will make our API calls to Chat Engine. 64 | 65 | Open the `functions/src/index.js` file in your IDE and the following code: 66 | 67 | ```typescript 68 | import * as functions from "firebase-functions"; 69 | 70 | exports.createChatEngineUser = functions.auth.user().onCreate((user) => { 71 | console.log("create", user); 72 | }); 73 | 74 | exports.deleteChatEngineUser = functions.auth.user().onDelete((user) => { 75 | console.log("delete", user); 76 | }); 77 | ``` 78 | 79 | This will simple state when a user signs up or deleted their account. 80 | 81 | To deploy this, just run `firebase deploy`. You will be prompted to enable Authentication and the Blaze plan if you haven't already. 82 | 83 | I just enabled Google Authentication and followed the steps in the website. 84 | 85 | ```bash 86 | firebase deploy 87 | ``` 88 | 89 | Your backend be good to go! And you should see the following in your functions tab online: 90 | 91 | [TODO: image of firebase console] 92 | 93 | Now, let's connect this backend to ChatEngine.io! 94 | 95 | ## 3. Connect Firebase to Chat Engine 96 | 97 | Chat Engine's APIs let us host chatrooms on their platform, and provide us tools to build pretty chat UIs. We'll be signing up our new Firebase users to Chat Engine automatically here. 98 | 99 | Here is the documentation on Chat Engine's APIs: https://rest.chatengine.io 100 | 101 | In particular, we'll be using their [Create User API](https://rest.chatengine.io/#6bd8427f-d6e0-4f25-8b96-e753aa73f99a) and their [Delete User API](https://rest.chatengine.io/#902cf1ff-3f42-4430-8f0d-3a7067d3213b). 102 | 103 | To start, go to https://chatengine.io, create an account, and set up a new Project. The Private Key and Project ID will be needed for our API calls. 104 | 105 | Now, let's add the code to `functions/src/index.js` and be sure to replace `XXX` and `YYY` with your Private Key and Project ID, respectively. 106 | 107 | ```typescript 108 | import * as functions from "firebase-functions"; 109 | 110 | const axios = require("axios"); 111 | 112 | exports.createChatEngineUser = functions.auth.user().onCreate((user) => { 113 | axios.post( 114 | "https://api.chatengine.io/users/", 115 | { 116 | username: user.email, 117 | secret: user.uid, 118 | email: user.email, 119 | first_name: user.displayName, 120 | }, 121 | { headers: { "Private-Key": "8c63dbce-80a7-455a-890b-9368d16e1dcb" } } 122 | ); 123 | }); 124 | 125 | exports.deleteChatEngineUser = functions.auth.user().onDelete((user) => { 126 | axios.delete("https://api.chatengine.io/users/me/", { 127 | headers: { 128 | "Project-ID": "794653df-052a-4ad2-b0aa-d6c251a10aef", 129 | "User-Name": user.email, 130 | "User-Secret": user.uid, 131 | }, 132 | }); 133 | }); 134 | ``` 135 | 136 | If you're using TypeScript, you may need to modify the rules in `.eslintrc.js` like I did below: 137 | 138 | ```json 139 | rules: { 140 | // prettier-ignore 141 | "quotes": ["error", "double"], 142 | "import/no-unresolved": 0, 143 | // prettier-ignore 144 | "indent": ["off", "always"], 145 | "object-curly-spacing": ["off", "always"], 146 | "@typescript-eslint/no-var-requires": ["off", "always"], 147 | } 148 | ``` 149 | 150 | Our backend is complete! Now users have access to Chat Engine when they sign up and their account goes away is they delete their data! 151 | 152 | Let's setup a frontend and connect it to Firebase and Chat Engine! 153 | 154 | ## 4. Set up the Frontend 155 | 156 | At the top level of our project, we'll run the command below to make a NextJS project. Be sure to select the following too: 157 | 158 | ```bash 159 | npx create-next-app@latest --ts 160 | ``` 161 | 162 | - Name the project "frontend" 163 | - "No" for using .eslint 164 | - "No" for using the `src/` directory 165 | - "No" for using the `app/` directory 166 | - "Yes" for using the `@` imports 167 | 168 | I highly recommend TypeScript with Chat Engine, you'll see why soon :) 169 | 170 | Now you should have a `frontend/` folder right next to `functions/`. 171 | 172 | Important side note: Replace `styles/gloabl.css` with [the following code too](https://raw.githubusercontent.com/alamorre/fullstack-chat/main/server-firebase/frontend/styles/globals.css). 173 | 174 | Let's CD into this folder, install dependencies, and run in dev-mode. 175 | 176 | ```bash 177 | cd frontend 178 | npm install 179 | npm run dev 180 | ``` 181 | 182 | And now you should see a pretty website on http://localhost:3000 183 | 184 | For the final push, let's add all the code we need to our frontend! 185 | 186 | ## 5. Coding the Frontend 187 | 188 | To start, diable StrictMode in `next.config.js` and re-run the server with `yarn dev`. 189 | 190 | ```javascript 191 | /** @type {import('next').NextConfig} */ 192 | const nextConfig = { 193 | reactStrictMode: false, 194 | }; 195 | 196 | module.exports = nextConfig; 197 | ``` 198 | 199 | This allows us to connect web-sockets in a NextJS project. 200 | 201 | Next, let's delete the following: 202 | 203 | - `styles/Home.module.css` 204 | - The `pages/api` directory 205 | 206 | Next, let's create an auth page (`pages/AuthPage.tsx`) with the following code 207 | 208 | ```typescript 209 | export default function Page() { 210 | return
; 211 | } 212 | ``` 213 | 214 | Next, let's create an chats page (`pages/ChatsPage.tsx`) with the following code 215 | 216 | - `pages/ChatsPage.tsx` 217 | 218 | ```typescript 219 | import { User } from "firebase/auth"; 220 | 221 | interface ChatProps { 222 | user: User; 223 | } 224 | 225 | export default function Page(props: ChatProps) { 226 | return
; 227 | } 228 | ``` 229 | 230 | Next, add a loading page (`pages/Loading.tsx`) with the following code: 231 | 232 | ```typescript 233 | export default function Loading() { 234 | return ( 235 |
236 |
☝️
237 |
Loading your info...
238 |
239 | ); 240 | } 241 | ``` 242 | 243 | Finally, modify the `pages/index.tsx` file to load the pages based on auth state: 244 | 245 | ```typescript 246 | import { useState } from "react"; 247 | 248 | import AuthPage from "./AuthPage"; 249 | import ChatPage from "./ChatsPage"; 250 | import Loading from "./Loading"; 251 | import { auth } from "@/firebase"; 252 | import { User } from "firebase/auth"; 253 | 254 | export default function Home() { 255 | const [user, setUser] = useState(); 256 | auth.onAuthStateChanged((user) => setUser(user)); 257 | 258 | if (user === undefined) { 259 | return ; 260 | } else if (user === null) { 261 | return ; 262 | } else { 263 | return ; 264 | } 265 | } 266 | ``` 267 | 268 | ### Enable a Firebase app and create frontend/firebase.ts 269 | 270 | - npm install firebase 271 | - create a .env file and replace with your values 272 | 273 | ``` 274 | NEXT_PUBLIC_FIREBASE_API_KEY=AIzaSyDqXgd1AwRdqSCpdrhf_t0-rB1MuE2sd4A 275 | NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=chat-rce.firebaseapp.com 276 | NEXT_PUBLIC_FIREBASE_PROJECT_ID=chat-rce 277 | NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=chat-rce.appspot.com 278 | NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=45443650042 279 | NEXT_PUBLIC_FIREBASE_APP_ID=1:45443650042:web:2598e30783c0e0bd545443 280 | NEXT_PUBLIC_FUNCTIONS_URL=https://us-central1-chat-rce.cloudfunctions.net/v1 281 | NEXT_PUBLIC_CHAT_ENGINE_PROJECT_ID=16b08e85-e4c9-4541-b30e-45e157ec3821 282 | ``` 283 | 284 | Make sure `NEXT_PUBLIC_CHAT_ENGINE_PROJECT_ID` is there with yuor project ID. 285 | 286 | - add this code to firebase.ts 287 | 288 | ``` 289 | import { initializeApp } from "firebase/app"; 290 | import { getAuth } from "firebase/auth"; 291 | 292 | const firebaseConfig = { 293 | apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY, 294 | authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN, 295 | projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID, 296 | storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET, 297 | messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID, 298 | appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID, 299 | }; 300 | 301 | export const app = initializeApp(firebaseConfig); 302 | export const auth = getAuth(app); 303 | ``` 304 | 305 | Now add this code to AuthPage: 306 | 307 | ``` 308 | import { auth } from "@/firebase"; 309 | import { signInWithRedirect, GoogleAuthProvider } from "firebase/auth"; 310 | 311 | export default function AuthPage() { 312 | const onClick = () => { 313 | signInWithRedirect(auth, new GoogleAuthProvider()); 314 | }; 315 | 316 | return ( 317 |
318 |
👋 💬 🤖
319 |
Welcome to ChatRCE
320 |
321 | Log in with your account to continue 322 |
323 | {" "} 326 | 329 |
330 | ); 331 | } 332 | ``` 333 | 334 | Finally, let's connect our new user to Chat Engine using [react-chat-engine-pretty](https://www.npmjs.com/package/react-chat-engine-pretty)! 335 | 336 | Modify `pages/ChatsPage.tsx` with the following code: 337 | 338 | ```typescript 339 | import { auth } from "@/firebase"; 340 | import { signOut, User } from "firebase/auth"; 341 | import { PrettyChatWindow } from "react-chat-engine-pretty"; 342 | interface ChatProps { 343 | user: User; 344 | } 345 | 346 | export default function Page(props: ChatProps) { 347 | return ( 348 |
349 | 355 | 361 |
362 | ); 363 | } 364 | ``` 365 | 366 | And we're done! 367 | -------------------------------------------------------------------------------- /server-nodejs/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server-node", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "server-node", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "axios": "^1.2.0", 13 | "cors": "^2.8.5", 14 | "express": "^4.18.2" 15 | }, 16 | "devDependencies": { 17 | "nodemon": "^2.0.20" 18 | } 19 | }, 20 | "node_modules/abbrev": { 21 | "version": "1.1.1", 22 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", 23 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", 24 | "dev": true 25 | }, 26 | "node_modules/accepts": { 27 | "version": "1.3.8", 28 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 29 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 30 | "dependencies": { 31 | "mime-types": "~2.1.34", 32 | "negotiator": "0.6.3" 33 | }, 34 | "engines": { 35 | "node": ">= 0.6" 36 | } 37 | }, 38 | "node_modules/anymatch": { 39 | "version": "3.1.3", 40 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 41 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 42 | "dev": true, 43 | "dependencies": { 44 | "normalize-path": "^3.0.0", 45 | "picomatch": "^2.0.4" 46 | }, 47 | "engines": { 48 | "node": ">= 8" 49 | } 50 | }, 51 | "node_modules/array-flatten": { 52 | "version": "1.1.1", 53 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 54 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" 55 | }, 56 | "node_modules/asynckit": { 57 | "version": "0.4.0", 58 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 59 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 60 | }, 61 | "node_modules/axios": { 62 | "version": "1.2.2", 63 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.2.tgz", 64 | "integrity": "sha512-bz/J4gS2S3I7mpN/YZfGFTqhXTYzRho8Ay38w2otuuDR322KzFIWm/4W2K6gIwvWaws5n+mnb7D1lN9uD+QH6Q==", 65 | "dependencies": { 66 | "follow-redirects": "^1.15.0", 67 | "form-data": "^4.0.0", 68 | "proxy-from-env": "^1.1.0" 69 | } 70 | }, 71 | "node_modules/balanced-match": { 72 | "version": "1.0.2", 73 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 74 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 75 | "dev": true 76 | }, 77 | "node_modules/binary-extensions": { 78 | "version": "2.2.0", 79 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 80 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 81 | "dev": true, 82 | "engines": { 83 | "node": ">=8" 84 | } 85 | }, 86 | "node_modules/body-parser": { 87 | "version": "1.20.1", 88 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", 89 | "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", 90 | "dependencies": { 91 | "bytes": "3.1.2", 92 | "content-type": "~1.0.4", 93 | "debug": "2.6.9", 94 | "depd": "2.0.0", 95 | "destroy": "1.2.0", 96 | "http-errors": "2.0.0", 97 | "iconv-lite": "0.4.24", 98 | "on-finished": "2.4.1", 99 | "qs": "6.11.0", 100 | "raw-body": "2.5.1", 101 | "type-is": "~1.6.18", 102 | "unpipe": "1.0.0" 103 | }, 104 | "engines": { 105 | "node": ">= 0.8", 106 | "npm": "1.2.8000 || >= 1.4.16" 107 | } 108 | }, 109 | "node_modules/brace-expansion": { 110 | "version": "1.1.11", 111 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 112 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 113 | "dev": true, 114 | "dependencies": { 115 | "balanced-match": "^1.0.0", 116 | "concat-map": "0.0.1" 117 | } 118 | }, 119 | "node_modules/braces": { 120 | "version": "3.0.2", 121 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 122 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 123 | "dev": true, 124 | "dependencies": { 125 | "fill-range": "^7.0.1" 126 | }, 127 | "engines": { 128 | "node": ">=8" 129 | } 130 | }, 131 | "node_modules/bytes": { 132 | "version": "3.1.2", 133 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 134 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", 135 | "engines": { 136 | "node": ">= 0.8" 137 | } 138 | }, 139 | "node_modules/call-bind": { 140 | "version": "1.0.2", 141 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", 142 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", 143 | "dependencies": { 144 | "function-bind": "^1.1.1", 145 | "get-intrinsic": "^1.0.2" 146 | }, 147 | "funding": { 148 | "url": "https://github.com/sponsors/ljharb" 149 | } 150 | }, 151 | "node_modules/chokidar": { 152 | "version": "3.5.3", 153 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 154 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 155 | "dev": true, 156 | "funding": [ 157 | { 158 | "type": "individual", 159 | "url": "https://paulmillr.com/funding/" 160 | } 161 | ], 162 | "dependencies": { 163 | "anymatch": "~3.1.2", 164 | "braces": "~3.0.2", 165 | "glob-parent": "~5.1.2", 166 | "is-binary-path": "~2.1.0", 167 | "is-glob": "~4.0.1", 168 | "normalize-path": "~3.0.0", 169 | "readdirp": "~3.6.0" 170 | }, 171 | "engines": { 172 | "node": ">= 8.10.0" 173 | }, 174 | "optionalDependencies": { 175 | "fsevents": "~2.3.2" 176 | } 177 | }, 178 | "node_modules/combined-stream": { 179 | "version": "1.0.8", 180 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 181 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 182 | "dependencies": { 183 | "delayed-stream": "~1.0.0" 184 | }, 185 | "engines": { 186 | "node": ">= 0.8" 187 | } 188 | }, 189 | "node_modules/concat-map": { 190 | "version": "0.0.1", 191 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 192 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 193 | "dev": true 194 | }, 195 | "node_modules/content-disposition": { 196 | "version": "0.5.4", 197 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", 198 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", 199 | "dependencies": { 200 | "safe-buffer": "5.2.1" 201 | }, 202 | "engines": { 203 | "node": ">= 0.6" 204 | } 205 | }, 206 | "node_modules/content-type": { 207 | "version": "1.0.4", 208 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 209 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", 210 | "engines": { 211 | "node": ">= 0.6" 212 | } 213 | }, 214 | "node_modules/cookie": { 215 | "version": "0.5.0", 216 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", 217 | "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", 218 | "engines": { 219 | "node": ">= 0.6" 220 | } 221 | }, 222 | "node_modules/cookie-signature": { 223 | "version": "1.0.6", 224 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 225 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" 226 | }, 227 | "node_modules/cors": { 228 | "version": "2.8.5", 229 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 230 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 231 | "dependencies": { 232 | "object-assign": "^4", 233 | "vary": "^1" 234 | }, 235 | "engines": { 236 | "node": ">= 0.10" 237 | } 238 | }, 239 | "node_modules/debug": { 240 | "version": "2.6.9", 241 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 242 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 243 | "dependencies": { 244 | "ms": "2.0.0" 245 | } 246 | }, 247 | "node_modules/delayed-stream": { 248 | "version": "1.0.0", 249 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 250 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 251 | "engines": { 252 | "node": ">=0.4.0" 253 | } 254 | }, 255 | "node_modules/depd": { 256 | "version": "2.0.0", 257 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 258 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", 259 | "engines": { 260 | "node": ">= 0.8" 261 | } 262 | }, 263 | "node_modules/destroy": { 264 | "version": "1.2.0", 265 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", 266 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", 267 | "engines": { 268 | "node": ">= 0.8", 269 | "npm": "1.2.8000 || >= 1.4.16" 270 | } 271 | }, 272 | "node_modules/ee-first": { 273 | "version": "1.1.1", 274 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 275 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" 276 | }, 277 | "node_modules/encodeurl": { 278 | "version": "1.0.2", 279 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 280 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", 281 | "engines": { 282 | "node": ">= 0.8" 283 | } 284 | }, 285 | "node_modules/escape-html": { 286 | "version": "1.0.3", 287 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 288 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" 289 | }, 290 | "node_modules/etag": { 291 | "version": "1.8.1", 292 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 293 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", 294 | "engines": { 295 | "node": ">= 0.6" 296 | } 297 | }, 298 | "node_modules/express": { 299 | "version": "4.18.2", 300 | "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", 301 | "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", 302 | "dependencies": { 303 | "accepts": "~1.3.8", 304 | "array-flatten": "1.1.1", 305 | "body-parser": "1.20.1", 306 | "content-disposition": "0.5.4", 307 | "content-type": "~1.0.4", 308 | "cookie": "0.5.0", 309 | "cookie-signature": "1.0.6", 310 | "debug": "2.6.9", 311 | "depd": "2.0.0", 312 | "encodeurl": "~1.0.2", 313 | "escape-html": "~1.0.3", 314 | "etag": "~1.8.1", 315 | "finalhandler": "1.2.0", 316 | "fresh": "0.5.2", 317 | "http-errors": "2.0.0", 318 | "merge-descriptors": "1.0.1", 319 | "methods": "~1.1.2", 320 | "on-finished": "2.4.1", 321 | "parseurl": "~1.3.3", 322 | "path-to-regexp": "0.1.7", 323 | "proxy-addr": "~2.0.7", 324 | "qs": "6.11.0", 325 | "range-parser": "~1.2.1", 326 | "safe-buffer": "5.2.1", 327 | "send": "0.18.0", 328 | "serve-static": "1.15.0", 329 | "setprototypeof": "1.2.0", 330 | "statuses": "2.0.1", 331 | "type-is": "~1.6.18", 332 | "utils-merge": "1.0.1", 333 | "vary": "~1.1.2" 334 | }, 335 | "engines": { 336 | "node": ">= 0.10.0" 337 | } 338 | }, 339 | "node_modules/fill-range": { 340 | "version": "7.0.1", 341 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 342 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 343 | "dev": true, 344 | "dependencies": { 345 | "to-regex-range": "^5.0.1" 346 | }, 347 | "engines": { 348 | "node": ">=8" 349 | } 350 | }, 351 | "node_modules/finalhandler": { 352 | "version": "1.2.0", 353 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", 354 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", 355 | "dependencies": { 356 | "debug": "2.6.9", 357 | "encodeurl": "~1.0.2", 358 | "escape-html": "~1.0.3", 359 | "on-finished": "2.4.1", 360 | "parseurl": "~1.3.3", 361 | "statuses": "2.0.1", 362 | "unpipe": "~1.0.0" 363 | }, 364 | "engines": { 365 | "node": ">= 0.8" 366 | } 367 | }, 368 | "node_modules/follow-redirects": { 369 | "version": "1.15.2", 370 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", 371 | "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", 372 | "funding": [ 373 | { 374 | "type": "individual", 375 | "url": "https://github.com/sponsors/RubenVerborgh" 376 | } 377 | ], 378 | "engines": { 379 | "node": ">=4.0" 380 | }, 381 | "peerDependenciesMeta": { 382 | "debug": { 383 | "optional": true 384 | } 385 | } 386 | }, 387 | "node_modules/form-data": { 388 | "version": "4.0.0", 389 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 390 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 391 | "dependencies": { 392 | "asynckit": "^0.4.0", 393 | "combined-stream": "^1.0.8", 394 | "mime-types": "^2.1.12" 395 | }, 396 | "engines": { 397 | "node": ">= 6" 398 | } 399 | }, 400 | "node_modules/forwarded": { 401 | "version": "0.2.0", 402 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 403 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", 404 | "engines": { 405 | "node": ">= 0.6" 406 | } 407 | }, 408 | "node_modules/fresh": { 409 | "version": "0.5.2", 410 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 411 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", 412 | "engines": { 413 | "node": ">= 0.6" 414 | } 415 | }, 416 | "node_modules/fsevents": { 417 | "version": "2.3.2", 418 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 419 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 420 | "dev": true, 421 | "hasInstallScript": true, 422 | "optional": true, 423 | "os": [ 424 | "darwin" 425 | ], 426 | "engines": { 427 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 428 | } 429 | }, 430 | "node_modules/function-bind": { 431 | "version": "1.1.1", 432 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 433 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 434 | }, 435 | "node_modules/get-intrinsic": { 436 | "version": "1.1.3", 437 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", 438 | "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", 439 | "dependencies": { 440 | "function-bind": "^1.1.1", 441 | "has": "^1.0.3", 442 | "has-symbols": "^1.0.3" 443 | }, 444 | "funding": { 445 | "url": "https://github.com/sponsors/ljharb" 446 | } 447 | }, 448 | "node_modules/glob-parent": { 449 | "version": "5.1.2", 450 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 451 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 452 | "dev": true, 453 | "dependencies": { 454 | "is-glob": "^4.0.1" 455 | }, 456 | "engines": { 457 | "node": ">= 6" 458 | } 459 | }, 460 | "node_modules/has": { 461 | "version": "1.0.3", 462 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 463 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 464 | "dependencies": { 465 | "function-bind": "^1.1.1" 466 | }, 467 | "engines": { 468 | "node": ">= 0.4.0" 469 | } 470 | }, 471 | "node_modules/has-flag": { 472 | "version": "3.0.0", 473 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 474 | "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", 475 | "dev": true, 476 | "engines": { 477 | "node": ">=4" 478 | } 479 | }, 480 | "node_modules/has-symbols": { 481 | "version": "1.0.3", 482 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 483 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", 484 | "engines": { 485 | "node": ">= 0.4" 486 | }, 487 | "funding": { 488 | "url": "https://github.com/sponsors/ljharb" 489 | } 490 | }, 491 | "node_modules/http-errors": { 492 | "version": "2.0.0", 493 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 494 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 495 | "dependencies": { 496 | "depd": "2.0.0", 497 | "inherits": "2.0.4", 498 | "setprototypeof": "1.2.0", 499 | "statuses": "2.0.1", 500 | "toidentifier": "1.0.1" 501 | }, 502 | "engines": { 503 | "node": ">= 0.8" 504 | } 505 | }, 506 | "node_modules/iconv-lite": { 507 | "version": "0.4.24", 508 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 509 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 510 | "dependencies": { 511 | "safer-buffer": ">= 2.1.2 < 3" 512 | }, 513 | "engines": { 514 | "node": ">=0.10.0" 515 | } 516 | }, 517 | "node_modules/ignore-by-default": { 518 | "version": "1.0.1", 519 | "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", 520 | "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", 521 | "dev": true 522 | }, 523 | "node_modules/inherits": { 524 | "version": "2.0.4", 525 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 526 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 527 | }, 528 | "node_modules/ipaddr.js": { 529 | "version": "1.9.1", 530 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 531 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", 532 | "engines": { 533 | "node": ">= 0.10" 534 | } 535 | }, 536 | "node_modules/is-binary-path": { 537 | "version": "2.1.0", 538 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 539 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 540 | "dev": true, 541 | "dependencies": { 542 | "binary-extensions": "^2.0.0" 543 | }, 544 | "engines": { 545 | "node": ">=8" 546 | } 547 | }, 548 | "node_modules/is-extglob": { 549 | "version": "2.1.1", 550 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 551 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 552 | "dev": true, 553 | "engines": { 554 | "node": ">=0.10.0" 555 | } 556 | }, 557 | "node_modules/is-glob": { 558 | "version": "4.0.3", 559 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 560 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 561 | "dev": true, 562 | "dependencies": { 563 | "is-extglob": "^2.1.1" 564 | }, 565 | "engines": { 566 | "node": ">=0.10.0" 567 | } 568 | }, 569 | "node_modules/is-number": { 570 | "version": "7.0.0", 571 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 572 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 573 | "dev": true, 574 | "engines": { 575 | "node": ">=0.12.0" 576 | } 577 | }, 578 | "node_modules/media-typer": { 579 | "version": "0.3.0", 580 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 581 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", 582 | "engines": { 583 | "node": ">= 0.6" 584 | } 585 | }, 586 | "node_modules/merge-descriptors": { 587 | "version": "1.0.1", 588 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 589 | "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" 590 | }, 591 | "node_modules/methods": { 592 | "version": "1.1.2", 593 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 594 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", 595 | "engines": { 596 | "node": ">= 0.6" 597 | } 598 | }, 599 | "node_modules/mime": { 600 | "version": "1.6.0", 601 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 602 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 603 | "bin": { 604 | "mime": "cli.js" 605 | }, 606 | "engines": { 607 | "node": ">=4" 608 | } 609 | }, 610 | "node_modules/mime-db": { 611 | "version": "1.52.0", 612 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 613 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 614 | "engines": { 615 | "node": ">= 0.6" 616 | } 617 | }, 618 | "node_modules/mime-types": { 619 | "version": "2.1.35", 620 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 621 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 622 | "dependencies": { 623 | "mime-db": "1.52.0" 624 | }, 625 | "engines": { 626 | "node": ">= 0.6" 627 | } 628 | }, 629 | "node_modules/minimatch": { 630 | "version": "3.1.2", 631 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 632 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 633 | "dev": true, 634 | "dependencies": { 635 | "brace-expansion": "^1.1.7" 636 | }, 637 | "engines": { 638 | "node": "*" 639 | } 640 | }, 641 | "node_modules/ms": { 642 | "version": "2.0.0", 643 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 644 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" 645 | }, 646 | "node_modules/negotiator": { 647 | "version": "0.6.3", 648 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 649 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", 650 | "engines": { 651 | "node": ">= 0.6" 652 | } 653 | }, 654 | "node_modules/nodemon": { 655 | "version": "2.0.20", 656 | "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz", 657 | "integrity": "sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==", 658 | "dev": true, 659 | "dependencies": { 660 | "chokidar": "^3.5.2", 661 | "debug": "^3.2.7", 662 | "ignore-by-default": "^1.0.1", 663 | "minimatch": "^3.1.2", 664 | "pstree.remy": "^1.1.8", 665 | "semver": "^5.7.1", 666 | "simple-update-notifier": "^1.0.7", 667 | "supports-color": "^5.5.0", 668 | "touch": "^3.1.0", 669 | "undefsafe": "^2.0.5" 670 | }, 671 | "bin": { 672 | "nodemon": "bin/nodemon.js" 673 | }, 674 | "engines": { 675 | "node": ">=8.10.0" 676 | }, 677 | "funding": { 678 | "type": "opencollective", 679 | "url": "https://opencollective.com/nodemon" 680 | } 681 | }, 682 | "node_modules/nodemon/node_modules/debug": { 683 | "version": "3.2.7", 684 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", 685 | "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", 686 | "dev": true, 687 | "dependencies": { 688 | "ms": "^2.1.1" 689 | } 690 | }, 691 | "node_modules/nodemon/node_modules/ms": { 692 | "version": "2.1.3", 693 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 694 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 695 | "dev": true 696 | }, 697 | "node_modules/nopt": { 698 | "version": "1.0.10", 699 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", 700 | "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", 701 | "dev": true, 702 | "dependencies": { 703 | "abbrev": "1" 704 | }, 705 | "bin": { 706 | "nopt": "bin/nopt.js" 707 | }, 708 | "engines": { 709 | "node": "*" 710 | } 711 | }, 712 | "node_modules/normalize-path": { 713 | "version": "3.0.0", 714 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 715 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 716 | "dev": true, 717 | "engines": { 718 | "node": ">=0.10.0" 719 | } 720 | }, 721 | "node_modules/object-assign": { 722 | "version": "4.1.1", 723 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 724 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", 725 | "engines": { 726 | "node": ">=0.10.0" 727 | } 728 | }, 729 | "node_modules/object-inspect": { 730 | "version": "1.12.3", 731 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", 732 | "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", 733 | "funding": { 734 | "url": "https://github.com/sponsors/ljharb" 735 | } 736 | }, 737 | "node_modules/on-finished": { 738 | "version": "2.4.1", 739 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", 740 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 741 | "dependencies": { 742 | "ee-first": "1.1.1" 743 | }, 744 | "engines": { 745 | "node": ">= 0.8" 746 | } 747 | }, 748 | "node_modules/parseurl": { 749 | "version": "1.3.3", 750 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 751 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 752 | "engines": { 753 | "node": ">= 0.8" 754 | } 755 | }, 756 | "node_modules/path-to-regexp": { 757 | "version": "0.1.7", 758 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 759 | "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" 760 | }, 761 | "node_modules/picomatch": { 762 | "version": "2.3.1", 763 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 764 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 765 | "dev": true, 766 | "engines": { 767 | "node": ">=8.6" 768 | }, 769 | "funding": { 770 | "url": "https://github.com/sponsors/jonschlinkert" 771 | } 772 | }, 773 | "node_modules/proxy-addr": { 774 | "version": "2.0.7", 775 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 776 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 777 | "dependencies": { 778 | "forwarded": "0.2.0", 779 | "ipaddr.js": "1.9.1" 780 | }, 781 | "engines": { 782 | "node": ">= 0.10" 783 | } 784 | }, 785 | "node_modules/proxy-from-env": { 786 | "version": "1.1.0", 787 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 788 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 789 | }, 790 | "node_modules/pstree.remy": { 791 | "version": "1.1.8", 792 | "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", 793 | "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", 794 | "dev": true 795 | }, 796 | "node_modules/qs": { 797 | "version": "6.11.0", 798 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", 799 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", 800 | "dependencies": { 801 | "side-channel": "^1.0.4" 802 | }, 803 | "engines": { 804 | "node": ">=0.6" 805 | }, 806 | "funding": { 807 | "url": "https://github.com/sponsors/ljharb" 808 | } 809 | }, 810 | "node_modules/range-parser": { 811 | "version": "1.2.1", 812 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 813 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 814 | "engines": { 815 | "node": ">= 0.6" 816 | } 817 | }, 818 | "node_modules/raw-body": { 819 | "version": "2.5.1", 820 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", 821 | "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", 822 | "dependencies": { 823 | "bytes": "3.1.2", 824 | "http-errors": "2.0.0", 825 | "iconv-lite": "0.4.24", 826 | "unpipe": "1.0.0" 827 | }, 828 | "engines": { 829 | "node": ">= 0.8" 830 | } 831 | }, 832 | "node_modules/readdirp": { 833 | "version": "3.6.0", 834 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 835 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 836 | "dev": true, 837 | "dependencies": { 838 | "picomatch": "^2.2.1" 839 | }, 840 | "engines": { 841 | "node": ">=8.10.0" 842 | } 843 | }, 844 | "node_modules/safe-buffer": { 845 | "version": "5.2.1", 846 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 847 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 848 | "funding": [ 849 | { 850 | "type": "github", 851 | "url": "https://github.com/sponsors/feross" 852 | }, 853 | { 854 | "type": "patreon", 855 | "url": "https://www.patreon.com/feross" 856 | }, 857 | { 858 | "type": "consulting", 859 | "url": "https://feross.org/support" 860 | } 861 | ] 862 | }, 863 | "node_modules/safer-buffer": { 864 | "version": "2.1.2", 865 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 866 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 867 | }, 868 | "node_modules/semver": { 869 | "version": "5.7.1", 870 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 871 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", 872 | "dev": true, 873 | "bin": { 874 | "semver": "bin/semver" 875 | } 876 | }, 877 | "node_modules/send": { 878 | "version": "0.18.0", 879 | "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", 880 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", 881 | "dependencies": { 882 | "debug": "2.6.9", 883 | "depd": "2.0.0", 884 | "destroy": "1.2.0", 885 | "encodeurl": "~1.0.2", 886 | "escape-html": "~1.0.3", 887 | "etag": "~1.8.1", 888 | "fresh": "0.5.2", 889 | "http-errors": "2.0.0", 890 | "mime": "1.6.0", 891 | "ms": "2.1.3", 892 | "on-finished": "2.4.1", 893 | "range-parser": "~1.2.1", 894 | "statuses": "2.0.1" 895 | }, 896 | "engines": { 897 | "node": ">= 0.8.0" 898 | } 899 | }, 900 | "node_modules/send/node_modules/ms": { 901 | "version": "2.1.3", 902 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 903 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 904 | }, 905 | "node_modules/serve-static": { 906 | "version": "1.15.0", 907 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", 908 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", 909 | "dependencies": { 910 | "encodeurl": "~1.0.2", 911 | "escape-html": "~1.0.3", 912 | "parseurl": "~1.3.3", 913 | "send": "0.18.0" 914 | }, 915 | "engines": { 916 | "node": ">= 0.8.0" 917 | } 918 | }, 919 | "node_modules/setprototypeof": { 920 | "version": "1.2.0", 921 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 922 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" 923 | }, 924 | "node_modules/side-channel": { 925 | "version": "1.0.4", 926 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", 927 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", 928 | "dependencies": { 929 | "call-bind": "^1.0.0", 930 | "get-intrinsic": "^1.0.2", 931 | "object-inspect": "^1.9.0" 932 | }, 933 | "funding": { 934 | "url": "https://github.com/sponsors/ljharb" 935 | } 936 | }, 937 | "node_modules/simple-update-notifier": { 938 | "version": "1.1.0", 939 | "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", 940 | "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", 941 | "dev": true, 942 | "dependencies": { 943 | "semver": "~7.0.0" 944 | }, 945 | "engines": { 946 | "node": ">=8.10.0" 947 | } 948 | }, 949 | "node_modules/simple-update-notifier/node_modules/semver": { 950 | "version": "7.0.0", 951 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", 952 | "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", 953 | "dev": true, 954 | "bin": { 955 | "semver": "bin/semver.js" 956 | } 957 | }, 958 | "node_modules/statuses": { 959 | "version": "2.0.1", 960 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 961 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", 962 | "engines": { 963 | "node": ">= 0.8" 964 | } 965 | }, 966 | "node_modules/supports-color": { 967 | "version": "5.5.0", 968 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 969 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 970 | "dev": true, 971 | "dependencies": { 972 | "has-flag": "^3.0.0" 973 | }, 974 | "engines": { 975 | "node": ">=4" 976 | } 977 | }, 978 | "node_modules/to-regex-range": { 979 | "version": "5.0.1", 980 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 981 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 982 | "dev": true, 983 | "dependencies": { 984 | "is-number": "^7.0.0" 985 | }, 986 | "engines": { 987 | "node": ">=8.0" 988 | } 989 | }, 990 | "node_modules/toidentifier": { 991 | "version": "1.0.1", 992 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 993 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", 994 | "engines": { 995 | "node": ">=0.6" 996 | } 997 | }, 998 | "node_modules/touch": { 999 | "version": "3.1.0", 1000 | "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", 1001 | "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", 1002 | "dev": true, 1003 | "dependencies": { 1004 | "nopt": "~1.0.10" 1005 | }, 1006 | "bin": { 1007 | "nodetouch": "bin/nodetouch.js" 1008 | } 1009 | }, 1010 | "node_modules/type-is": { 1011 | "version": "1.6.18", 1012 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 1013 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 1014 | "dependencies": { 1015 | "media-typer": "0.3.0", 1016 | "mime-types": "~2.1.24" 1017 | }, 1018 | "engines": { 1019 | "node": ">= 0.6" 1020 | } 1021 | }, 1022 | "node_modules/undefsafe": { 1023 | "version": "2.0.5", 1024 | "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", 1025 | "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", 1026 | "dev": true 1027 | }, 1028 | "node_modules/unpipe": { 1029 | "version": "1.0.0", 1030 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1031 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", 1032 | "engines": { 1033 | "node": ">= 0.8" 1034 | } 1035 | }, 1036 | "node_modules/utils-merge": { 1037 | "version": "1.0.1", 1038 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 1039 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", 1040 | "engines": { 1041 | "node": ">= 0.4.0" 1042 | } 1043 | }, 1044 | "node_modules/vary": { 1045 | "version": "1.1.2", 1046 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1047 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", 1048 | "engines": { 1049 | "node": ">= 0.8" 1050 | } 1051 | } 1052 | }, 1053 | "dependencies": { 1054 | "abbrev": { 1055 | "version": "1.1.1", 1056 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", 1057 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", 1058 | "dev": true 1059 | }, 1060 | "accepts": { 1061 | "version": "1.3.8", 1062 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 1063 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 1064 | "requires": { 1065 | "mime-types": "~2.1.34", 1066 | "negotiator": "0.6.3" 1067 | } 1068 | }, 1069 | "anymatch": { 1070 | "version": "3.1.3", 1071 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 1072 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 1073 | "dev": true, 1074 | "requires": { 1075 | "normalize-path": "^3.0.0", 1076 | "picomatch": "^2.0.4" 1077 | } 1078 | }, 1079 | "array-flatten": { 1080 | "version": "1.1.1", 1081 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 1082 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" 1083 | }, 1084 | "asynckit": { 1085 | "version": "0.4.0", 1086 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 1087 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 1088 | }, 1089 | "axios": { 1090 | "version": "1.2.2", 1091 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.2.tgz", 1092 | "integrity": "sha512-bz/J4gS2S3I7mpN/YZfGFTqhXTYzRho8Ay38w2otuuDR322KzFIWm/4W2K6gIwvWaws5n+mnb7D1lN9uD+QH6Q==", 1093 | "requires": { 1094 | "follow-redirects": "^1.15.0", 1095 | "form-data": "^4.0.0", 1096 | "proxy-from-env": "^1.1.0" 1097 | } 1098 | }, 1099 | "balanced-match": { 1100 | "version": "1.0.2", 1101 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 1102 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 1103 | "dev": true 1104 | }, 1105 | "binary-extensions": { 1106 | "version": "2.2.0", 1107 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 1108 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 1109 | "dev": true 1110 | }, 1111 | "body-parser": { 1112 | "version": "1.20.1", 1113 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", 1114 | "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", 1115 | "requires": { 1116 | "bytes": "3.1.2", 1117 | "content-type": "~1.0.4", 1118 | "debug": "2.6.9", 1119 | "depd": "2.0.0", 1120 | "destroy": "1.2.0", 1121 | "http-errors": "2.0.0", 1122 | "iconv-lite": "0.4.24", 1123 | "on-finished": "2.4.1", 1124 | "qs": "6.11.0", 1125 | "raw-body": "2.5.1", 1126 | "type-is": "~1.6.18", 1127 | "unpipe": "1.0.0" 1128 | } 1129 | }, 1130 | "brace-expansion": { 1131 | "version": "1.1.11", 1132 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 1133 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 1134 | "dev": true, 1135 | "requires": { 1136 | "balanced-match": "^1.0.0", 1137 | "concat-map": "0.0.1" 1138 | } 1139 | }, 1140 | "braces": { 1141 | "version": "3.0.2", 1142 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 1143 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 1144 | "dev": true, 1145 | "requires": { 1146 | "fill-range": "^7.0.1" 1147 | } 1148 | }, 1149 | "bytes": { 1150 | "version": "3.1.2", 1151 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 1152 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" 1153 | }, 1154 | "call-bind": { 1155 | "version": "1.0.2", 1156 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", 1157 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", 1158 | "requires": { 1159 | "function-bind": "^1.1.1", 1160 | "get-intrinsic": "^1.0.2" 1161 | } 1162 | }, 1163 | "chokidar": { 1164 | "version": "3.5.3", 1165 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", 1166 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", 1167 | "dev": true, 1168 | "requires": { 1169 | "anymatch": "~3.1.2", 1170 | "braces": "~3.0.2", 1171 | "fsevents": "~2.3.2", 1172 | "glob-parent": "~5.1.2", 1173 | "is-binary-path": "~2.1.0", 1174 | "is-glob": "~4.0.1", 1175 | "normalize-path": "~3.0.0", 1176 | "readdirp": "~3.6.0" 1177 | } 1178 | }, 1179 | "combined-stream": { 1180 | "version": "1.0.8", 1181 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 1182 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 1183 | "requires": { 1184 | "delayed-stream": "~1.0.0" 1185 | } 1186 | }, 1187 | "concat-map": { 1188 | "version": "0.0.1", 1189 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 1190 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 1191 | "dev": true 1192 | }, 1193 | "content-disposition": { 1194 | "version": "0.5.4", 1195 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", 1196 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", 1197 | "requires": { 1198 | "safe-buffer": "5.2.1" 1199 | } 1200 | }, 1201 | "content-type": { 1202 | "version": "1.0.4", 1203 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 1204 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 1205 | }, 1206 | "cookie": { 1207 | "version": "0.5.0", 1208 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", 1209 | "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" 1210 | }, 1211 | "cookie-signature": { 1212 | "version": "1.0.6", 1213 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 1214 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" 1215 | }, 1216 | "cors": { 1217 | "version": "2.8.5", 1218 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 1219 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 1220 | "requires": { 1221 | "object-assign": "^4", 1222 | "vary": "^1" 1223 | } 1224 | }, 1225 | "debug": { 1226 | "version": "2.6.9", 1227 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 1228 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 1229 | "requires": { 1230 | "ms": "2.0.0" 1231 | } 1232 | }, 1233 | "delayed-stream": { 1234 | "version": "1.0.0", 1235 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 1236 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" 1237 | }, 1238 | "depd": { 1239 | "version": "2.0.0", 1240 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 1241 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" 1242 | }, 1243 | "destroy": { 1244 | "version": "1.2.0", 1245 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", 1246 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" 1247 | }, 1248 | "ee-first": { 1249 | "version": "1.1.1", 1250 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 1251 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" 1252 | }, 1253 | "encodeurl": { 1254 | "version": "1.0.2", 1255 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 1256 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" 1257 | }, 1258 | "escape-html": { 1259 | "version": "1.0.3", 1260 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 1261 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" 1262 | }, 1263 | "etag": { 1264 | "version": "1.8.1", 1265 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 1266 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" 1267 | }, 1268 | "express": { 1269 | "version": "4.18.2", 1270 | "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", 1271 | "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", 1272 | "requires": { 1273 | "accepts": "~1.3.8", 1274 | "array-flatten": "1.1.1", 1275 | "body-parser": "1.20.1", 1276 | "content-disposition": "0.5.4", 1277 | "content-type": "~1.0.4", 1278 | "cookie": "0.5.0", 1279 | "cookie-signature": "1.0.6", 1280 | "debug": "2.6.9", 1281 | "depd": "2.0.0", 1282 | "encodeurl": "~1.0.2", 1283 | "escape-html": "~1.0.3", 1284 | "etag": "~1.8.1", 1285 | "finalhandler": "1.2.0", 1286 | "fresh": "0.5.2", 1287 | "http-errors": "2.0.0", 1288 | "merge-descriptors": "1.0.1", 1289 | "methods": "~1.1.2", 1290 | "on-finished": "2.4.1", 1291 | "parseurl": "~1.3.3", 1292 | "path-to-regexp": "0.1.7", 1293 | "proxy-addr": "~2.0.7", 1294 | "qs": "6.11.0", 1295 | "range-parser": "~1.2.1", 1296 | "safe-buffer": "5.2.1", 1297 | "send": "0.18.0", 1298 | "serve-static": "1.15.0", 1299 | "setprototypeof": "1.2.0", 1300 | "statuses": "2.0.1", 1301 | "type-is": "~1.6.18", 1302 | "utils-merge": "1.0.1", 1303 | "vary": "~1.1.2" 1304 | } 1305 | }, 1306 | "fill-range": { 1307 | "version": "7.0.1", 1308 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 1309 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 1310 | "dev": true, 1311 | "requires": { 1312 | "to-regex-range": "^5.0.1" 1313 | } 1314 | }, 1315 | "finalhandler": { 1316 | "version": "1.2.0", 1317 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", 1318 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", 1319 | "requires": { 1320 | "debug": "2.6.9", 1321 | "encodeurl": "~1.0.2", 1322 | "escape-html": "~1.0.3", 1323 | "on-finished": "2.4.1", 1324 | "parseurl": "~1.3.3", 1325 | "statuses": "2.0.1", 1326 | "unpipe": "~1.0.0" 1327 | } 1328 | }, 1329 | "follow-redirects": { 1330 | "version": "1.15.2", 1331 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", 1332 | "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" 1333 | }, 1334 | "form-data": { 1335 | "version": "4.0.0", 1336 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 1337 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 1338 | "requires": { 1339 | "asynckit": "^0.4.0", 1340 | "combined-stream": "^1.0.8", 1341 | "mime-types": "^2.1.12" 1342 | } 1343 | }, 1344 | "forwarded": { 1345 | "version": "0.2.0", 1346 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 1347 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" 1348 | }, 1349 | "fresh": { 1350 | "version": "0.5.2", 1351 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 1352 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" 1353 | }, 1354 | "fsevents": { 1355 | "version": "2.3.2", 1356 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 1357 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 1358 | "dev": true, 1359 | "optional": true 1360 | }, 1361 | "function-bind": { 1362 | "version": "1.1.1", 1363 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 1364 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 1365 | }, 1366 | "get-intrinsic": { 1367 | "version": "1.1.3", 1368 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", 1369 | "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", 1370 | "requires": { 1371 | "function-bind": "^1.1.1", 1372 | "has": "^1.0.3", 1373 | "has-symbols": "^1.0.3" 1374 | } 1375 | }, 1376 | "glob-parent": { 1377 | "version": "5.1.2", 1378 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 1379 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 1380 | "dev": true, 1381 | "requires": { 1382 | "is-glob": "^4.0.1" 1383 | } 1384 | }, 1385 | "has": { 1386 | "version": "1.0.3", 1387 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 1388 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 1389 | "requires": { 1390 | "function-bind": "^1.1.1" 1391 | } 1392 | }, 1393 | "has-flag": { 1394 | "version": "3.0.0", 1395 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 1396 | "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", 1397 | "dev": true 1398 | }, 1399 | "has-symbols": { 1400 | "version": "1.0.3", 1401 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 1402 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" 1403 | }, 1404 | "http-errors": { 1405 | "version": "2.0.0", 1406 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 1407 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 1408 | "requires": { 1409 | "depd": "2.0.0", 1410 | "inherits": "2.0.4", 1411 | "setprototypeof": "1.2.0", 1412 | "statuses": "2.0.1", 1413 | "toidentifier": "1.0.1" 1414 | } 1415 | }, 1416 | "iconv-lite": { 1417 | "version": "0.4.24", 1418 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 1419 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 1420 | "requires": { 1421 | "safer-buffer": ">= 2.1.2 < 3" 1422 | } 1423 | }, 1424 | "ignore-by-default": { 1425 | "version": "1.0.1", 1426 | "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", 1427 | "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", 1428 | "dev": true 1429 | }, 1430 | "inherits": { 1431 | "version": "2.0.4", 1432 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1433 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 1434 | }, 1435 | "ipaddr.js": { 1436 | "version": "1.9.1", 1437 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 1438 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" 1439 | }, 1440 | "is-binary-path": { 1441 | "version": "2.1.0", 1442 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 1443 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 1444 | "dev": true, 1445 | "requires": { 1446 | "binary-extensions": "^2.0.0" 1447 | } 1448 | }, 1449 | "is-extglob": { 1450 | "version": "2.1.1", 1451 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1452 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 1453 | "dev": true 1454 | }, 1455 | "is-glob": { 1456 | "version": "4.0.3", 1457 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 1458 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 1459 | "dev": true, 1460 | "requires": { 1461 | "is-extglob": "^2.1.1" 1462 | } 1463 | }, 1464 | "is-number": { 1465 | "version": "7.0.0", 1466 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1467 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 1468 | "dev": true 1469 | }, 1470 | "media-typer": { 1471 | "version": "0.3.0", 1472 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 1473 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" 1474 | }, 1475 | "merge-descriptors": { 1476 | "version": "1.0.1", 1477 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 1478 | "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" 1479 | }, 1480 | "methods": { 1481 | "version": "1.1.2", 1482 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 1483 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" 1484 | }, 1485 | "mime": { 1486 | "version": "1.6.0", 1487 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 1488 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 1489 | }, 1490 | "mime-db": { 1491 | "version": "1.52.0", 1492 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 1493 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" 1494 | }, 1495 | "mime-types": { 1496 | "version": "2.1.35", 1497 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 1498 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 1499 | "requires": { 1500 | "mime-db": "1.52.0" 1501 | } 1502 | }, 1503 | "minimatch": { 1504 | "version": "3.1.2", 1505 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 1506 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 1507 | "dev": true, 1508 | "requires": { 1509 | "brace-expansion": "^1.1.7" 1510 | } 1511 | }, 1512 | "ms": { 1513 | "version": "2.0.0", 1514 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1515 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" 1516 | }, 1517 | "negotiator": { 1518 | "version": "0.6.3", 1519 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 1520 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" 1521 | }, 1522 | "nodemon": { 1523 | "version": "2.0.20", 1524 | "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz", 1525 | "integrity": "sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==", 1526 | "dev": true, 1527 | "requires": { 1528 | "chokidar": "^3.5.2", 1529 | "debug": "^3.2.7", 1530 | "ignore-by-default": "^1.0.1", 1531 | "minimatch": "^3.1.2", 1532 | "pstree.remy": "^1.1.8", 1533 | "semver": "^5.7.1", 1534 | "simple-update-notifier": "^1.0.7", 1535 | "supports-color": "^5.5.0", 1536 | "touch": "^3.1.0", 1537 | "undefsafe": "^2.0.5" 1538 | }, 1539 | "dependencies": { 1540 | "debug": { 1541 | "version": "3.2.7", 1542 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", 1543 | "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", 1544 | "dev": true, 1545 | "requires": { 1546 | "ms": "^2.1.1" 1547 | } 1548 | }, 1549 | "ms": { 1550 | "version": "2.1.3", 1551 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1552 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 1553 | "dev": true 1554 | } 1555 | } 1556 | }, 1557 | "nopt": { 1558 | "version": "1.0.10", 1559 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", 1560 | "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", 1561 | "dev": true, 1562 | "requires": { 1563 | "abbrev": "1" 1564 | } 1565 | }, 1566 | "normalize-path": { 1567 | "version": "3.0.0", 1568 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1569 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 1570 | "dev": true 1571 | }, 1572 | "object-assign": { 1573 | "version": "4.1.1", 1574 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1575 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" 1576 | }, 1577 | "object-inspect": { 1578 | "version": "1.12.3", 1579 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", 1580 | "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" 1581 | }, 1582 | "on-finished": { 1583 | "version": "2.4.1", 1584 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", 1585 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 1586 | "requires": { 1587 | "ee-first": "1.1.1" 1588 | } 1589 | }, 1590 | "parseurl": { 1591 | "version": "1.3.3", 1592 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 1593 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 1594 | }, 1595 | "path-to-regexp": { 1596 | "version": "0.1.7", 1597 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 1598 | "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" 1599 | }, 1600 | "picomatch": { 1601 | "version": "2.3.1", 1602 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1603 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1604 | "dev": true 1605 | }, 1606 | "proxy-addr": { 1607 | "version": "2.0.7", 1608 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 1609 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 1610 | "requires": { 1611 | "forwarded": "0.2.0", 1612 | "ipaddr.js": "1.9.1" 1613 | } 1614 | }, 1615 | "proxy-from-env": { 1616 | "version": "1.1.0", 1617 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 1618 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 1619 | }, 1620 | "pstree.remy": { 1621 | "version": "1.1.8", 1622 | "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", 1623 | "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", 1624 | "dev": true 1625 | }, 1626 | "qs": { 1627 | "version": "6.11.0", 1628 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", 1629 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", 1630 | "requires": { 1631 | "side-channel": "^1.0.4" 1632 | } 1633 | }, 1634 | "range-parser": { 1635 | "version": "1.2.1", 1636 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 1637 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 1638 | }, 1639 | "raw-body": { 1640 | "version": "2.5.1", 1641 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", 1642 | "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", 1643 | "requires": { 1644 | "bytes": "3.1.2", 1645 | "http-errors": "2.0.0", 1646 | "iconv-lite": "0.4.24", 1647 | "unpipe": "1.0.0" 1648 | } 1649 | }, 1650 | "readdirp": { 1651 | "version": "3.6.0", 1652 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 1653 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 1654 | "dev": true, 1655 | "requires": { 1656 | "picomatch": "^2.2.1" 1657 | } 1658 | }, 1659 | "safe-buffer": { 1660 | "version": "5.2.1", 1661 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1662 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 1663 | }, 1664 | "safer-buffer": { 1665 | "version": "2.1.2", 1666 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1667 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1668 | }, 1669 | "semver": { 1670 | "version": "5.7.1", 1671 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 1672 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", 1673 | "dev": true 1674 | }, 1675 | "send": { 1676 | "version": "0.18.0", 1677 | "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", 1678 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", 1679 | "requires": { 1680 | "debug": "2.6.9", 1681 | "depd": "2.0.0", 1682 | "destroy": "1.2.0", 1683 | "encodeurl": "~1.0.2", 1684 | "escape-html": "~1.0.3", 1685 | "etag": "~1.8.1", 1686 | "fresh": "0.5.2", 1687 | "http-errors": "2.0.0", 1688 | "mime": "1.6.0", 1689 | "ms": "2.1.3", 1690 | "on-finished": "2.4.1", 1691 | "range-parser": "~1.2.1", 1692 | "statuses": "2.0.1" 1693 | }, 1694 | "dependencies": { 1695 | "ms": { 1696 | "version": "2.1.3", 1697 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1698 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 1699 | } 1700 | } 1701 | }, 1702 | "serve-static": { 1703 | "version": "1.15.0", 1704 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", 1705 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", 1706 | "requires": { 1707 | "encodeurl": "~1.0.2", 1708 | "escape-html": "~1.0.3", 1709 | "parseurl": "~1.3.3", 1710 | "send": "0.18.0" 1711 | } 1712 | }, 1713 | "setprototypeof": { 1714 | "version": "1.2.0", 1715 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 1716 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" 1717 | }, 1718 | "side-channel": { 1719 | "version": "1.0.4", 1720 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", 1721 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", 1722 | "requires": { 1723 | "call-bind": "^1.0.0", 1724 | "get-intrinsic": "^1.0.2", 1725 | "object-inspect": "^1.9.0" 1726 | } 1727 | }, 1728 | "simple-update-notifier": { 1729 | "version": "1.1.0", 1730 | "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", 1731 | "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", 1732 | "dev": true, 1733 | "requires": { 1734 | "semver": "~7.0.0" 1735 | }, 1736 | "dependencies": { 1737 | "semver": { 1738 | "version": "7.0.0", 1739 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", 1740 | "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", 1741 | "dev": true 1742 | } 1743 | } 1744 | }, 1745 | "statuses": { 1746 | "version": "2.0.1", 1747 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 1748 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" 1749 | }, 1750 | "supports-color": { 1751 | "version": "5.5.0", 1752 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 1753 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 1754 | "dev": true, 1755 | "requires": { 1756 | "has-flag": "^3.0.0" 1757 | } 1758 | }, 1759 | "to-regex-range": { 1760 | "version": "5.0.1", 1761 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1762 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1763 | "dev": true, 1764 | "requires": { 1765 | "is-number": "^7.0.0" 1766 | } 1767 | }, 1768 | "toidentifier": { 1769 | "version": "1.0.1", 1770 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 1771 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" 1772 | }, 1773 | "touch": { 1774 | "version": "3.1.0", 1775 | "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", 1776 | "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", 1777 | "dev": true, 1778 | "requires": { 1779 | "nopt": "~1.0.10" 1780 | } 1781 | }, 1782 | "type-is": { 1783 | "version": "1.6.18", 1784 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 1785 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 1786 | "requires": { 1787 | "media-typer": "0.3.0", 1788 | "mime-types": "~2.1.24" 1789 | } 1790 | }, 1791 | "undefsafe": { 1792 | "version": "2.0.5", 1793 | "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", 1794 | "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", 1795 | "dev": true 1796 | }, 1797 | "unpipe": { 1798 | "version": "1.0.0", 1799 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1800 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" 1801 | }, 1802 | "utils-merge": { 1803 | "version": "1.0.1", 1804 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 1805 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" 1806 | }, 1807 | "vary": { 1808 | "version": "1.1.2", 1809 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1810 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" 1811 | } 1812 | } 1813 | } 1814 | --------------------------------------------------------------------------------