diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..438657a9e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,25 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+.env
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/ERD/CodeFM Ultra-2023-10-29-162922.png b/ERD/CodeFM Ultra-2023-10-29-162922.png
new file mode 100644
index 000000000..a67c3f99b
Binary files /dev/null and b/ERD/CodeFM Ultra-2023-10-29-162922.png differ
diff --git a/GIFs/CRUD_Backend.gif b/GIFs/CRUD_Backend.gif
new file mode 100644
index 000000000..c2405ad71
Binary files /dev/null and b/GIFs/CRUD_Backend.gif differ
diff --git a/GIFs/CRUD_Frontend.gif b/GIFs/CRUD_Frontend.gif
new file mode 100644
index 000000000..6c3aed7d6
Binary files /dev/null and b/GIFs/CRUD_Frontend.gif differ
diff --git a/GIFs/ExpressbackendandReactFrontend.gif b/GIFs/ExpressbackendandReactFrontend.gif
new file mode 100644
index 000000000..a2f2648eb
Binary files /dev/null and b/GIFs/ExpressbackendandReactFrontend.gif differ
diff --git a/GIFs/Railway Backend.gif b/GIFs/Railway Backend.gif
new file mode 100644
index 000000000..28d93a848
Binary files /dev/null and b/GIFs/Railway Backend.gif differ
diff --git a/README.md b/README.md
index 0e1211217..59e13ffb7 100644
--- a/README.md
+++ b/README.md
@@ -1,49 +1,158 @@
-# [your app name here]
+# CodeFM
-CodePath WEB103 Final Project
+Designed and developed by: Myesha Mahazabeen & Farnaz Zinnah
-Designed and developed by: [your names here]
-
-🔗 Link to deployed app:
+🔗 Link to deployed app: https://codefm-client-production.up.railway.app/
## About
+CodeFM is a React based web app that provides vibrant online community for budding coders and tech enthusiasts. Our platform offers a supportive environment where users can access resources, connect with peers, and embark on their coding journey with confidence
+
### Description and Purpose
-[text goes here]
+- Users can sign up/ log in
+- Users can create, read, update and delete posts, comment and react on others post (CRUD operations, One to Many)
+- Users can access learning resorces posted by others and can add his/her findings as well
+- Users can develop coding or other CS related skills based on preferences (beginner friendly)
+- Can build strong network with like minded peers
+- Newbeies can get guidance from experts and can learn coding from existing resources
+- Users can get insightful information about upcoming events/career fair across the country
+- Personalized learning environment
+
### Inspiration
-[text goes here]
+- Codecademy
+- GitHub
+- CodePath
## Tech Stack
-Frontend:
+Frontend: React JavaScript
+
+Backend: Node.js, Express.js, PostgreSQL
-Backend:
## Features
-### [Name of Feature 1]
+1. **Target Audience**: Focus on college computer science freshmen and sophomores.
+2. **Personalized Learning**: Users can access resources from platforms like YouTube, Leetcode, and FreeCodeCamp.
+3. **Connect with Peers**: Users can engage on discussion boards with CRUD operations.
+
+### Baseline Features:
+
+- ✅ 1. **Express backend and React frontend**: Express for the CRUD operations on the discussion boards and React to display content and allow user interactions.
+
+
+- ✅ 2. **Dynamic Routes**: The discussion board would have dynamic routes for individual posts or discussions. For example, `/posts/:postId`.
+- ✅ 3. **Deployment on Railway**: Railway for deployment.
+
+
+- ✅ 4. **One-to-many database relationship**: Connection between users and their posts on the discussion board. One user can have multiple posts.
+- ✅ 5. **Many-to-many with a join table**: Users can share multiple resources, and a single resource (like a YouTube tutorial). This requires a join table.
+- ✅ 6. **RESTful API**:
+ - **GET**: Fetch a list of posts or resources.
+ - **POST**: Create a new post on the discussion board.
+ - **PATCH**: Edit an existing post.
+ - **DELETE**: Remove a post.
+
+- ✅ 7. **Proper naming conventions for routes**: For instance:
+ - GET: `/api/posts/` to get all posts.
+ - POST: `/api/posts/` to create a new post.
+ - PATCH: `/api/posts/:postId` to edit a specific post.
+ - DELETE: `/api/posts/:postId` to delete a specific post.
+- ✅ 8. **Frontend Redirection**: After submitting a new post, redirect the user back to the list of posts or to their newly created post.
+- ✅ 9. **On-page interactions**: Users can create or edit a post on the same page without navigating to a new page.
+
+
+- ✅ 10. **Dynamic frontend routes with React Router**: Using React Router, you can create dynamic routes like `/posts/:postId` to view a specific post's details.
+- ✅ 11. **Hierarchical React components**: Break down frontend components methodically:
+ - ✅ **Page components**: `Home`, `DiscussionBoard`, `ResourceList`.
+ - ✅ **Presenter components**: `Post`, `Comment`, `ResourceItem`.
+ - ✅ **Container components**: `PostContainer`, `ResourceContainer` (handles logic).
+
+
+### Custom Features:
+
+- ✅ 1. **Data Validation**: Validate any POST or PATCH requests to ensure that users aren't submitting empty or inappropriate content.
+
+- ✅ 2. **Filtering**: Users could have the option to filter resources based on platforms or categories like "YouTube", "Leetcode", or "Articles".
+
+###
+###
+###
+###
+
+## Features to be implemented in the future
+
+### Target Audience
+- **Focused Learning**: The app is specifically designed for college computer science freshman and sophomore students, ensuring content and resources are tailored to their needs.
-[short description goes here]
+### Personalized Learning
+- **Skill Development**: Users can practice/learn coding with various resources from platforms like YouTube, Leetcode, FreeCodeCamp, etc., in different programming languages to improve technical skills and understanding.
+
+### Connect with Peers
+- **Engagement**: Users can join discussions and ask questions in a welcoming community of fellow learners and mentors.
+- **Discussion Boards**: Full CRUD operations where users can create, read, update, and delete their posts. They can also comment and react to posts made by others, fostering a community-driven learning environment.
-[gif goes here]
+### Progress Tracking
+- **Journey Monitoring**: Users can easily monitor their learning journey and see how their learning/engaging skills grow over time.
+- **Achievements**: Users earn badges as they progress, providing visual feedback and motivation.
-### [Name of Feature 2]
+### Network Building
+- **Networking**: Users can connect with like-minded peers, laying the foundation for future collaborations or study groups.
+- **Guidance**: Newbies have the opportunity to seek guidance from senior students and alumni, bridging the knowledge gap.
-[short description goes here]
+### Resource Sharing
+- **Resource Hub**: A dedicated section where users can access resources shared by the community. Users can both consume and add resources, establishing a many-to-many relationship in the database for shared resources.
-[gif goes here]
+### Career Insights
+- **Events & Opportunities**: Users can get information about upcoming events, career fairs, free courses like CodePath, and internships such as Break Through Tech, ensuring they don't miss out on valuable opportunities.
-### [Name of Feature 3]
+
+### Technical Features (Baseline Features)
-[short description goes here]
+- **Integration**:
+ - **Express Backend**: The app uses an Express backend to handle server-side operations.
+ - **React Frontend**: A responsive frontend built with React for an interactive user experience.
-[gif goes here]
+- **Dynamic Routes**: The application includes dynamic routing capabilities for both the frontend and backend, ensuring a seamless user journey.
+
+- **Deployment**: The web application is deployed on Railway with all pages and features fully operational.
+
+- **Database Relationships**:
+ - **One-to-Many**: The app showcases a one-to-many relationship, evident in the interaction between users and their multiple posts.
+ - **Many-to-Many with a Join Table**: [Feature/Implementation details to be added when developed]
+
+- **RESTful API**: Our backend features a robust API:
+ - **Request Types**: Supports GET (read), POST (create), PATCH (update), and DELETE (remove) requests.
+ - **Route Naming**: Proper naming conventions have been followed to maintain clarity and consistency.
+
+- **Database Reset**: The app offers the functionality to reset the database to its default state, aiding in debugging and maintenance.
+
+- **User Experience**:
+ - **Redirection**: Seamless redirections are implemented in several user flows for enhanced navigation.
+ - **On-Page Interactions**: Features such as creating posts are done without needing to reload or navigate away, enhancing user experience.
+
+- **Frontend Routing**: Dynamic frontend routes are crafted using React Router, ensuring appropriate component rendering based on URLs.
+
+- **Hierarchical React Components**:
+ - **Component Categorization**: The frontend's React components are organized methodically into specific categories, facilitating better code understanding and maintenance.
+ - **Component Types**: The app incorporates both container components (handling logic) and presenter components (handling UI), ensuring a clear separation of concerns.
+
+
+### Custom Features
+
+- **Filtering and Sorting**:
+ - **Role-based Resources**: The application provides filtering options tailored to specific roles in the tech industry. Whether a user is interested in software engineering, product management, or UX design, the resources and discussions can be filtered or sorted to cater to their specific needs.
+
+- **Data Validation**:
+ - **Secure Submissions**: Before any data is updated in our database through POST or PATCH requests, the input is validated. For instance:
+ - When a user is discussing topics related to "MCAT" or "Medical School", the system checks for content relevance.
+ - An if-statement ensures that posts have proper categorization. If a user tries to submit without selecting a category, an error is thrown, ensuring that all posts are properly categorized and easily retrievable.
-### [ADDITIONAL FEATURES GO HERE - ADD ALL FEATURES HERE IN THE FORMAT ABOVE; you will check these off and add gifs as you complete them]
## Installation Instructions
-[instructions go here]
+Login on website
+
diff --git a/client/index.html b/client/index.html
new file mode 100644
index 000000000..9c33bdadd
--- /dev/null
+++ b/client/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ CodeFM
+
+
+
+
+
+
diff --git a/client/package-lock.json b/client/package-lock.json
new file mode 100644
index 000000000..ce5eca90e
--- /dev/null
+++ b/client/package-lock.json
@@ -0,0 +1,2576 @@
+{
+ "name": "client",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "client",
+ "version": "1.0.0",
+ "dependencies": {
+ "@fortawesome/free-brands-svg-icons": "^6.4.2",
+ "@fortawesome/free-solid-svg-icons": "^6.4.2",
+ "@fortawesome/react-fontawesome": "^0.2.0",
+ "react": "^18.2.0",
+ "react-axios": "^2.0.6",
+ "react-dom": "^18.2.0",
+ "react-icons": "^4.12.0",
+ "react-modal": "^3.16.1",
+ "react-router-dom": "^6.18.0"
+ },
+ "devDependencies": {
+ "@vitejs/plugin-react": "^4.1.0",
+ "serve": "^14.2.1",
+ "vite": "^4.5.0"
+ }
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
+ "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.22.13",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
+ "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/highlight": "^7.22.13",
+ "chalk": "^2.4.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.23.3",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.3.tgz",
+ "integrity": "sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.23.3",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.3.tgz",
+ "integrity": "sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew==",
+ "dev": true,
+ "dependencies": {
+ "@ampproject/remapping": "^2.2.0",
+ "@babel/code-frame": "^7.22.13",
+ "@babel/generator": "^7.23.3",
+ "@babel/helper-compilation-targets": "^7.22.15",
+ "@babel/helper-module-transforms": "^7.23.3",
+ "@babel/helpers": "^7.23.2",
+ "@babel/parser": "^7.23.3",
+ "@babel/template": "^7.22.15",
+ "@babel/traverse": "^7.23.3",
+ "@babel/types": "^7.23.3",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.23.3",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.3.tgz",
+ "integrity": "sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.23.3",
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "@jridgewell/trace-mapping": "^0.3.17",
+ "jsesc": "^2.5.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.22.15",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz",
+ "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.22.9",
+ "@babel/helper-validator-option": "^7.22.15",
+ "browserslist": "^4.21.9",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-environment-visitor": {
+ "version": "7.22.20",
+ "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
+ "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-function-name": {
+ "version": "7.23.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
+ "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.22.15",
+ "@babel/types": "^7.23.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-hoist-variables": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
+ "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.22.15",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz",
+ "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.22.15"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.23.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz",
+ "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-environment-visitor": "^7.22.20",
+ "@babel/helper-module-imports": "^7.22.15",
+ "@babel/helper-simple-access": "^7.22.5",
+ "@babel/helper-split-export-declaration": "^7.22.6",
+ "@babel/helper-validator-identifier": "^7.22.20"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz",
+ "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-simple-access": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz",
+ "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-split-export-declaration": {
+ "version": "7.22.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
+ "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz",
+ "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.22.20",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
+ "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.22.15",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz",
+ "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.23.2",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz",
+ "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.22.15",
+ "@babel/traverse": "^7.23.2",
+ "@babel/types": "^7.23.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/highlight": {
+ "version": "7.22.20",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
+ "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.22.20",
+ "chalk": "^2.4.2",
+ "js-tokens": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.23.3",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz",
+ "integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==",
+ "dev": true,
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
+ "version": "7.23.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz",
+ "integrity": "sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
+ "version": "7.23.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz",
+ "integrity": "sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.22.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.22.15",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
+ "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.22.13",
+ "@babel/parser": "^7.22.15",
+ "@babel/types": "^7.22.15"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.23.3",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.3.tgz",
+ "integrity": "sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.22.13",
+ "@babel/generator": "^7.23.3",
+ "@babel/helper-environment-visitor": "^7.22.20",
+ "@babel/helper-function-name": "^7.23.0",
+ "@babel/helper-hoist-variables": "^7.22.5",
+ "@babel/helper-split-export-declaration": "^7.22.6",
+ "@babel/parser": "^7.23.3",
+ "@babel/types": "^7.23.3",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.23.3",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz",
+ "integrity": "sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.22.5",
+ "@babel/helper-validator-identifier": "^7.22.20",
+ "to-fast-properties": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
+ "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz",
+ "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz",
+ "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz",
+ "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz",
+ "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz",
+ "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz",
+ "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz",
+ "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz",
+ "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz",
+ "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz",
+ "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz",
+ "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz",
+ "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz",
+ "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz",
+ "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz",
+ "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz",
+ "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz",
+ "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz",
+ "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz",
+ "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz",
+ "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz",
+ "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@fortawesome/fontawesome-common-types": {
+ "version": "6.4.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.2.tgz",
+ "integrity": "sha512-1DgP7f+XQIJbLFCTX1V2QnxVmpLdKdzzo2k8EmvDOePfchaIGQ9eCHj2up3/jNEbZuBqel5OxiaOJf37TWauRA==",
+ "hasInstallScript": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/fontawesome-svg-core": {
+ "version": "6.4.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.4.2.tgz",
+ "integrity": "sha512-gjYDSKv3TrM2sLTOKBc5rH9ckje8Wrwgx1CxAPbN5N3Fm4prfi7NsJVWd1jklp7i5uSCVwhZS5qlhMXqLrpAIg==",
+ "hasInstallScript": true,
+ "peer": true,
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.4.2"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/free-brands-svg-icons": {
+ "version": "6.4.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.4.2.tgz",
+ "integrity": "sha512-LKOwJX0I7+mR/cvvf6qIiqcERbdnY+24zgpUSouySml+5w8B4BJOx8EhDR/FTKAu06W12fmUIcv6lzPSwYKGGg==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.4.2"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/free-solid-svg-icons": {
+ "version": "6.4.2",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.4.2.tgz",
+ "integrity": "sha512-sYwXurXUEQS32fZz9hVCUUv/xu49PEJEyUOsA51l6PU/qVgfbTb2glsTEaJngVVT8VqBATRIdh7XVgV1JF1LkA==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@fortawesome/fontawesome-common-types": "6.4.2"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@fortawesome/react-fontawesome": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz",
+ "integrity": "sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==",
+ "dependencies": {
+ "prop-types": "^15.8.1"
+ },
+ "peerDependencies": {
+ "@fortawesome/fontawesome-svg-core": "~1 || ~6",
+ "react": ">=16.3"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
+ "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/set-array": "^1.0.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
+ "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+ "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.15",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
+ "dev": true
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.20",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz",
+ "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@remix-run/router": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.12.0.tgz",
+ "integrity": "sha512-2hXv036Bux90e1GXTWSMfNzfDDK8LA8JYEWfyHxzvwdp6GyoWEovKc9cotb3KCKmkdwsIBuFGX7ScTWyiHv7Eg==",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.4.tgz",
+ "integrity": "sha512-mLnSC22IC4vcWiuObSRjrLd9XcBTGf59vUSoq2jkQDJ/QQ8PMI9rSuzE+aEV8karUMbskw07bKYoUJCKTUaygg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.6.7",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.7.tgz",
+ "integrity": "sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dev": true,
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.20.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.4.tgz",
+ "integrity": "sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.20.7"
+ }
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.2.0.tgz",
+ "integrity": "sha512-+MHTH/e6H12kRp5HUkzOGqPMksezRMmW+TNzlh/QXfI8rRf6l2Z2yH/v12no1UvTwhZgEDMuQ7g7rrfMseU6FQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/core": "^7.23.3",
+ "@babel/plugin-transform-react-jsx-self": "^7.23.3",
+ "@babel/plugin-transform-react-jsx-source": "^7.23.3",
+ "@types/babel__core": "^7.20.4",
+ "react-refresh": "^0.14.0"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^4.2.0 || ^5.0.0"
+ }
+ },
+ "node_modules/@zeit/schemas": {
+ "version": "2.29.0",
+ "resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.29.0.tgz",
+ "integrity": "sha512-g5QiLIfbg3pLuYUJPlisNKY+epQJTcMDsOnVNkscrDP1oi7vmJnzOANYJI/1pZcVJ6umUkBv3aFtlg1UvUHGzA==",
+ "dev": true
+ },
+ "node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "dev": true,
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
+ "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-align": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
+ "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^4.1.0"
+ }
+ },
+ "node_modules/ansi-align/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-align/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "node_modules/ansi-align/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-align/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/arch": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz",
+ "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/arg": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
+ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
+ "dev": true
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "peer": true
+ },
+ "node_modules/axios": {
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+ "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
+ "peer": true,
+ "dependencies": {
+ "follow-redirects": "^1.14.9",
+ "form-data": "^4.0.0"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "node_modules/boxen": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.0.tgz",
+ "integrity": "sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-align": "^3.0.1",
+ "camelcase": "^7.0.0",
+ "chalk": "^5.0.1",
+ "cli-boxes": "^3.0.0",
+ "string-width": "^5.1.2",
+ "type-fest": "^2.13.0",
+ "widest-line": "^4.0.1",
+ "wrap-ansi": "^8.0.1"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/boxen/node_modules/chalk": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
+ "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
+ "dev": true,
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.22.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz",
+ "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001541",
+ "electron-to-chromium": "^1.4.535",
+ "node-releases": "^2.0.13",
+ "update-browserslist-db": "^1.0.13"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/bytes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+ "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz",
+ "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==",
+ "dev": true,
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001563",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001563.tgz",
+ "integrity": "sha512-na2WUmOxnwIZtwnFI2CZ/3er0wdNzU7hN+cPYz/z2ajHThnkWjNBOpEPP4n+4r2WPM847JaMotaJE3bnfzjyKw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ]
+ },
+ "node_modules/chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/chalk-template": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz",
+ "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.1.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk-template?sponsor=1"
+ }
+ },
+ "node_modules/chalk-template/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/chalk-template/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chalk-template/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/chalk-template/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/chalk-template/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/chalk-template/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cli-boxes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz",
+ "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/clipboardy": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-3.0.0.tgz",
+ "integrity": "sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==",
+ "dev": true,
+ "dependencies": {
+ "arch": "^2.2.0",
+ "execa": "^5.1.1",
+ "is-wsl": "^2.2.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+ "dev": true
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "peer": true,
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/compressible": {
+ "version": "2.0.18",
+ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+ "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+ "dev": true,
+ "dependencies": {
+ "mime-db": ">= 1.43.0 < 2"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/compression": {
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
+ "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
+ "dev": true,
+ "dependencies": {
+ "accepts": "~1.3.5",
+ "bytes": "3.0.0",
+ "compressible": "~2.0.16",
+ "debug": "2.6.9",
+ "on-headers": "~1.0.2",
+ "safe-buffer": "5.1.2",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/compression/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/compression/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "node_modules/content-disposition": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
+ "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "peer": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "dev": true
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.4.588",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.588.tgz",
+ "integrity": "sha512-soytjxwbgcCu7nh5Pf4S2/4wa6UIu+A3p03U2yVr53qGxi1/VTR3ENI+p50v+UxqqZAfl48j3z55ud7VHIOr9w==",
+ "dev": true
+ },
+ "node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "dev": true
+ },
+ "node_modules/esbuild": {
+ "version": "0.18.20",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz",
+ "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/android-arm": "0.18.20",
+ "@esbuild/android-arm64": "0.18.20",
+ "@esbuild/android-x64": "0.18.20",
+ "@esbuild/darwin-arm64": "0.18.20",
+ "@esbuild/darwin-x64": "0.18.20",
+ "@esbuild/freebsd-arm64": "0.18.20",
+ "@esbuild/freebsd-x64": "0.18.20",
+ "@esbuild/linux-arm": "0.18.20",
+ "@esbuild/linux-arm64": "0.18.20",
+ "@esbuild/linux-ia32": "0.18.20",
+ "@esbuild/linux-loong64": "0.18.20",
+ "@esbuild/linux-mips64el": "0.18.20",
+ "@esbuild/linux-ppc64": "0.18.20",
+ "@esbuild/linux-riscv64": "0.18.20",
+ "@esbuild/linux-s390x": "0.18.20",
+ "@esbuild/linux-x64": "0.18.20",
+ "@esbuild/netbsd-x64": "0.18.20",
+ "@esbuild/openbsd-x64": "0.18.20",
+ "@esbuild/sunos-x64": "0.18.20",
+ "@esbuild/win32-arm64": "0.18.20",
+ "@esbuild/win32-ia32": "0.18.20",
+ "@esbuild/win32-x64": "0.18.20"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/execa": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
+ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/exenv": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz",
+ "integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw=="
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "node_modules/fast-url-parser": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz",
+ "integrity": "sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^1.3.2"
+ }
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.15.3",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz",
+ "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "peer": true,
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "peer": true,
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/human-signals": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.17.0"
+ }
+ },
+ "node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true
+ },
+ "node_modules/is-docker": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+ "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
+ "dev": true,
+ "bin": {
+ "is-docker": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-port-reachable": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-port-reachable/-/is-port-reachable-4.0.0.tgz",
+ "integrity": "sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig==",
+ "dev": true,
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-wsl": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+ "dev": true,
+ "dependencies": {
+ "is-docker": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+ },
+ "node_modules/jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "dev": true,
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+ "dev": true
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.7",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
+ "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.13",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz",
+ "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==",
+ "dev": true
+ },
+ "node_modules/npm-run-path": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
+ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/on-headers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+ "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "dependencies": {
+ "mimic-fn": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/path-is-inside": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+ "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==",
+ "dev": true
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-to-regexp": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-2.2.1.tgz",
+ "integrity": "sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==",
+ "dev": true
+ },
+ "node_modules/picocolors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+ "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+ "dev": true
+ },
+ "node_modules/postcss": {
+ "version": "8.4.31",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+ "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "nanoid": "^3.3.6",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/prop-types": {
+ "version": "15.8.1",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+ "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+ "dependencies": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.13.1"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==",
+ "dev": true
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
+ "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/rc": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
+ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+ "dev": true,
+ "dependencies": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "bin": {
+ "rc": "cli.js"
+ }
+ },
+ "node_modules/react": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
+ "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-axios": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/react-axios/-/react-axios-2.0.6.tgz",
+ "integrity": "sha512-srQnLZXaW9LDJyC4/qvQ7aPi/rUpsggd3RIM5Q/vFLlQZ4l5bvPtqP/2+UeRXhJH75NfxbcPD3FkjiKONn5V8Q==",
+ "peerDependencies": {
+ "axios": "^0.27.2",
+ "prop-types": "^15.8.1",
+ "react": "^16.0.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
+ "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.0"
+ },
+ "peerDependencies": {
+ "react": "^18.2.0"
+ }
+ },
+ "node_modules/react-icons": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.12.0.tgz",
+ "integrity": "sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==",
+ "peerDependencies": {
+ "react": "*"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ },
+ "node_modules/react-lifecycles-compat": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
+ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
+ },
+ "node_modules/react-modal": {
+ "version": "3.16.1",
+ "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.16.1.tgz",
+ "integrity": "sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==",
+ "dependencies": {
+ "exenv": "^1.2.0",
+ "prop-types": "^15.7.2",
+ "react-lifecycles-compat": "^3.0.0",
+ "warning": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "peerDependencies": {
+ "react": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18",
+ "react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18"
+ }
+ },
+ "node_modules/react-refresh": {
+ "version": "0.14.0",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
+ "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-router": {
+ "version": "6.19.0",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.19.0.tgz",
+ "integrity": "sha512-0W63PKCZ7+OuQd7Tm+RbkI8kCLmn4GPjDbX61tWljPxWgqTKlEpeQUwPkT1DRjYhF8KSihK0hQpmhU4uxVMcdw==",
+ "dependencies": {
+ "@remix-run/router": "1.12.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8"
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "6.19.0",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.19.0.tgz",
+ "integrity": "sha512-N6dWlcgL2w0U5HZUUqU2wlmOrSb3ighJmtQ438SWbhB1yuLTXQ8yyTBMK3BSvVjp7gBtKurT554nCtMOgxCZmQ==",
+ "dependencies": {
+ "@remix-run/router": "1.12.0",
+ "react-router": "6.19.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8",
+ "react-dom": ">=16.8"
+ }
+ },
+ "node_modules/registry-auth-token": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz",
+ "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==",
+ "dev": true,
+ "dependencies": {
+ "rc": "^1.1.6",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/registry-url": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz",
+ "integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==",
+ "dev": true,
+ "dependencies": {
+ "rc": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "3.29.4",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz",
+ "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==",
+ "dev": true,
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=14.18.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ },
+ "node_modules/scheduler": {
+ "version": "0.23.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
+ "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/serve": {
+ "version": "14.2.1",
+ "resolved": "https://registry.npmjs.org/serve/-/serve-14.2.1.tgz",
+ "integrity": "sha512-48er5fzHh7GCShLnNyPBRPEjs2I6QBozeGr02gaacROiyS/8ARADlj595j39iZXAqBbJHH/ivJJyPRWY9sQWZA==",
+ "dev": true,
+ "dependencies": {
+ "@zeit/schemas": "2.29.0",
+ "ajv": "8.11.0",
+ "arg": "5.0.2",
+ "boxen": "7.0.0",
+ "chalk": "5.0.1",
+ "chalk-template": "0.4.0",
+ "clipboardy": "3.0.0",
+ "compression": "1.7.4",
+ "is-port-reachable": "4.0.0",
+ "serve-handler": "6.1.5",
+ "update-check": "1.5.4"
+ },
+ "bin": {
+ "serve": "build/main.js"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/serve-handler": {
+ "version": "6.1.5",
+ "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.5.tgz",
+ "integrity": "sha512-ijPFle6Hwe8zfmBxJdE+5fta53fdIY0lHISJvuikXB3VYFafRjMRpOffSPvCYsbKyBA7pvy9oYr/BT1O3EArlg==",
+ "dev": true,
+ "dependencies": {
+ "bytes": "3.0.0",
+ "content-disposition": "0.5.2",
+ "fast-url-parser": "1.1.3",
+ "mime-types": "2.1.18",
+ "minimatch": "3.1.2",
+ "path-is-inside": "1.0.2",
+ "path-to-regexp": "2.2.1",
+ "range-parser": "1.2.0"
+ }
+ },
+ "node_modules/serve-handler/node_modules/mime-db": {
+ "version": "1.33.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
+ "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/serve-handler/node_modules/mime-types": {
+ "version": "2.1.18",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
+ "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
+ "dev": true,
+ "dependencies": {
+ "mime-db": "~1.33.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/serve/node_modules/chalk": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz",
+ "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==",
+ "dev": true,
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true
+ },
+ "node_modules/source-map-js": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+ "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "dev": true,
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/strip-final-newline": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "2.19.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
+ "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.0.13",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
+ "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "escalade": "^3.1.1",
+ "picocolors": "^1.0.0"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/update-check": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.4.tgz",
+ "integrity": "sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==",
+ "dev": true,
+ "dependencies": {
+ "registry-auth-token": "3.3.2",
+ "registry-url": "3.1.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/uri-js/node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/vite": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz",
+ "integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==",
+ "dev": true,
+ "dependencies": {
+ "esbuild": "^0.18.10",
+ "postcss": "^8.4.27",
+ "rollup": "^3.27.1"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ },
+ "peerDependencies": {
+ "@types/node": ">= 14",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/warning": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+ "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/widest-line": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz",
+ "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ }
+ }
+}
diff --git a/client/package.json b/client/package.json
new file mode 100644
index 000000000..9231b9c8b
--- /dev/null
+++ b/client/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "client",
+ "version": "1.0.0",
+ "private": true,
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "start": "serve dist -s -n -L -p $PORT"
+ },
+ "dependencies": {
+ "@fortawesome/free-brands-svg-icons": "^6.4.2",
+ "@fortawesome/free-solid-svg-icons": "^6.4.2",
+ "@fortawesome/react-fontawesome": "^0.2.0",
+ "react": "^18.2.0",
+ "react-axios": "^2.0.6",
+ "react-dom": "^18.2.0",
+ "react-icons": "^4.12.0",
+ "react-modal": "^3.16.1",
+ "react-router-dom": "^6.18.0"
+ },
+ "devDependencies": {
+ "@vitejs/plugin-react": "^4.1.0",
+ "vite": "^4.5.0",
+ "serve": "^14.2.1"
+ },
+ "vite": {
+ "server": {
+ "proxy": {
+ "/api": "http://localhost:3001",
+ "/auth": "http://localhost:3001"
+ }
+ }
+ }
+ }
+
\ No newline at end of file
diff --git a/client/public/home.jpeg b/client/public/home.jpeg
new file mode 100644
index 000000000..35990507c
Binary files /dev/null and b/client/public/home.jpeg differ
diff --git a/client/public/logo.PNG b/client/public/logo.PNG
new file mode 100644
index 000000000..260c0fac6
Binary files /dev/null and b/client/public/logo.PNG differ
diff --git a/client/public/vite.svg b/client/public/vite.svg
new file mode 100644
index 000000000..e7b8dfb1b
--- /dev/null
+++ b/client/public/vite.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/client/src/App.css b/client/src/App.css
new file mode 100644
index 000000000..bc6c6ad03
--- /dev/null
+++ b/client/src/App.css
@@ -0,0 +1,10 @@
+.app {
+ background-size: cover;
+ background-position: center;
+ background-attachment: fixed;
+ width: 100vw;
+ height: 100vh;
+ overflow-y: scroll;
+ overflow-x: hidden;
+ padding-bottom: 50px;
+}
\ No newline at end of file
diff --git a/client/src/App.jsx b/client/src/App.jsx
new file mode 100644
index 000000000..5e7a081b7
--- /dev/null
+++ b/client/src/App.jsx
@@ -0,0 +1,45 @@
+// App.jsx
+import React from 'react';
+import { Routes, Route } from 'react-router-dom';
+import Navbar from "./components/Navbar";
+import Home from './pages/Home';
+import DiscussionBoard from './pages/DiscussionBoard';
+import ResourceList from './pages/ResourceList';
+import CreatePost from './pages/CreatePost';
+import CreateResource from './pages/CreateResource';
+import AboutUs from './pages/AboutUs';
+import Events from './pages/Events';
+import './App.css';
+
+import Login from './pages/Login';
+import ProtectedRoute from './components/ProtectedRoute';
+import { AuthProvider } from './contexts/AuthContext';
+import { ApiUrlProvider } from './contexts/ApiContext';
+import PostDetail from './components/PostDetail';
+
+// const API_URL = process.env.NODE_ENV === 'production' ? 'https://codefm-server-production.up.railway.app' : 'http://localhost:3001';
+
+const App = () => {
+ return (
+
+
+
+
+
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+
+
+
+
+ );
+};
+
+export default App;
\ No newline at end of file
diff --git a/client/src/components/Comment.jsx b/client/src/components/Comment.jsx
new file mode 100644
index 000000000..44f8a5f9f
--- /dev/null
+++ b/client/src/components/Comment.jsx
@@ -0,0 +1,12 @@
+import React from 'react';
+
+const Comment = ({ comment, onDelete }) => {
+ return (
+
+
{comment.content}
+
onDelete(comment.id)}>Delete
+
+ );
+};
+
+export default Comment;
diff --git a/client/src/components/Navbar.jsx b/client/src/components/Navbar.jsx
new file mode 100644
index 000000000..52d4a824b
--- /dev/null
+++ b/client/src/components/Navbar.jsx
@@ -0,0 +1,79 @@
+import React, { useEffect } from 'react';
+import { Link } from 'react-router-dom';
+import { useAuth } from '../contexts/AuthContext';
+import { useApiUrl } from '../contexts/ApiContext';
+import axios from 'axios'; // Import axios for making HTTP requests
+import { useNavigate } from 'react-router-dom';
+
+const Navbar = () => {
+ const { isAuthenticated, user, setAuthInfo } = useAuth();
+ const apiUrl = useApiUrl();
+ const navigate = useNavigate();
+ useEffect(() => {
+ // This will cause the component to re-render when isAuthenticated changes
+ }, [isAuthenticated]);
+ const handleLogout = () => {
+ axios.get(`${apiUrl}/auth/logout`, { withCredentials: true })
+ .then(() => {
+ // Update auth state
+ setAuthInfo({ isAuthenticated: false, user: null });
+ })
+ .catch(error => {
+ console.error('Logout failed', error);
+ });
+ };
+
+ const loginButtonStyle = {
+ backgroundColor: 'orange',
+ color: 'white',
+ padding: '5px 10px',
+ borderRadius: '5px',
+ textDecoration: 'none',
+ boxShadow: 'none',
+ };
+
+ return (
+
+ {/*
*/}
+
navigate('/')}
+ style={{ cursor: 'pointer' }}
+ />
+
+
+ About Us
+
+
+ Events
+
+
+ Resources
+
+
+ Discussion Board
+
+
+
+ {isAuthenticated ? (
+
+ {user.username ? (
+ {user.username}
+ ) : (
+ User
+ )}
+ Logout
+
+ ) : (
+
+ Login
+
+ )}
+
+
+ );
+};
+
+export default Navbar;
diff --git a/client/src/components/Post.jsx b/client/src/components/Post.jsx
new file mode 100644
index 000000000..f434df975
--- /dev/null
+++ b/client/src/components/Post.jsx
@@ -0,0 +1,26 @@
+import React from 'react';
+import { Link } from 'react-router-dom';
+
+const Post = ({ post, onDelete, onEdit }) => {
+ const handleDelete = () => {
+ if (typeof onDelete === 'function') {
+ onDelete(post.id);
+ }
+ };
+
+ const handleEdit = () => {
+ if (typeof onEdit === 'function') {
+ onEdit(post.id);
+ }
+ };
+
+ return (
+
+
{post.content}
+ onEdit()}>Edit {/* Call the onEdit function */}
+ onDelete(post.id)}>Delete
+
+ );
+};
+
+export default Post;
diff --git a/client/src/components/PostDetail.jsx b/client/src/components/PostDetail.jsx
new file mode 100644
index 000000000..eb61e4d2a
--- /dev/null
+++ b/client/src/components/PostDetail.jsx
@@ -0,0 +1,101 @@
+import React, { useEffect, useState } from 'react';
+import axios from 'axios';
+import { getAllPosts, updatePost } from '../services/postService';
+import Post from '../components/Post';
+import EditPostModal from '../pages/EditPostModal';
+import { useApiUrl } from '../contexts/ApiContext';
+
+const DiscussionBoard = () => {
+ const [posts, setPosts] = useState([]);
+ const [editingPost, setEditingPost] = useState(null);
+ const apiUrl = useApiUrl();
+
+ useEffect(() => {
+ const fetchPosts = async () => {
+ try {
+ const data = await getAllPosts(apiUrl);
+
+ const filteredPosts = data.filter(post => !post.deleted);
+ setPosts(filteredPosts);
+ } catch (error) {
+ console.error('Failed to fetch posts:', error);
+ }
+ };
+
+ fetchPosts();
+ }, [apiUrl]);
+
+ const openEditModal = (postId) => {
+ const postToEdit = posts.find(function (post) {
+ return post.id === postId;
+ });
+ setEditingPost(postToEdit);
+ };
+
+ const closeEditModal = () => {
+ setEditingPost(null);
+ };
+
+ const deletePost = function (postId) {
+ axios
+ .delete(`/api/posts/${postId}`)
+ .then(function (response) {
+ const updatedPosts = posts.filter(function (post) {
+ return post.id !== postId;
+ });
+ setPosts(updatedPosts);
+ })
+ .catch(function (error) {
+ console.error('Failed to delete post:', error);
+ });
+ };
+
+ const saveEditedPost = async function (postId, editedContent) {
+ try {
+ await updatePost(postId, { content: editedContent });
+ const updatedPosts = posts.map(function (post) {
+ if (post.id === postId) {
+ return { ...post, content: editedContent };
+ }
+ return post;
+ });
+ setPosts(updatedPosts);
+ } catch (error) {
+ console.error('Failed to save edited post:', error);
+ }
+ closeEditModal();
+ };
+
+ return (
+
+
Discussion Board
+
+ Create Post
+
+ {posts.map(function (post) {
+ return (
+
+ );
+ })}
+
+ {editingPost && (
+
+ )}
+
+ );
+};
+
+export default DiscussionBoard;
diff --git a/client/src/components/ProtectedRoute.jsx b/client/src/components/ProtectedRoute.jsx
new file mode 100644
index 000000000..bf2f3f1bb
--- /dev/null
+++ b/client/src/components/ProtectedRoute.jsx
@@ -0,0 +1,17 @@
+import React from 'react';
+import { Navigate } from 'react-router-dom';
+import { useAuth } from '../contexts/AuthContext';
+
+const ProtectedRoute = ({ children }) => {
+ const { isAuthenticated } = useAuth();
+ console.log('Is Authenticated:', isAuthenticated);
+
+ if (!isAuthenticated) {
+ // Redirect to the login page if not authenticated
+ return ;
+ }
+
+ return children;
+};
+
+export default ProtectedRoute;
diff --git a/client/src/components/ResourceItem.jsx b/client/src/components/ResourceItem.jsx
new file mode 100644
index 000000000..aada734ee
--- /dev/null
+++ b/client/src/components/ResourceItem.jsx
@@ -0,0 +1,26 @@
+// ResourceItem.jsx
+import React from 'react';
+import axios from 'axios';
+
+const ResourceItem = ({ resource, shareUserId }) => {
+ // Function to handle sharing
+ const handleShare = async () => {
+ try {
+ await axios.post('/api/resources/share', { userId: shareUserId, resourceId: resource.id });
+ alert('Resource shared successfully');
+ } catch (error) {
+ console.error('Error sharing resource:', error);
+ alert('Failed to share resource');
+ }
+ };
+
+ return (
+
+ );
+};
+
+export default ResourceItem;
diff --git a/client/src/contexts/ApiContext.jsx b/client/src/contexts/ApiContext.jsx
new file mode 100644
index 000000000..20ea8fac1
--- /dev/null
+++ b/client/src/contexts/ApiContext.jsx
@@ -0,0 +1,23 @@
+// src/contexts/ApiContext.js
+import React, { createContext, useContext } from 'react';
+
+// Create a Context for the API URL
+const ApiUrlContext = createContext('');
+
+// const API_URL = process.env.NODE_ENV === 'production' ? 'https://codefm-server-production.up.railway.app' : 'http://localhost:3001';
+
+// Create a Provider component
+export const ApiUrlProvider = ({ children }) => {
+ const apiUrl = process.env.NODE_ENV === 'production'
+ ? 'https://codefm-server-production.up.railway.app'
+ : 'http://localhost:3001';
+
+ return (
+
+ {children}
+
+ );
+};
+
+// Create a hook to use the API URL context
+export const useApiUrl = () => useContext(ApiUrlContext);
diff --git a/client/src/contexts/AuthContext.jsx b/client/src/contexts/AuthContext.jsx
new file mode 100644
index 000000000..25288aa4d
--- /dev/null
+++ b/client/src/contexts/AuthContext.jsx
@@ -0,0 +1,42 @@
+import React, { createContext, useState, useContext, useEffect } from 'react';
+import axios from 'axios';
+import { useApiUrl } from './ApiContext';
+
+const AuthContext = createContext(null);
+
+export const AuthProvider = ({ children }) => {
+ const [authState, setAuthState] = useState({
+ isAuthenticated: false,
+ user: null,
+ loading: true, // Add a loading state
+ });
+
+ const apiUrl = useApiUrl();
+
+ useEffect(() => {
+ axios.get(`${apiUrl}/auth/login/success`, { withCredentials: true })
+ .then(response => {
+ if (response.data.success) {
+ setAuthState({ isAuthenticated: true, user: response.data.user, loading: false });
+ } else {
+ setAuthState({ isAuthenticated: false, user: null, loading: false });
+ }
+ })
+ .catch(error => {
+ console.error('Authentication check failed', error);
+ setAuthState({ isAuthenticated: false, user: null, loading: false });
+ });
+ }, [apiUrl]);
+
+ const setAuthInfo = ({ isAuthenticated, user }) => {
+ setAuthState({ isAuthenticated, user, loading: false });
+ };
+
+ return (
+
+ {!authState.loading && children} {/* Render children only when not loading */}
+
+ );
+};
+
+export const useAuth = () => useContext(AuthContext);
diff --git a/client/src/css/AboutUs.css b/client/src/css/AboutUs.css
new file mode 100644
index 000000000..71a227f28
--- /dev/null
+++ b/client/src/css/AboutUs.css
@@ -0,0 +1,25 @@
+.about-us-container {
+ background-color: rgba(255, 165, 0, 0.5);
+ padding: 20px;
+ margin: auto;
+ width: 50%;
+ text-align: left;
+ border-radius: 10px;
+ margin-top: 50px;
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ }
+
+ .content-container {
+ line-height: 1.5;
+ font-size: 18px;
+ }
+
+ h1 {
+ text-align: center;
+ color: #333;
+ font-size: 24px;
+ }
+
\ No newline at end of file
diff --git a/client/src/css/CreatePost.css b/client/src/css/CreatePost.css
new file mode 100644
index 000000000..3ac166b68
--- /dev/null
+++ b/client/src/css/CreatePost.css
@@ -0,0 +1,43 @@
+.form-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ }
+
+ .form-field {
+ margin-top: 20px;
+ width: 100%;
+ font-family: Arial, sans-serif;
+ }
+
+ .form-label {
+ margin-bottom: 10px;
+ }
+
+ .input-field {
+ margin-bottom: 40px;
+ width: 100%;
+ padding: 10px;
+ font-family: Arial, sans-serif;
+ }
+
+ .textarea-field {
+ width: 100%;
+ height: 200px;
+ padding: 10px;
+ font-family: Arial, sans-serif;
+ }
+
+ .submit-button {
+ background-color: orange;
+ color: white;
+ padding: 10px 20px;
+ border: none;
+ border-radius: 5px;
+ text-align: center;
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ font-size: 16px;
+ }
+
\ No newline at end of file
diff --git a/client/src/css/Events.css b/client/src/css/Events.css
new file mode 100644
index 000000000..2462c289f
--- /dev/null
+++ b/client/src/css/Events.css
@@ -0,0 +1,63 @@
+/* Events.css */
+
+.events-container {
+ width: 80%;
+ margin: 20px auto;
+ }
+
+ .events-heading-container {
+ text-align: center;
+ }
+
+ .events-heading {
+ color: black;
+ }
+
+ .filter-options {
+ margin-top: 10px;
+ }
+
+ .event-list {
+ list-style: none;
+ padding: 0;
+ }
+
+ .event-item {
+ background-color: #fffffb;
+ margin: 10px 0;
+ padding: 10px;
+ border-radius: 8px;
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
+ border: 1px solid black;
+ }
+
+ .event-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .event-title {
+ margin-bottom: 5px;
+ }
+
+ .button-container {
+ display: flex;
+ gap: 10px; /* Add some spacing between buttons */
+ }
+
+ .bookmark-button,
+ .share-button {
+ background-color: #2d1e06;
+ color: rgb(242, 160, 53);
+ border: none;
+ padding: 8px;
+ border-radius: 5px;
+ cursor: pointer;
+ }
+
+ .bookmark-button:hover,
+ .share-button:hover {
+ background-color: #ffcf95;
+ }
+
\ No newline at end of file
diff --git a/client/src/css/Home.css b/client/src/css/Home.css
new file mode 100644
index 000000000..0088c6035
--- /dev/null
+++ b/client/src/css/Home.css
@@ -0,0 +1,98 @@
+.navbar {
+ background-color: #483C32;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 5px 10px;
+ color: white;
+ }
+
+ .link-container {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ }
+
+ .link {
+ text-decoration: none;
+ color: white;
+ margin: 0 30px;
+ font-size: 20px;
+ }
+
+ .logo {
+ height: 6%;
+ max-width: 6%;
+ }
+
+ .image-container {
+ position: relative;
+ }
+
+ .image {
+ width: 100%;
+ }
+
+ .resource-bar {
+ background-color: #FFAA33;
+ display: flex;
+ justify-content: space-around;
+ align-items: center;
+ padding: 20px 0;
+ }
+
+ .icon {
+ font-size: 36px;
+ margin-right: 10px;
+ }
+
+ .text-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ }
+
+ .bar2 {
+ background-color: #483C32;
+ height: 80px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0 20px;
+ }
+
+ .bar2-text {
+ color: white;
+ font-size: 24px;
+ }
+
+ .join-us-button {
+ background-color: orange;
+ padding: 20px 40px;
+ border-radius: 5px;
+ text-decoration: none;
+ color: white;
+ font-size: 20px;
+ position: absolute;
+ top: 70%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ }
+
+ .login-button {
+ background-color: orange;
+ padding: 10px 20px;
+ border: none;
+ border-radius: 5px;
+ color: white;
+ font-size: 20px;
+ text-decoration: none;
+ }
+
+ .footer {
+ background-color: #483C32;
+ color: white;
+ padding: 10px;
+ text-align: center;
+ }
+
\ No newline at end of file
diff --git a/client/src/css/Login.css b/client/src/css/Login.css
new file mode 100644
index 000000000..8c0088d88
--- /dev/null
+++ b/client/src/css/Login.css
@@ -0,0 +1,47 @@
+.login-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ min-height: calc(100vh - 80px);
+ background: #FAF1E7;
+ }
+
+ .logo {
+ width: 100px;
+ }
+
+ .login-title {
+ font-size: 24px;
+ }
+
+ .input-container {
+ margin-bottom: 20px;
+ }
+
+ .input-field {
+ border: none;
+ border-bottom: 1px solid #000;
+ padding: 10px;
+ width: 100%;
+ }
+
+ .login-button {
+ background-color: #483C32;
+ color: orange;
+ padding: 15px 30px;
+ border-radius: 5px;
+ border: none;
+ box-shadow: none;
+
+ }
+
+ .login-link {
+ color: black;
+ font-size:large;
+ }
+
+ .signup-link {
+ color: orange;
+ }
+
\ No newline at end of file
diff --git a/client/src/css/ResourceList.css b/client/src/css/ResourceList.css
new file mode 100644
index 000000000..d4b93457d
--- /dev/null
+++ b/client/src/css/ResourceList.css
@@ -0,0 +1,25 @@
+/* ResourceList.css */
+.resource-list-container {
+ text-align: left;
+ }
+
+ .orange-container {
+ background-color: orange;
+ padding: 10px;
+ border-radius: 8px;
+ margin-bottom: 20px;
+ text-align: center; /* Center the text within the orange container */
+ }
+
+ .resource-section {
+ margin-bottom: 20px;
+ }
+
+ .resource-section h3 {
+ text-align: left;
+ }
+
+ .resource-item {
+ margin-bottom: 10px;
+ }
+
\ No newline at end of file
diff --git a/client/src/index.css b/client/src/index.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/client/src/main.jsx b/client/src/main.jsx
new file mode 100644
index 000000000..a2ca76165
--- /dev/null
+++ b/client/src/main.jsx
@@ -0,0 +1,13 @@
+import React from 'react'
+import ReactDOM from 'react-dom/client'
+import { BrowserRouter } from 'react-router-dom'
+import App from './App'
+import './index.css'
+
+ReactDOM.createRoot(document.getElementById('root')).render(
+
+
+
+
+
+)
\ No newline at end of file
diff --git a/client/src/pages/AboutUs.jsx b/client/src/pages/AboutUs.jsx
new file mode 100644
index 000000000..0f16642c9
--- /dev/null
+++ b/client/src/pages/AboutUs.jsx
@@ -0,0 +1,21 @@
+import React from 'react';
+
+import "../css/AboutUs.css";
+
+const AboutUs = () => {
+ return (
+
+
+
+
About Us
+
+
+ CodeFM is a dedicated web app tailored for Computer Science students, particularly designed for freshmen and sophomores. Our platform provides a vibrant community where students can freely share their thoughts, ask questions, access valuable resources, collaborate to solve problems, and stay informed about various topics. We strive to create an inclusive and supportive space for CS enthusiasts to enhance their learning experience and build lasting connections within the field.
+
+
+
+
+ );
+};
+
+export default AboutUs;
diff --git a/client/src/pages/CreatePost.jsx b/client/src/pages/CreatePost.jsx
new file mode 100644
index 000000000..9d94f30c9
--- /dev/null
+++ b/client/src/pages/CreatePost.jsx
@@ -0,0 +1,68 @@
+import React, { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { createPost } from '../services/postService';
+import { useApiUrl } from '../contexts/ApiContext';
+import '../css/CreatePost.css';
+
+const CreatePost = () => {
+ const [post, setPost] = useState({ title: '', content: '' });
+ const navigate = useNavigate();
+ const apiUrl = useApiUrl();
+
+ const handleSubmit = async (event) => {
+ event.preventDefault();
+ try {
+ await createPost(post, apiUrl);
+ navigate('/posts');
+ } catch (error) {
+ console.error('Failed to create post:', error);
+ }
+ };
+
+
+ const handleTitleChange = (e) => {
+ setPost({ ...post, title: e.target.value });
+ };
+
+ const handleContentChange = (e) => {
+ setPost({ ...post, content: e.target.value });
+ };
+
+ return (
+
+ );
+};
+
+export default CreatePost;
diff --git a/client/src/pages/CreateResource.jsx b/client/src/pages/CreateResource.jsx
new file mode 100644
index 000000000..7d69c417c
--- /dev/null
+++ b/client/src/pages/CreateResource.jsx
@@ -0,0 +1,43 @@
+import React, { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { createResource } from '../services/resourceService';
+
+const CreateResource = () => {
+ const [resource, setResource] = useState({ title: '', link: '' });
+ const navigate = useNavigate();
+
+ const handleSubmit = async (event) => {
+ event.preventDefault();
+ try {
+ await createResource(resource);
+ navigate('/resources');
+ } catch (error) {
+ console.error('Failed to create resource:', error);
+ }
+ };
+
+ return (
+
+
Add Resource
+
+ setResource({ ...resource, title: e.target.value })}
+ placeholder="Title"
+ required
+ />
+ setResource({ ...resource, link: e.target.value })}
+ placeholder="Resource Link"
+ required
+ />
+ Submit
+
+
+ );
+};
+
+export default CreateResource;
diff --git a/client/src/pages/DiscussionBoard.jsx b/client/src/pages/DiscussionBoard.jsx
new file mode 100644
index 000000000..fe273ae10
--- /dev/null
+++ b/client/src/pages/DiscussionBoard.jsx
@@ -0,0 +1,101 @@
+import React, { useEffect, useState } from 'react';
+import axios from 'axios';
+import { getAllPosts, updatePost } from '../services/postService';
+import Post from '../components/Post';
+import EditPostModal from './EditPostModal';
+import { useApiUrl } from '../contexts/ApiContext';
+
+const DiscussionBoard = () => {
+ const [posts, setPosts] = useState([]);
+ const [editingPost, setEditingPost] = useState(null);
+ const apiUrl = useApiUrl();
+
+ useEffect(() => {
+ const fetchPosts = async () => {
+ try {
+ const data = await getAllPosts(apiUrl);
+
+ const filteredPosts = data.filter(post => !post.deleted);
+ setPosts(filteredPosts);
+ } catch (error) {
+ console.error('Failed to fetch posts:', error);
+ }
+ };
+
+ fetchPosts();
+ }, [apiUrl]);
+
+ const openEditModal = (postId) => {
+ const postToEdit = posts.find(function (post) {
+ return post.id === postId;
+ });
+ setEditingPost(postToEdit);
+ };
+
+ const closeEditModal = () => {
+ setEditingPost(null);
+ };
+
+ const deletePost = function (postId) {
+ axios
+ .delete(`/api/posts/${postId}`)
+ .then(function (response) {
+ const updatedPosts = posts.filter(function (post) {
+ return post.id !== postId;
+ });
+ setPosts(updatedPosts);
+ })
+ .catch(function (error) {
+ console.error('Failed to delete post:', error);
+ });
+ };
+
+ const saveEditedPost = async function (postId, editedContent) {
+ try {
+ await updatePost(postId, { content: editedContent }, apiUrl);
+ const updatedPosts = posts.map(function (post) {
+ if (post.id === postId) {
+ return { ...post, content: editedContent };
+ }
+ return post;
+ });
+ setPosts(updatedPosts);
+ } catch (error) {
+ console.error('Failed to save edited post:', error);
+ }
+ closeEditModal();
+ };
+
+ return (
+
+
Discussion Board
+
+ Create Post
+
+ {posts.map(function (post) {
+ return (
+
+ );
+ })}
+
+ {editingPost && (
+
+ )}
+
+ );
+};
+
+export default DiscussionBoard;
diff --git a/client/src/pages/EditPost.jsx b/client/src/pages/EditPost.jsx
new file mode 100644
index 000000000..2aa7e7191
--- /dev/null
+++ b/client/src/pages/EditPost.jsx
@@ -0,0 +1,36 @@
+import React, { useState } from 'react';
+import { updatePost } from '../services/postService';
+
+const EditPost = ({ post, onSave }) => {
+ const [editedPost, setEditedPost] = useState(post);
+
+ const handleContentChange = (e) => {
+ setEditedPost({ ...editedPost, content: e.target.value });
+ };
+
+ const handleSave = async () => {
+ try {
+ console.log('Save button clicked');
+ await updatePost(editedPost.id, editedPost);
+ onSave(editedPost);
+ } catch (error) {
+ console.error('Failed to update post:', error);
+ }
+ };
+
+
+ return (
+
+
Edit Post
+
+ Save
+
+ );
+};
+
+export default EditPost;
diff --git a/client/src/pages/EditPostModal.jsx b/client/src/pages/EditPostModal.jsx
new file mode 100644
index 000000000..539ef136e
--- /dev/null
+++ b/client/src/pages/EditPostModal.jsx
@@ -0,0 +1,34 @@
+import React, { useState } from 'react';
+import Modal from 'react-modal';
+import { useApiUrl } from '../contexts/ApiContext';
+
+Modal.setAppElement('#root');
+
+const EditPostModal = ({ post, onSave, onRequestClose }) => {
+ const [editedContent, setEditedContent] = useState(post.content);
+ const apiUrl = useApiUrl();
+
+ const handleSave = () => {
+ onSave(post.id, editedContent, apiUrl);
+ };
+
+ return (
+
+ Edit Post
+ setEditedContent(e.target.value)}
+ placeholder="Edit the content"
+ required
+ />
+ Save
+ Cancel
+
+ );
+};
+
+export default EditPostModal;
diff --git a/client/src/pages/Events.jsx b/client/src/pages/Events.jsx
new file mode 100644
index 000000000..5942957a9
--- /dev/null
+++ b/client/src/pages/Events.jsx
@@ -0,0 +1,101 @@
+// Events.jsx
+import React, { useState } from 'react';
+// import { events } from '../../../server/data/mockData';
+import { FaBookmark, FaRegBookmark, FaShare } from 'react-icons/fa'; // Import icons from React Icons
+import "../css/Events.css";
+
+// create events array
+const events = [
+ { category: 'hackathon', title: 'Global Hack Week: Career Week', link: 'https://events.mlh.io/events/10077?_gl=1*bok2g4*_ga*MjEwMjQyNTIxMC4xNjk5NzMwODY2*_ga_E5KT6TC4TK*MTcwMDE0OTI1NC4yLjAuMTcwMDE0OTI1NC4wLjAuMA..', level: 'Beginner' },
+{ category: 'hackathon', title: 'Generate the future in the 404', link: 'https://www.aiatl.io/', level: 'Intermediate' },
+{ category: 'hackathon', title: 'Boston Hacks', link: 'https://bostonhacks.org/', level: 'Beginner' },
+{ category: 'hackathon', title: 'Microsoft AI Classroom Hackathon', link: 'https://microsoftaiclassroom.devpost.com/?ref_feature=challenge&ref_medium=discover', level: 'Intermediate' },
+{ category: 'hackathon', title: 'Google’s Immersive Geospatial Challenge', link: 'https://googlesimmersive.devpost.com/?ref_feature=challenge&ref_medium=discover', level: 'Beginner'},
+{ category: 'hackathon', title: 'MLH Month Long Hackathon', link: 'https://hackfest-november.devpost.com/?ref_feature=challenge&ref_medium=discover', level: 'Beginner' },
+]
+
+const Events = () => {
+ const [selectedLevel, setSelectedLevel] = useState('all');
+ const [bookmarkedEvents, setBookmarkedEvents] = useState([]);
+
+ const filteredEvents =
+ selectedLevel === 'all'
+ ? events
+ : events.filter((event) => event.level.toLowerCase() === selectedLevel.toLowerCase());
+
+ const handleLevelChange = (level) => {
+ setSelectedLevel(level);
+ };
+
+ const handleBookmarkToggle = (event) => {
+ const isBookmarked = bookmarkedEvents.includes(event.title);
+
+ if (isBookmarked) {
+ // Remove from bookmarks
+ setBookmarkedEvents(bookmarkedEvents.filter((title) => title !== event.title));
+ } else {
+ // Add to bookmarks
+ setBookmarkedEvents([...bookmarkedEvents, event.title]);
+ }
+ };
+
+ const handleShareClick = (event) => {
+ // Replace 'your-email@example.com' with the desired recipient email
+ const recipientEmail = 'your-email@example.com';
+ const subject = encodeURIComponent(`Check out this event: ${event.title}`);
+ const body = encodeURIComponent(`Learn more about this event: ${event.link}`);
+ const mailtoLink = `mailto:${recipientEmail}?subject=${subject}&body=${body}`;
+
+ window.location.href = mailtoLink;
+ };
+
+ return (
+
+
+
Upcoming Hackathons
+
+
+
+
+ Filter by Level:{' '}
+ handleLevelChange(e.target.value)}>
+ All
+ Beginner
+ Intermediate
+ Advanced
+
+
+
+
+
+ {filteredEvents.map((event) => (
+
+
+
{event.title}
+
+ handleBookmarkToggle(event)}
+ >
+ {bookmarkedEvents.includes(event.title) ? : }
+
+ handleShareClick(event)}
+ >
+
+
+
+
+ Level: {event.level}
+
+ Learn More
+
+
+ ))}
+
+
+ );
+};
+
+export default Events;
diff --git a/client/src/pages/Home.jsx b/client/src/pages/Home.jsx
new file mode 100644
index 000000000..9f1216215
--- /dev/null
+++ b/client/src/pages/Home.jsx
@@ -0,0 +1,43 @@
+import React from 'react';
+import { Link } from 'react-router-dom';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { faGraduationCap, faUsers, faCalendar, faTrophy } from '@fortawesome/free-solid-svg-icons';
+import '../css/Home.css';
+
+const Home = () => {
+ return (
+
+
+
+
Join Us
+
+
+
+
+ Resource
+
+
+
+ Community
+
+
+
+ Events
+
+
+
+ Skills
+
+
+
+ Unlock Your Learning Potential With CodeFM!
+ Join Us
+
+
+ © {new Date().getFullYear()} CodeFM. All rights reserved.
+
+
+ );
+};
+
+export default Home;
diff --git a/client/src/pages/Login.jsx b/client/src/pages/Login.jsx
new file mode 100644
index 000000000..1f2a34886
--- /dev/null
+++ b/client/src/pages/Login.jsx
@@ -0,0 +1,48 @@
+import React, { useEffect } from 'react';
+import { useNavigate } from 'react-router-dom';
+import axios from 'axios';
+import { useAuth } from '../contexts/AuthContext';
+import { useApiUrl } from '../contexts/ApiContext';
+import "../css/Login.css";
+
+const Login = () => {
+ const apiUrl = useApiUrl();
+ const navigate = useNavigate();
+ const { setAuthInfo } = useAuth();
+ const AUTH_URL = `${apiUrl}/auth/github`;
+
+ useEffect(() => {
+ axios.get(`${apiUrl}/auth/login/success`, { withCredentials: true })
+ .then(response => {
+ if (response.data.success) {
+ console.log('Login Success:', response.data); // Add this line
+ setAuthInfo({ isAuthenticated: true, user: response.data.user });
+ navigate('/');
+ }
+ })
+ .catch(error => {
+ if (error.response && error.response.status === 401) {
+ console.log('User is not logged in');
+ } else {
+ console.error('Login check failed', error);
+ }
+ });
+ }, [apiUrl, navigate, setAuthInfo]);
+
+ return (
+
+ );
+};
+
+export default Login;
diff --git a/client/src/pages/ResourceList.jsx b/client/src/pages/ResourceList.jsx
new file mode 100644
index 000000000..51f50dd56
--- /dev/null
+++ b/client/src/pages/ResourceList.jsx
@@ -0,0 +1,142 @@
+// ResourceList.jsx
+import React, { useEffect, useState } from 'react';
+import ResourceItem from '../components/ResourceItem';
+import { getAllResources } from '../services/resourceService';
+import { useApiUrl } from '../contexts/ApiContext';
+import '../css/ResourceList.css';
+
+const ResourceList = () => {
+ const [resources, setResources] = useState([]);
+ const [shareUserId, setShareUserId] = useState(''); // State to hold the user ID to share with
+ const apiUrl = useApiUrl();
+
+ useEffect(() => {
+ const fetchResources = async () => {
+ try {
+ const data = await getAllResources(apiUrl);
+ setResources(data);
+ } catch (error) {
+ console.error('Error fetching resources:', error);
+ }
+ };
+
+ fetchResources();
+ }, [apiUrl]);
+
+ // Group resources by type or category
+ const groupedResources = resources.reduce((acc, resource) => {
+ const category = resource.type || 'Other';
+ acc[category] = [...(acc[category] || []), resource];
+ return acc;
+ }, {});
+
+ return (
+
+
setShareUserId(e.target.value)}
+ />
+
Resource List
+ {Object.entries(groupedResources).map(([category, resourcesInCategory]) => (
+
+
+
{category}
+
+ {resourcesInCategory.map(resource => (
+
+ ))}
+
+ ))}
+
+ );
+};
+
+export default ResourceList;
+
+
+// import React, { useEffect, useState } from 'react';
+// import ResourceItem from '../components/ResourceItem';
+// import { getAllResources } from '../services/resourceService';
+// import '../css/ResourceList.css';
+
+// const ResourceList = () => {
+// const [resources, setResources] = useState([]);
+
+// useEffect(() => {
+// const fetchResources = async () => {
+// try {
+// const data = await getAllResources();
+// setResources(data);
+// } catch (error) {
+// console.error('Error fetching resources:', error);
+// }
+// };
+
+// fetchResources();
+// }, []);
+
+// // Group resources by type or category
+// const groupedResources = resources.reduce((acc, resource) => {
+// const category = resource.type || 'Other';
+// acc[category] = [...(acc[category] || []), resource];
+// return acc;
+// }, {});
+
+// return (
+//
+//
Resource List
+// {Object.entries(groupedResources).map(([category, resourcesInCategory]) => (
+//
+//
+//
{category}
+//
+// {resourcesInCategory.map(resource => (
+//
+// ))}
+//
+// ))}
+//
+// );
+// };
+
+// export default ResourceList;
+
+
+// // ResourceList.jsx
+// import React from 'react';
+// import { mockData } from '../../../server/data/mockData';
+// import ResourceItem from '../components/ResourceItem';
+// import '../css/ResourceList.css'; // Import the CSS file
+
+// const ResourceList = () => {
+// const groupedResources = mockData.resources.reduce((acc, resource) => {
+// const category = resource.type || 'Other';
+// acc[category] = [...(acc[category] || []), resource];
+// return acc;
+// }, {});
+
+// return (
+//
+
+//
+//
Resource List
+
+// {/* Display each section */}
+// {Object.entries(groupedResources).map(([category, resourcesInCategory]) => (
+//
+//
+//
{category === 'youtube' ? 'YouTube' : 'Coding Problems'}
+//
+// {resourcesInCategory.map(resource => (
+//
+// ))}
+//
+// ))}
+//
+//
+// );
+// };
+
+// export default ResourceList;
diff --git a/client/src/services/commentService.js b/client/src/services/commentService.js
new file mode 100644
index 000000000..bdef5d808
--- /dev/null
+++ b/client/src/services/commentService.js
@@ -0,0 +1,28 @@
+// commentService.js
+import axios from 'axios';
+
+// const API_URL = '/api/comments';
+const API_URL = process.env.NODE_ENV === 'production' ? 'https://codefm-production.up.railway.app' : 'http://localhost:3001';
+
+
+export const getAllComments = async () => {
+ try {
+ const response = await axios.get(`${API_URL}/api/comments`);
+ return response.data;
+ } catch (error) {
+ console.error('Error fetching comments:', error);
+ throw error;
+ }
+};
+
+export const createComment = async (commentData) => {
+ try {
+ const response = await axios.post(API_URL, commentData);
+ return response.data;
+ } catch (error) {
+ console.error('Error creating comment:', error);
+ throw error;
+ }
+};
+
+// Add other service functions for update and delete operations as needed
diff --git a/client/src/services/postService.js b/client/src/services/postService.js
new file mode 100644
index 000000000..43af509e9
--- /dev/null
+++ b/client/src/services/postService.js
@@ -0,0 +1,41 @@
+import axios from 'axios';
+
+export const getAllPosts = async (apiUrl) => {
+ try {
+ const response = await axios.get(`${apiUrl}/api/posts`);
+ return response.data;
+ } catch (error) {
+ console.error('Error fetching posts:', error);
+ throw error;
+ }
+};
+
+export const createPost = async (postData, apiUrl) => {
+ try {
+ const response = await axios.post(`${apiUrl}/api/posts`, postData);
+ return response.data;
+ } catch (error) {
+ console.error('Error creating post:', error);
+ throw error;
+ }
+};
+
+export const updatePost = async (postId, postData, apiUrl) => {
+ try {
+ const response = await axios.put(`${apiUrl}/api/posts/${postId}`, postData);
+ return response.data;
+ } catch (error) {
+ console.error('Error updating post:', error);
+ throw error;
+ }
+};
+
+export const deletePost = async (postId, apiUrl) => {
+ try {
+ const response = await axios.delete(`${apiUrl}/api/posts/${postId}`);
+ return response.data;
+ } catch (error) {
+ console.error('Error deleting post:', error);
+ throw error;
+ }
+};
diff --git a/client/src/services/resourceService.js b/client/src/services/resourceService.js
new file mode 100644
index 000000000..2f3c7b825
--- /dev/null
+++ b/client/src/services/resourceService.js
@@ -0,0 +1,21 @@
+import axios from 'axios';
+
+export const getAllResources = async (apiUrl) => {
+ try {
+ const response = await axios.get(`${apiUrl}/api/resources`);
+ return response.data;
+ } catch (error) {
+ console.error('Error fetching resources:', error);
+ throw error;
+ }
+};
+
+export const createResource = async (resourceData, apiUrl) => {
+ try {
+ const response = await axios.post(`${apiUrl}/api/resources`, resourceData);
+ return response.data;
+ } catch (error) {
+ console.error('Error creating resource:', error);
+ throw error;
+ }
+};
diff --git a/client/src/services/typeService.js b/client/src/services/typeService.js
new file mode 100644
index 000000000..a4558dc82
--- /dev/null
+++ b/client/src/services/typeService.js
@@ -0,0 +1,21 @@
+import axios from 'axios';
+
+export const getAllTypes = async (apiUrl) => {
+ try {
+ const response = await axios.get(`${apiUrl}/api/types`);
+ return response.data;
+ } catch (error) {
+ console.error('Error fetching types:', error);
+ throw error;
+ }
+};
+
+export const createType = async (typeData, apiUrl) => {
+ try {
+ const response = await axios.post(`${apiUrl}/api/types`, typeData);
+ return response.data;
+ } catch (error) {
+ console.error('Error creating type:', error);
+ throw error;
+ }
+};
diff --git a/client/src/services/userService.js b/client/src/services/userService.js
new file mode 100644
index 000000000..77cd5e901
--- /dev/null
+++ b/client/src/services/userService.js
@@ -0,0 +1,26 @@
+// userService.js
+import axios from 'axios';
+
+const API_URL = '/api/users';
+
+export const getAllUsers = async () => {
+ try {
+ const response = await axios.get(API_URL);
+ return response.data;
+ } catch (error) {
+ console.error('Error fetching users:', error);
+ throw error;
+ }
+};
+
+export const createUser = async (userData) => {
+ try {
+ const response = await axios.post(API_URL, userData);
+ return response.data;
+ } catch (error) {
+ console.error('Error creating user:', error);
+ throw error;
+ }
+};
+
+// Add other service functions for update and delete operations as needed
diff --git a/client/vite.config.js b/client/vite.config.js
new file mode 100644
index 000000000..24430b06e
--- /dev/null
+++ b/client/vite.config.js
@@ -0,0 +1,25 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+import path from 'path'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [react()],
+ build: {
+ outDir: 'dist',
+ emptyOutDir: true
+ },
+ server: {
+ proxy: {
+ '/api': {
+ target: 'http://localhost:3001'
+ },
+ '/auth': {
+ target: 'http://localhost:3001',
+ changeOrigin: true,
+ secure: false,
+ ws: true
+ }
+ }
+ }
+})
diff --git a/milestones/milestone1.md b/milestones/milestone1.md
index a8f086378..8802d12b7 100644
--- a/milestones/milestone1.md
+++ b/milestones/milestone1.md
@@ -1,31 +1,27 @@
# Milestone 1
-This document should be completed and submitted during **Unit 5** of this course. You **must** check off all completed tasks in this document in order to receive credit for your work.
-
## Checklist
-This unit, be sure to complete all tasks listed below. To complete a task, place an `x` between the brackets.
-
-- [ ] Read and understand all required features
- - [ ] Understand you **must** implement **all** baseline features and **two** custom features
-- [ ] In `readme.md`: update app name
-- [ ] In `readme.md`: add all group members' names
-- [ ] In `readme.md`: complete the **Description and Purpose** section
-- [ ] In `readme.md`: complete the **Inspiration** section
-- [ ] In `readme.md`: list all features you intend to include in your app (in future units, you will check off features as you complete them and add GIFs demonstrating the features)
-- [ ] In `planning/user_stories.md`: add all user stories
-- [ ] In this document, complete the **Reflection** section below
+- [x] Read and understand all required features
+ - [x] Understand we **must** implement **all** baseline features and **two** custom features
+- [x] In `readme.md`: update app name
+- [x] In `readme.md`: add all group members' names
+- [x] In `readme.md`: complete the **Description and Purpose** section
+- [x] In `readme.md`: complete the **Inspiration** section
+- [x] In `readme.md`: list all features we intend to include in our app (in future units, we will check off features as we complete them and add GIFs demonstrating the features)
+- [x] In `planning/user_stories.md`: add all user stories
+- [x] In this document, complete the **Reflection** section below
## Reflection
### 1. What went well during this unit?
-[👉🏾👉🏾👉🏾 your answer here]
+During this unit, our team was successful in several areas. We effectively brainstormed and agreed upon an app concept that is both exciting and feasible - a community platform for new coders. We clearly defined our app's purpose and drew inspiration from notable platforms like Codecademy, GitHub, and CodePath, ensuring our app would have practical relevance and a solid user base. We listed the features we aim to include, giving a clear direction for the app's development. We were able to break down the user interactions with our app into detailed user stories, ensuring that we're focusing on user-centric design from the start. This approach is guiding us in the right direction for the next phases of the project.
### 2. What were some challenges your group faced in this unit?
-[👉🏾👉🏾👉🏾 your answer here]
+One challenge we faced was determining the scope of our project. With so many potential features and user stories, it was challenging to decide which elements were essential for our MVP (Minimum Viable Product). This scope definition was critical to ensure that our project is manageable within the given timeframe and with our skill set.
### 3. What additional support will you need in upcoming units as you continue to work on your final project?
-[👉🏾👉🏾👉🏾 your answer here]
+Moving forward, we might need more mentorship in performance optimization. As our platform is expected to handle a number of user interactions and data, strategies for optimizing load times and ensuring a smooth, responsive user experience are important. Insight into best practices for code optimization and efficient database queries would significantly help our project's robustness and user satisfaction.
diff --git a/milestones/milestone2.md b/milestones/milestone2.md
index 7d7687f1e..c2c26d5e3 100644
--- a/milestones/milestone2.md
+++ b/milestones/milestone2.md
@@ -1,27 +1,22 @@
# Milestone 2
-This document should be completed and submitted during **Unit 6** of this course. You **must** check off all completed tasks in this document in order to receive credit for your work.
-
-## Checklist
-
-This unit, be sure to complete all tasks listed below. To complete a task, place an `x` between the brackets.
-
-- [ ] In `planning/wireframes.md`: add wireframes for at least three pages in your web app
-- [ ] In `planning/entity_relationship_diagram.md`: add the entity relationship diagram you developed for your database
-- [ ] Prepare your three-minute pitch presentation, to be presented during Unit 7 (the next unit)
- - [ ] You do **not** need to submit any materials in advance of your pitch
-- [ ] In this document, complete the **Reflection** section below
+- [x] In `planning/wireframes.md`: add wireframes for at least three pages in our web app
+- [x] In `planning/entity_relationship_diagram.md`: add the entity relationship diagram we developed for our database
+- [x] Prepare three-minute pitch presentation, to be presented during Unit 7 (the next unit)
+- [x] In this document, complete the **Reflection** section below
## Reflection
### 1. What went well during this unit?
-[👉🏾👉🏾👉🏾 your answer here]
+During this unit, the development of the Entity Relationship Diagram (ERD) was a significant achievement. It provided a clear visualization of the database structure, ensuring that all the necessary entities and their relationships were well-defined. This helped in understanding the flow of data and the interactions between different entities, which is crucial for the app's functionality.
+
+Creating the wireframes and later adding those the slide was a learning experience and we got to collaborate more.
### 2. What were some challenges your group faced in this unit?
-[👉🏾👉🏾👉🏾 your answer here]
+One of the challenges faced during this unit was ensuring that the ERD accurately represented all the functionalities and features of the app. With a diverse set of features, from discussion boards to resource sharing and progress tracking, it was essential to capture every detail in the ERD. Another challenge was deciding on the relationships between entities, especially when it came to many-to-many relationships and their representation. Balancing the user experience with the technical constraints was also a point of consideration, ensuring that the app is both user-friendly and technically robust.
### 3. What additional support will you need in upcoming units as you continue to work on your final project?
-[👉🏾👉🏾👉🏾 your answer here]
+As we move forward with the development, we'll need support in implementing the many-to-many relationships, especially with the join tables. Guidance on best practices for database optimization and ensuring efficient queries will be beneficial.
diff --git a/milestones/milestone3.md b/milestones/milestone3.md
index 74f5aa99a..f004bfc5f 100644
--- a/milestones/milestone3.md
+++ b/milestones/milestone3.md
@@ -1,40 +1,38 @@
# Milestone 3
-This document should be completed and submitted during **Unit 7** of this course. You **must** check off all completed tasks in this document in order to receive credit for your work.
-
## Checklist
This unit, be sure to complete all tasks listed below. To complete a task, place an `x` between the brackets.
You will need to reference the GitHub Project Management guide in the course portal for more information about how to complete each of these steps.
-- [ ] Create a project board associated with this repo
-- [ ] In this repo, create GitHub milestones for each of the remaining milestones in this unit
-- [ ] In this repo, create issues for each feature on your feature list
- - [ ] Make sure to add the issues to your project board and to the appropriate milestones
-- [ ] Update the status of issues in your project board as you complete them
-- [ ] In `readme.md`, check off the features you have completed in this unit by adding a ✅ emoji in front of their title
- - [ ] Under each feature you have completed, **include a GIF** showing feature functionality
-- [ ] In this document, complete the **Reflection** section below
+- [x] Create a project board associated with this repo
+- [x] In this repo, create GitHub milestones for each of the remaining milestones in this unit
+- [x] In this repo, create issues for each feature on your feature list
+ - [x] Make sure to add the issues to your project board and to the appropriate milestones
+- [x] Update the status of issues in your project board as you complete them
+- [x] In `readme.md`, check off the features you have completed in this unit by adding a ✅ emoji in front of their title
+ - [x] Under each feature you have completed, **include a GIF** showing feature functionality
+- [x] In this document, complete the **Reflection** section below
## Reflection
### 1. What went well during this unit?
-[👉🏾👉🏾👉🏾 your answer here]
+- The collaboration between backend and frontend elements was seamless in terms of integrating the React components with the Express server. Myesha's design for the homepage and the CreatePost page provided a strong foundation for user interaction. On the backend, the setup of a basic Express server and the configuration to serve the React application went smoothly, which was crucial for the project's infrastructure.
### 2. What were some challenges your group faced in this unit?
-[👉🏾👉🏾👉🏾 your answer here]
+- A significant challenge was distributing tasks to align with our respective strengths, particularly as Myesha focused on UI design, leaving the bulk of the backend and integration work to be addressed. Coordinating and merging our work sometimes posed delays and required extensive communication to ensure compatibility between the frontend and backend.
### Did you finish all of your tasks in your sprint plan for this week? If you did not finish all of the planned tasks, how would you prioritize the remaining tasks on your list?
-[👉🏾👉🏾👉🏾 your answer here]
+- While we managed to complete the basic setup for both the frontend and backend, some tasks, such as the complete integration and testing of dynamic routing and database schema optimizations, are pending. Prioritization of remaining tasks would focus on backend features critical to user interaction, like authentication and CRUD operations for the discussion board, which are central to the user experience.
### Which features and user stories would you consider “at risk”? How will you change your plan if those items remain “at risk”?
-[👉🏾👉🏾👉🏾 your answer here]
+- Currently, the user authentication is "at risk" due to its complex nature and dependency on both frontend and backend completion. If these remain at risk, we will need to reassess the task distribution, potentially seeking additional support for backend tasks to ensure timely completion.
### 5. What additional support will you need in upcoming units as you continue to work on your final project?
-[👉🏾👉🏾👉🏾 your answer here]
+- Given the workload, additional support in the form of backend development resources would be beneficial.
diff --git a/milestones/milestone4.md b/milestones/milestone4.md
index 8c781fd23..21a36f739 100644
--- a/milestones/milestone4.md
+++ b/milestones/milestone4.md
@@ -6,29 +6,29 @@ This document should be completed and submitted during **Unit 8** of this course
This unit, be sure to complete all tasks listed below. To complete a task, place an `x` between the brackets.
-- [ ] Update the status of issues in your project board as you complete them
-- [ ] In `readme.md`, check off the features you have completed in this unit by adding a ✅ emoji in front of their title
- - [ ] Under each feature you have completed, **include a GIF** showing feature functionality
-- [ ] In this document, complete the **Reflection** section below
+- [x] Update the status of issues in your project board as you complete them
+- [x] In `readme.md`, check off the features you have completed in this unit by adding a ✅ emoji in front of their title
+ - [x] Under each feature you have completed, **include a GIF** showing feature functionality
+- [x] In this document, complete the **Reflection** section below
## Reflection
### 1. What went well during this unit?
-[👉🏾👉🏾👉🏾 your answer here]
+We were able to communicate and discuss about our we app, what important feauters we want and how to optimize our features so that we can finish all our baseline features along with the streched features on time. Additionally, we successfuly set up our backend and frontend and were able to complete most of the baseline features.
### 2. What were some challenges your group faced in this unit?
-[👉🏾👉🏾👉🏾 your answer here]
+Although Login/SignUp frontend were set up, we are having challenges to set up the backend for the GitHub authentication for users login and sign up.
### Did you finish all of your tasks in your sprint plan for this week? If you did not finish all of the planned tasks, how would you prioritize the remaining tasks on your list?
-[👉🏾👉🏾👉🏾 your answer here]
+We were able to finish 60% of our this weeks target which inclued implementing CRUD features in discussion board, dynamic routes, and database schema optimization. Prioritization of remaining tasks would focus on backend features critical to user interaction, like authentication which is central to the user experience.
### Which features and user stories would you consider “at risk”? How will you change your plan if those items remain “at risk”?
-[👉🏾👉🏾👉🏾 your answer here]
+Currently, the user authentication is "at risk" due to its complex nature and dependency on both frontend and backend completion. If these remain at risk, we will need to reassess the task distribution, potentially seeking additional support for backend tasks to ensure timely completion.
### 5. What additional support will you need in upcoming units as you continue to work on your final project?
-[👉🏾👉🏾👉🏾 your answer here]
+Given the workload, additional support in the form of backend development resources would be beneficial.
diff --git a/milestones/milestone5.md b/milestones/milestone5.md
index 2f582b179..b994fa45f 100644
--- a/milestones/milestone5.md
+++ b/milestones/milestone5.md
@@ -6,15 +6,15 @@ This document should be completed and submitted during **Unit 9** of this course
This unit, be sure to complete all tasks listed below. To complete a task, place an `x` between the brackets.
-- [ ] Deploy your project on Railway
- - [ ] In `readme.md`, add the link to your deployed project
-- [ ] Update the status of issues in your project board as you complete them
-- [ ] In `readme.md`, check off the features you have completed in this unit by adding a ✅ emoji in front of their title
+- [x] Deploy your project on Railway
+ - [x] In `readme.md`, add the link to your deployed project
+- [x] Update the status of issues in your project board as you complete them
+- [x] In `readme.md`, check off the features you have completed in this unit by adding a ✅ emoji in front of their title
- [ ] Under each feature you have completed, **include a GIF** showing feature functionality
-- [ ] In this document, complete the **Reflection** section below
-- [ ] 🚩🚩🚩**Complete the Final Project Feature Checklist section below**, detailing each feature you completed in the project (ONLY include features you implemented, not features you planned)
-- [ ] 🚩🚩🚩**Record a GIF showing a complete run-through of your app** that displays all the components included in the **Final Project Feature Checklist** below
- - [ ] Include this GIF in the **Final Demo GIF** section below
+- [x] In this document, complete the **Reflection** section below
+- [x] 🚩🚩🚩**Complete the Final Project Feature Checklist section below**, detailing each feature you completed in the project (ONLY include features you implemented, not features you planned)
+- [x] 🚩🚩🚩**Record a GIF showing a complete run-through of your app** that displays all the components included in the **Final Project Feature Checklist** below
+ - [x] Include this GIF in the **Final Demo GIF** section below
## Final Project Feature Checklist
@@ -24,35 +24,35 @@ Complete the checklist below detailing each baseline, custom, and stretch featur
👉🏾👉🏾👉🏾 Check off each completed feature below.
-- [ ] The project includes an Express backend app and a React frontend app
-- [ ] The project includes these backend-specific features:
- - [ ] At least one of each of the following database relationship in Postgres
- - [ ] one-to-many
- - [ ] many-to-many with a join table
- - [ ] A well-designed RESTful API
- - [ ] The API can respond to at least one of each type of request: GET, POST, PATCH, and DELETE
- - [ ] Routes follow proper naming conventions
- - [ ] The ability to reset the database to its default state
-- [ ] The project includes these frontend-specific features:
- - [ ] At least one redirection
- - [ ] At least one interaction that the user can initiate and complete on the same page without navigating to a new page
- - [ ] Dynamic frontend routes created with React Router
- - [ ] Hierarchically designed React components
- - [ ] Components broken down into categories, including Page and Component types
- - [ ] Corresponding container components and presenter components as appropriate
-- [ ] The project includes dynamic routes for both frontend and backend apps
-- [ ] The project is deployed on Railway with all pages and features working
+- [x] The project includes an Express backend app and a React frontend app
+- [x] The project includes these backend-specific features:
+ - [x] At least one of each of the following database relationship in Postgres
+ - [x] one-to-many
+ - [x] many-to-many with a join table
+ - [x] A well-designed RESTful API
+ - [x] The API can respond to at least one of each type of request: GET, POST, PATCH, and DELETE
+ - [x] Routes follow proper naming conventions
+ - [x] The ability to reset the database to its default state
+- [x] The project includes these frontend-specific features:
+ - [x] At least one redirection
+ - [x] At least one interaction that the user can initiate and complete on the same page without navigating to a new page
+ - [x] Dynamic frontend routes created with React Router
+ - [x] Hierarchically designed React components
+ - [x] Components broken down into categories, including Page and Component types
+ - [x] Corresponding container components and presenter components as appropriate
+- [x] The project includes dynamic routes for both frontend and backend apps
+- [x] The project is deployed on Railway with all pages and features working
### Custom Features
👉🏾👉🏾👉🏾 Check off each completed feature below.
-- [ ] The project gracefully handles errors
-- [ ] The project includes a one-to-one database relationship
-- [ ] The project includes a slide-out pane or modal as appropriate for your use case
-- [ ] The project includes a unique field within the join table
-- [ ] The project includes a custom non-RESTful route with corresponding controller actions
-- [ ] The project allows filtering and/or sorting as appropriate for your use case
+- [x] The project gracefully handles errors
+- [x] The project includes a one-to-one database relationship
+- [x] The project includes a slide-out pane or modal as appropriate for your use case
+- [x] The project includes a unique field within the join table
+- [x] The project includes a custom non-RESTful route with corresponding controller actions
+- [x] The project allows filtering and/or sorting as appropriate for your use case
- [ ] Data is automatically generated in response to a certain event or user action. Examples include generating a default inventory for a new user starting a game or creating a starter set of tasks for a user creating a new task app account
- [ ] Data submitted via a POST or PATCH request is validated before the database is updated
@@ -60,8 +60,8 @@ Complete the checklist below detailing each baseline, custom, and stretch featur
👉🏾👉🏾👉🏾 Check off each completed feature below.
-- [ ] A subset of pages require the user to log in before accessing the content
- - [ ] Users can log in and log out via GitHub OAuth with Passport.js
+- [x] A subset of pages require the user to log in before accessing the content
+ - [x] Users can log in and log out via GitHub OAuth with Passport.js
- [ ] Restrict available user options dynamically, such as restricting available purchases based on a user's currency
- [ ] Show a spinner while a page or page element is loading
- [ ] Disable buttons and inputs during the form submission process
@@ -77,20 +77,20 @@ Complete the checklist below detailing each baseline, custom, and stretch featur
### 1. What went well during this unit?
-[👉🏾👉🏾👉🏾 your answer here]
+During this unit, the most successful aspect was gaining a deep understanding of backend development. I was able to successfully implement various backend functionalities, ensuring that the server, database, and authentication mechanisms worked seamlessly. Additionally, I devoted considerable effort to the frontend, achieving a functional and user-friendly interface. My problem-solving skills were significantly enhanced, as I navigated through complex coding challenges and debugged effectively.
### 2. What were some challenges your group faced in this unit?
-[👉🏾👉🏾👉🏾 your answer here]
+The major challenges were centered around deployment and integration. As I was mostly working on the backend, I faced several issues with getting the application deployed successfully, particularly in aligning the backend and frontend components on the hosting platform. Moreover, helping my partner manage version control with Git presented its own set of hurdles. Ensuring code compatibility and resolving merge conflicts for both of our branches demanded patience and meticulous attention to detail.
### 3. What were some of the highlights or achievements that you are most proud of in this project?
-[👉🏾👉🏾👉🏾 your answer here]
+Implementing features like dynamic routing, RESTful API, and a many-to-many database relationship are among the technical accomplishments we are particularly proud of. The project also showcased our ability to apply concepts learned in class to a real-world application, demonstrating significant growth in our coding skills and understanding of web development.
### 4. Reflecting on your web development journey so far, how have you grown since the beginning of the course?
-[👉🏾👉🏾👉🏾 your answer here]
+Since the beginning of this course, my growth in web development has been substantial. I've transitioned from understanding basic concepts to implementing complex functionalities in a real-world application. My problem-solving skills have improved, and my ability to research and learn new technologies independently has become one of my strongest assets. This was my first time learning Express JS. I got to learn for the first time how to create a structure for the projects and how to set up the connection between frontend and backend which I was always curious about. Learning about all the familiar concepts(API, deployment, etc.) in depth and learning how to implement them in my coding was really insightful. The experience has also honed my collaboration and communication skills, crucial for any successful development project.
### 5. Looking ahead, what are your goals related to web development, and what steps do you plan to take to achieve them?
-[👉🏾👉🏾👉🏾 your answer here]
+Moving forward, my goal is to solidify my understanding of the tech stacks we learned, possibly by retaking this course next semester. I aim to delve deeper into web development, exploring advanced topics and emerging technologies. Practically, I plan to engage in more projects, both personal and collaborative, to refine my skills. Networking with professionals in the field and participating in coding communities will also be a focus, as these experiences are vital for continuous learning and professional growth.
diff --git a/planning/entity_relationship_diagram.md b/planning/entity_relationship_diagram.md
index 12c25f62c..1214346a5 100644
--- a/planning/entity_relationship_diagram.md
+++ b/planning/entity_relationship_diagram.md
@@ -1,17 +1,50 @@
-# Entity Relationship Diagram
+## List of Tables:
+
+### USER
+| Column Name | Type | Description |
+|-------------|-----------|---------------------------|
+| id | integer | primary key |
+| username | varchar | user's unique username |
+| password | varchar | user's encrypted password|
+
+### POST
+| Column Name | Type | Description |
+|-------------|-----------|---------------------------|
+| id | integer | primary key |
+| content | text | content of the post |
+| userId | int | foreign key to USER |
+
+### COMMENT
+| Column Name | Type | Description |
+|-------------|-----------|---------------------------|
+| id | integer | primary key |
+| content | text | content of the comment |
+| postId | int | foreign key to POST |
-Reference the Creating an Entity Relationship Diagram final project guide in the course portal for more information about how to complete this deliverable.
+### RESOURCE
+| Column Name | Type | Description |
+|-------------|-----------|---------------------------|
+| id | integer | primary key |
+| link | varchar | URL of the resource |
+| typeId | int | foreign key to TYPE |
+| userId | int | foreign key to USER |
-## Create the List of Tables
+### TYPE
+| Column Name | Type | Description |
+|-------------|-----------|---------------------------|
+| id | integer | primary key |
+| name | varchar | type of the resource |
+
+
+# Entity Relationship Diagram
-[👉🏾👉🏾👉🏾 List each table in your diagram]
+This ERD visually represents the relationships between the main entities in our application:
-## Add the Entity Relationship Diagram
+- **USER**: Represents the registered users of the platform.
+- **POST**: Represents the posts created by users.
+- **COMMENT**: Represents comments on posts.
+- **RESOURCE**: Represents the learning resources added by users.
+- **TYPE**: Represents the type of resource (e.g., "Videos" or "Articles").
-[👉🏾👉🏾👉🏾 Include an image or images of the diagram below. You may also wish to use the following markdown syntax to outline each table, as per your preference.]
+
-| Column Name | Type | Description |
-|-------------|------|-------------|
-| id | integer | primary key |
-| name | text | name of the shoe model |
-| ... | ... | ... |
diff --git a/planning/user_stories.md b/planning/user_stories.md
index 1e55ecbcd..e0e76a6f9 100644
--- a/planning/user_stories.md
+++ b/planning/user_stories.md
@@ -1,13 +1,18 @@
-# User Stories
+### User Stories
-Reference the Writing User Stories final project guide in the course portal for more information about how to complete each of the sections below.
-## Outline User Roles
+## User Roles:
+**Student**: A college computer science freshman or sophomore student using the platform to learn, share, and connect.
-[👉🏾👉🏾👉🏾 Include at least at least 1, but no more than 3, user roles.]
+### User Stories:
-## Draft User Stories
-
-[👉🏾👉🏾👉🏾 Include at least at least 10 user stories in this format:]
-
-1. As a [user role], I want to [what], so that [why].
+1. **As a Student**, I want to **register** for an account, so that I can access the platform's features.
+2. **As a Student**, I want to **log in** to my account, so that I can engage with the community.
+3. **As a Student**, I want to **browse available resources** (like YouTube videos, Leetcode problems, etc.), so that I can find learning materials that match my interests.
+4. **As a Student**, I want to **filter resources by type** (e.g., "Videos" or "Articles"), so that I can quickly find specific types of learning materials.
+5. **As a Student**, I want to **create a post** on the discussion board, so that I can share my thoughts, questions, or experiences with the community.
+6. **As a Student**, I want to **read posts** made by other students, so that I can learn from their insights and experiences.
+7. **As a Student**, I want to **update or delete my posts**, so that I can correct mistakes or remove outdated information.
+8. **As a Student**, I want to **comment on posts**, so that I can engage in discussions and provide feedback to peers.
+9. **As a Student**, I want to **navigate the platform easily** using dynamic routes, so that I can have a seamless user experience.
+10. **As a Student**, I want to **receive validation feedback** when submitting data, so that I can ensure my submissions are appropriate and complete.
diff --git a/planning/wireframes.md b/planning/wireframes.md
index fbcd15a0c..bb9c43cc2 100644
--- a/planning/wireframes.md
+++ b/planning/wireframes.md
@@ -4,18 +4,43 @@ Reference the Creating an Entity Relationship Diagram final project guide in the
## List of Pages
-[👉🏾👉🏾👉🏾 List the pages you expect to have in your app, with a ⭐ next to pages you have wireframed]
+- Home Page (Before login) ⭐
+- Discussion page/Dashboard Page (After Login) ⭐
+- Sign up page ⭐
+- Log in page ⭐
+- User Profile (settings) page ⭐
+- Events page ⭐
+- Resources Page ⭐
+- Badges Page ⭐
-## Wireframe 1: [page title]
+## Wireframe 1: Home Page (Before login)
-[👉🏾👉🏾👉🏾 include wireframe 1]
+
-## Wireframe 2: [page title]
+## Wireframe 2: Discussion page/Dashboard Page (After Login)
-[👉🏾👉🏾👉🏾 include wireframe 2]
+
-## Wireframe 3: [page title]
+## Wireframe 3: Sign up page
-[👉🏾👉🏾👉🏾 include wireframe 3]
+
-[👉🏾👉🏾👉🏾 include more wireframes as desired]
+## Wireframe 4: Log in page
+
+
+
+## Wireframe 5: User Profile (settings) page
+
+
+
+## Wireframe 6: Events page
+
+
+
+## Wireframe 7: Resources page
+
+
+
+## Wireframe 8: Badges page
+
+
diff --git a/server/config/auth.js b/server/config/auth.js
new file mode 100644
index 000000000..e824931fb
--- /dev/null
+++ b/server/config/auth.js
@@ -0,0 +1,55 @@
+import { pool } from './database.js';
+import GithubStrategy from 'passport-github2';
+import passport from 'passport';
+
+const options = {
+ clientID: process.env.GITHUB_CLIENT_ID,
+ clientSecret: process.env.GITHUB_CLIENT_SECRET,
+ // callbackURL: process.env.GITHUB_CALLBACK_URL,
+ // callbackURL: process.env.NODE_ENV === 'production'
+ // ? process.env.GITHUB_CALLBACK_URL
+ // : 'http://localhost:3001/auth/github/callback',
+ callbackURL: process.env.NODE_ENV === 'production'
+ ? 'https://codefm-server-production.up.railway.app/auth/github/callback'
+ : process.env.GITHUB_CALLBACK_URL,
+};
+
+const verify = async (accessToken, refreshToken, profile, callback) => {
+ console.log('Access Token:', accessToken);
+ console.log('Profile:', profile);
+ const { id, login, avatar_url } = profile._json;
+
+
+ if (!login) {
+ return callback(new Error('Username is required'), null);
+ }
+
+ const userData = {
+ githubId: id,
+ username: login,
+ avatarUrl: avatar_url,
+ accessToken,
+ };
+
+ try {
+ const results = await pool.query('SELECT * FROM GITHUBUSER WHERE username = $1', [userData.username]);
+
+ if (results.rows.length === 0) {
+ // Changed to fetch the entire user row
+ const insertResult = await pool.query(
+ 'INSERT INTO GITHUBUSER (githubid, username, avatarurl, accesstoken) VALUES ($1, $2, $3, $4) RETURNING *',
+ [userData.githubId, userData.username, userData.avatarUrl, accessToken],
+ );
+ return callback(null, insertResult.rows[0]); // Return the full user object
+ }
+
+ return callback(null, results.rows[0]); // Return the full user object
+ } catch (error) {
+ return callback(error);
+ }
+ console.log("User authenticated:", userData);
+};
+
+export const GitHub = new GithubStrategy(options, verify);
+
+export default passport;
diff --git a/server/config/database.js b/server/config/database.js
new file mode 100644
index 000000000..7b5183342
--- /dev/null
+++ b/server/config/database.js
@@ -0,0 +1,15 @@
+import pg from 'pg'
+import './dotenv.js'
+
+const connectionString = process.env.DATABASE_URL;
+
+const config = {
+ user: process.env.PGUSER,
+ password: process.env.PGPASSWORD,
+ host: process.env.PGHOST,
+ port: process.env.PGPORT,
+ database: process.env.PGDATABASE,
+ connectionString
+}
+
+export const pool = new pg.Pool(config)
\ No newline at end of file
diff --git a/server/config/dotenv.js b/server/config/dotenv.js
new file mode 100644
index 000000000..f33ba621a
--- /dev/null
+++ b/server/config/dotenv.js
@@ -0,0 +1,2 @@
+import dotenv from 'dotenv'
+dotenv.config({ path: '../.env' })
\ No newline at end of file
diff --git a/server/config/reset.js b/server/config/reset.js
new file mode 100644
index 000000000..4530414ab
--- /dev/null
+++ b/server/config/reset.js
@@ -0,0 +1,73 @@
+import { pool } from './database.js';
+//import { mockData } from '../data/mockData.js';
+
+const createUserTable = `
+CREATE TABLE IF NOT EXISTS "GITHUBUSER" (
+ id SERIAL PRIMARY KEY,
+ username text UNIQUE NOT NULL,
+ avatarurl text,
+ githubid text UNIQUE,
+ accesstoken text
+);
+`;
+
+const createPostTable = `
+CREATE TABLE IF NOT EXISTS "POST" (
+ id SERIAL PRIMARY KEY,
+ content TEXT NOT NULL,
+ userId INTEGER REFERENCES "GITHUBUSER"(id)
+);
+`;
+
+const createCommentTable = `
+CREATE TABLE IF NOT EXISTS "COMMENT" (
+ id SERIAL PRIMARY KEY,
+ content TEXT NOT NULL,
+ postId INTEGER REFERENCES "POST"(id)
+);
+`;
+
+const createTypeTable = `
+CREATE TABLE IF NOT EXISTS "TYPE" (
+ id SERIAL PRIMARY KEY,
+ name VARCHAR(255) NOT NULL
+);
+`;
+
+const createResourceTable = `
+CREATE TABLE IF NOT EXISTS "RESOURCE" (
+ id SERIAL PRIMARY KEY,
+ link VARCHAR(255) NOT NULL,
+ typeId INTEGER REFERENCES "TYPE"(id),
+ userId INTEGER REFERENCES "GITHUBUSER"(id)
+);
+`;
+
+const createUserResourceTable = `
+CREATE TABLE IF NOT EXISTS "USER_RESOURCE" (
+ id SERIAL PRIMARY KEY,
+ userId INTEGER REFERENCES "GITHUBUSER"(id),
+ resourceId INTEGER REFERENCES "RESOURCE"(id),
+ UNIQUE(userId, resourceId)
+);
+`;
+
+const createTables = async () => {
+ try {
+ await pool.query(createUserTable);
+ console.log('🎉 GITHUBUSER table created successfully');
+ await pool.query(createPostTable);
+ console.log('🎉 POST table created successfully');
+ await pool.query(createCommentTable);
+ console.log('🎉 COMMENT table created successfully');
+ await pool.query(createTypeTable);
+ console.log('🎉 TYPE table created successfully');
+ await pool.query(createResourceTable);
+ console.log('🎉 RESOURCE table created successfully');
+ await pool.query(createUserResourceTable);
+ console.log('🎉 USER_RESOURCE table created successfully');
+
+ } catch (err) {
+ console.error('⚠️ Error creating tables:', err);
+ }
+};
diff --git a/server/controllers/CommentController.js b/server/controllers/CommentController.js
new file mode 100644
index 000000000..f892e9cc2
--- /dev/null
+++ b/server/controllers/CommentController.js
@@ -0,0 +1,25 @@
+import { pool } from '../config/database.js';
+
+export const getAllComments = async (req, res) => {
+ try {
+ const result = await pool.query('SELECT * FROM "COMMENT"');
+ res.status(200).json(result.rows);
+ } catch (error) {
+ res.status(500).json({ error: error.message });
+ }
+};
+
+export const createComment = async (req, res) => {
+ const { content, postId } = req.body;
+
+ try {
+ const result = await pool.query('INSERT INTO "COMMENT" (content, postId) VALUES ($1, $2) RETURNING *', [content, postId]);
+ res.status(201).json(result.rows[0]);
+ } catch (error) {
+ res.status(500).json({ error: error.message });
+ }
+};
+
+// Implement these functions if needed
+// export const updateComment = async (req, res) => { ... };
+// export const deleteComment = async (req, res) => { ... };
diff --git a/server/controllers/PostController.js b/server/controllers/PostController.js
new file mode 100644
index 000000000..a2f9c4d8f
--- /dev/null
+++ b/server/controllers/PostController.js
@@ -0,0 +1,69 @@
+import { pool } from '../config/database.js';
+
+export const getAllPosts = async (req, res) => {
+ try {
+ const result = await pool.query('SELECT * FROM "POST"');
+ res.status(200).json(result.rows);
+ } catch (error) {
+ res.status(500).json({ error: error.message });
+ }
+};
+
+export const createPost = async (req, res) => {
+ const { content, userId, title } = req.body;
+
+ try {
+ const result = await pool.query('INSERT INTO "POST" (content, userId,title) VALUES ($1, $2, $3) RETURNING *', [content, userId, title]);
+ res.status(201).json(result.rows[0]);
+ } catch (error) {
+ res.status(500).json({ error: error.message });
+ }
+};
+
+export const getPostById = async (req, res) => {
+ const { id } = req.params;
+ try {
+ const result = await pool.query('SELECT * FROM "POST" WHERE id = $1', [id]);
+ if (result.rows.length === 0) {
+ return res.status(404).json({ message: 'Post not found' });
+ }
+ res.status(200).json(result.rows[0]);
+ } catch (error) {
+ res.status(500).json({ error: error.message });
+ }
+};
+
+
+export const updatePost = async (req, res) => {
+ const postId = req.params.id;
+ const { content, title } = req.body;
+
+ try {
+ const result = await pool.query(
+ 'UPDATE "POST" SET content = $1, title = $2 WHERE id = $3 RETURNING *',
+ [content, title, postId]
+ );
+
+ if (result.rowCount === 0) {
+ return res.status(404).json({ message: 'Post not found' });
+ }
+
+ res.status(200).json(result.rows[0]);
+ } catch (error) {
+ res.status(500).json({ error: error.message });
+ }
+ };
+
+
+ export const deletePost = async (req, res) => {
+ const postId = req.params.id;
+ try {
+ const result = await pool.query('DELETE FROM "POST" WHERE id = $1', [postId]);
+ if (result.rowCount === 0) {
+ return res.status(404).json({ message: 'Post not found' });
+ }
+ res.status(200).json({ message: 'Post deleted successfully' });
+ } catch (error) {
+ res.status(500).json({ error: error.message });
+ }
+};
diff --git a/server/controllers/ResourceController.js b/server/controllers/ResourceController.js
new file mode 100644
index 000000000..6ccb51114
--- /dev/null
+++ b/server/controllers/ResourceController.js
@@ -0,0 +1,51 @@
+import { pool } from '../config/database.js';
+
+export const getAllResources = async (req, res) => {
+ try {
+ const result = await pool.query(
+ 'SELECT R.*, T.name as type FROM "RESOURCE" R ' +
+ 'INNER JOIN "TYPE" T ON R.typeId = T.id'
+ );
+ res.status(200).json(result.rows);
+ } catch (error) {
+ res.status(500).json({ error: error.message });
+ }
+};
+
+
+export const createResource = async (req, res) => {
+ const { link, typeId, userId } = req.body;
+
+ try {
+ const resourceResult = await pool.query(
+ 'INSERT INTO "RESOURCE" (link, typeId) VALUES ($1, $2) RETURNING *',
+ [link, typeId]
+ );
+ const resourceId = resourceResult.rows[0].id;
+
+ await pool.query(
+ 'INSERT INTO "USER_RESOURCE" (userId, resourceId) VALUES ($1, $2)',
+ [userId, resourceId]
+ );
+
+ res.status(201).json(resourceResult.rows[0]);
+ } catch (error) {
+ res.status(500).json({ error: error.message });
+ }
+};
+
+
+export const shareResource = async (req, res) => {
+ const { userId, resourceId } = req.body;
+
+ try {
+ await pool.query(
+ 'INSERT INTO "USER_RESOURCE" (userId, resourceId) VALUES ($1, $2) ON CONFLICT (userId, resourceId) DO NOTHING',
+ [userId, resourceId]
+ );
+
+ res.status(200).json({ message: 'Resource shared successfully' });
+ } catch (error) {
+ res.status(500).json({ error: error.message });
+ }
+};
diff --git a/server/controllers/TypeController.js b/server/controllers/TypeController.js
new file mode 100644
index 000000000..87c3d1177
--- /dev/null
+++ b/server/controllers/TypeController.js
@@ -0,0 +1,25 @@
+import { pool } from '../config/database.js';
+
+export const getAllTypes = async (req, res) => {
+ try {
+ const result = await pool.query('SELECT * FROM "TYPE"');
+ res.status(200).json(result.rows);
+ } catch (error) {
+ res.status(500).json({ error: error.message });
+ }
+};
+
+export const createType = async (req, res) => {
+ const { name } = req.body;
+
+ try {
+ const result = await pool.query('INSERT INTO "TYPE" (name) VALUES ($1) RETURNING *', [name]);
+ res.status(201).json(result.rows[0]);
+ } catch (error) {
+ res.status(500).json({ error: error.message });
+ }
+};
+
+// Implement these functions if needed
+// export const updateType = async (req, res) => { ... };
+// export const deleteType = async (req, res) => { ... };
diff --git a/server/controllers/UserController.js b/server/controllers/UserController.js
new file mode 100644
index 000000000..793541966
--- /dev/null
+++ b/server/controllers/UserController.js
@@ -0,0 +1,25 @@
+import { pool } from '../config/database.js';
+
+export const getAllUsers = async (req, res) => {
+ try {
+ const result = await pool.query('SELECT * FROM "GITHUBUSER"');
+ res.status(200).json(result.rows);
+ } catch (error) {
+ res.status(500).json({ error: error.message });
+ }
+};
+
+// export const createUser = async (req, res) => {
+// const { username, password } = req.body;
+
+// try {
+// const result = await pool.query('INSERT INTO "USER" (username, password) VALUES ($1, $2) RETURNING *', [username, password]);
+// res.status(201).json(result.rows[0]);
+// } catch (error) {
+// res.status(500).json({ error: error.message });
+// }
+// };
+
+// Implement these functions if needed
+// export const updateUser = async (req, res) => { ... };
+// export const deleteUser = async (req, res) => { ... };
diff --git a/server/data/mockData.js b/server/data/mockData.js
new file mode 100644
index 000000000..0d903b2a0
--- /dev/null
+++ b/server/data/mockData.js
@@ -0,0 +1,70 @@
+const mockData = {
+ // "users": [
+ // { "id": 1, "username": "user1", "password": "password1" },
+ // { "id": 2, "username": "user2", "password": "password2" },
+ // { "id": 3, "username": "user3", "password": "password3" },
+ // { "id": 4, "username": "user4", "password": "password4" },
+ // { "id": 5, "username": "user5", "password": "password5" },
+ // { "id": 6, "username": "user6", "password": "password6" },
+ // { "id": 7, "username": "user7", "password": "password7" },
+ // { "id": 8, "username": "user8", "password": "password8" },
+ // { "id": 9, "username": "user9", "password": "password9" },
+ // { "id": 10, "username": "user10", "password": "password10" },
+ // { "id": 11, "username": "user11", "password": "password11" }
+ // ],
+ // "posts": [
+ // { "id": 1, "content": "Post 1 content", "userId": 1 },
+ // { "id": 2, "content": "Post 2 content", "userId": 2 },
+ // { "id": 3, "content": "Post 3 content", "userId": 3 },
+ // { "id": 4, "content": "Post 4 content", "userId": 4 },
+ // { "id": 5, "content": "Post 5 content", "userId": 5 },
+ // { "id": 6, "content": "Post 6 content", "userId": 6 },
+ // { "id": 7, "content": "Post 7 content", "userId": 7 },
+ // { "id": 8, "content": "Post 8 content", "userId": 8 },
+ // { "id": 9, "content": "Post 9 content", "userId": 9 },
+ // { "id": 10, "content": "Post 10 content", "userId": 10 },
+ // { "id": 11, "content": "Post 11 content", "userId": 11 }
+ // ],
+ // "comments": [
+ // { "id": 1, "content": "Comment 1 content", "postId": 1 },
+ // { "id": 2, "content": "Comment 2 content", "postId": 2 },
+ // { "id": 3, "content": "Comment 3 content", "postId": 3 },
+ // { "id": 4, "content": "Comment 4 content", "postId": 4 },
+ // { "id": 5, "content": "Comment 5 content", "postId": 5 },
+ // { "id": 6, "content": "Comment 6 content", "postId": 6 },
+ // { "id": 7, "content": "Comment 7 content", "postId": 7 },
+ // { "id": 8, "content": "Comment 8 content", "postId": 8 },
+ // { "id": 9, "content": "Comment 9 content", "postId": 9 },
+ // { "id": 10, "content": "Comment 10 content", "postId": 10 },
+ // { "id": 11, "content": "Comment 11 content", "postId": 11 }
+ // ],
+ "resources": [
+ { id: 1, type: 'youtube', link: 'https://www.youtube.com/watch?v=eWRfhZUzrAc&list=PLWKjhJtqVAbnqBxcdjVGgT3uVR10bzTEB', title: 'Python (Beginner)' },
+ { id: 2, type: 'youtube', link: 'https://www.youtube.com/watch?v=8jLOx1hD3_o', title: 'C++ Beginner to Advance' },
+ { id: 3, type: 'youtube', link: 'https://www.youtube.com/watch?v=eIrMbAQSU34', title: 'Java Beginner' },
+ { id: 4, type: 'youtube', link: 'https://www.youtube.com/watch?v=Q33KBiDriJY&list=PL9ooVrP1hQOEloRCBI97ZXkWUg6MJn0Yf', title: 'Web Development Full Course' },
+ { id: 5, type: 'coding', link: 'https://leetcode.com/tag/array/', title: 'Array' },
+ { id: 6, type: 'coding', link: 'https://leetcode.com/tag/binary-tree/', title: 'Binary Tree' },
+ { id: 6, type: 'coding', link: 'https://leetcode.com/tag/hash-table/', title: 'Hash Table' },
+ { id: 6, type: 'coding', link: 'https://leetcode.com/tag/dynamic-programming/', title: 'Dynamic Programming' },
+ ],
+
+ "events":[
+ { category: 'hackathon', title: 'Global Hack Week: Career Week', link: 'https://events.mlh.io/events/10077?_gl=1*bok2g4*_ga*MjEwMjQyNTIxMC4xNjk5NzMwODY2*_ga_E5KT6TC4TK*MTcwMDE0OTI1NC4yLjAuMTcwMDE0OTI1NC4wLjAuMA..', level: 'Beginner' },
+ { category: 'hackathon', title: 'Generate the future in the 404', link: 'https://www.aiatl.io/', level: 'Intermediate' },
+ { category: 'hackathon', title: 'Boston Hacks', link: 'https://bostonhacks.org/', level: 'Beginner' },
+ { category: 'hackathon', title: 'Microsoft AI Classroom Hackathon', link: 'https://microsoftaiclassroom.devpost.com/?ref_feature=challenge&ref_medium=discover', level: 'Intermediate' },
+ { category: 'hackathon', title: 'Google’s Immersive Geospatial Challenge', link: 'https://googlesimmersive.devpost.com/?ref_feature=challenge&ref_medium=discover', level: 'Beginner'},
+ { category: 'hackathon', title: 'MLH Month Long Hackathon', link: 'https://hackfest-november.devpost.com/?ref_feature=challenge&ref_medium=discover', level: 'Beginner' },
+ ]
+ // "types": [
+ // { "id": 1, "name": "Type 1" },
+ // { "id": 2, "name": "Type 2" },
+ // { "id": 3, "name": "Type 3" }
+ // ]
+};
+
+module.exports = {
+ events: mockData.events,
+ resources: mockData.resources,
+};
\ No newline at end of file
diff --git a/server/package-lock.json b/server/package-lock.json
new file mode 100644
index 000000000..837fb8391
--- /dev/null
+++ b/server/package-lock.json
@@ -0,0 +1,1366 @@
+{
+ "name": "server",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "server",
+ "version": "1.0.0",
+ "dependencies": {
+ "connect-pg-simple": "^9.0.1",
+ "cors": "^2.8.5",
+ "dotenv": "^16.3.1",
+ "express": "^4.18.2",
+ "express-session": "^1.17.3",
+ "passport": "^0.6.0",
+ "passport-github2": "^0.1.12",
+ "pg": "^8.11.3"
+ },
+ "devDependencies": {
+ "nodemon": "^3.0.1"
+ }
+ },
+ "node_modules/abbrev": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+ "dev": true
+ },
+ "node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "node_modules/base64url": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz",
+ "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/body-parser": {
+ "version": "1.20.1",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
+ "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
+ "dependencies": {
+ "bytes": "3.1.2",
+ "content-type": "~1.0.4",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.4.24",
+ "on-finished": "2.4.1",
+ "qs": "6.11.0",
+ "raw-body": "2.5.1",
+ "type-is": "~1.6.18",
+ "unpipe": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "dependencies": {
+ "fill-range": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/buffer-writer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
+ "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz",
+ "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==",
+ "dependencies": {
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.1",
+ "set-function-length": "^1.1.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+ "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ ],
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "node_modules/connect-pg-simple": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/connect-pg-simple/-/connect-pg-simple-9.0.1.tgz",
+ "integrity": "sha512-BuwWJH3K3aLpONkO9s12WhZ9ceMjIBxIJAh0JD9x4z1Y9nShmWqZvge5PG/+4j2cIOcguUoa2PSQ4HO/oTsrVg==",
+ "dependencies": {
+ "pg": "^8.8.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/content-disposition": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
+ "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
+ },
+ "node_modules/cors": {
+ "version": "2.8.5",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+ "dependencies": {
+ "object-assign": "^4",
+ "vary": "^1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
+ "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==",
+ "dependencies": {
+ "get-intrinsic": "^1.2.1",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/destroy": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "16.3.1",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
+ "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/motdotla/dotenv?sponsor=1"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
+ },
+ "node_modules/encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/express": {
+ "version": "4.18.2",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
+ "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
+ "dependencies": {
+ "accepts": "~1.3.8",
+ "array-flatten": "1.1.1",
+ "body-parser": "1.20.1",
+ "content-disposition": "0.5.4",
+ "content-type": "~1.0.4",
+ "cookie": "0.5.0",
+ "cookie-signature": "1.0.6",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "1.2.0",
+ "fresh": "0.5.2",
+ "http-errors": "2.0.0",
+ "merge-descriptors": "1.0.1",
+ "methods": "~1.1.2",
+ "on-finished": "2.4.1",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "0.1.7",
+ "proxy-addr": "~2.0.7",
+ "qs": "6.11.0",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.2.1",
+ "send": "0.18.0",
+ "serve-static": "1.15.0",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/express-session": {
+ "version": "1.17.3",
+ "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz",
+ "integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==",
+ "dependencies": {
+ "cookie": "0.4.2",
+ "cookie-signature": "1.0.6",
+ "debug": "2.6.9",
+ "depd": "~2.0.0",
+ "on-headers": "~1.0.2",
+ "parseurl": "~1.3.3",
+ "safe-buffer": "5.2.1",
+ "uid-safe": "~2.1.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/express-session/node_modules/cookie": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+ "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/finalhandler": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
+ "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
+ "dependencies": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "2.4.1",
+ "parseurl": "~1.3.3",
+ "statuses": "2.0.1",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
+ "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==",
+ "dependencies": {
+ "function-bind": "^1.1.2",
+ "has-proto": "^1.0.1",
+ "has-symbols": "^1.0.3",
+ "hasown": "^2.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
+ "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
+ "dependencies": {
+ "get-intrinsic": "^1.1.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz",
+ "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==",
+ "dependencies": {
+ "get-intrinsic": "^1.2.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
+ "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
+ "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+ "dependencies": {
+ "depd": "2.0.0",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "toidentifier": "1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ignore-by-default": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
+ "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
+ "dev": true
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+ "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
+ },
+ "node_modules/methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/nodemon": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.1.tgz",
+ "integrity": "sha512-g9AZ7HmkhQkqXkRc20w+ZfQ73cHLbE8hnPbtaFbFtCumZsjyMhKk9LajQ07U5Ux28lvFjZ5X7HvWR1xzU8jHVw==",
+ "dev": true,
+ "dependencies": {
+ "chokidar": "^3.5.2",
+ "debug": "^3.2.7",
+ "ignore-by-default": "^1.0.1",
+ "minimatch": "^3.1.2",
+ "pstree.remy": "^1.1.8",
+ "semver": "^7.5.3",
+ "simple-update-notifier": "^2.0.0",
+ "supports-color": "^5.5.0",
+ "touch": "^3.1.0",
+ "undefsafe": "^2.0.5"
+ },
+ "bin": {
+ "nodemon": "bin/nodemon.js"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/nodemon"
+ }
+ },
+ "node_modules/nodemon/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/nodemon/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true
+ },
+ "node_modules/nopt": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
+ "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==",
+ "dev": true,
+ "dependencies": {
+ "abbrev": "1"
+ },
+ "bin": {
+ "nopt": "bin/nopt.js"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/oauth": {
+ "version": "0.9.15",
+ "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
+ "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA=="
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
+ "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/on-headers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+ "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/packet-reader": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
+ "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/passport": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz",
+ "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==",
+ "dependencies": {
+ "passport-strategy": "1.x.x",
+ "pause": "0.0.1",
+ "utils-merge": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/jaredhanson"
+ }
+ },
+ "node_modules/passport-github2": {
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/passport-github2/-/passport-github2-0.1.12.tgz",
+ "integrity": "sha512-3nPUCc7ttF/3HSP/k9sAXjz3SkGv5Nki84I05kSQPo01Jqq1NzJACgMblCK0fGcv9pKCG/KXU3AJRDGLqHLoIw==",
+ "dependencies": {
+ "passport-oauth2": "1.x.x"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/passport-oauth2": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.7.0.tgz",
+ "integrity": "sha512-j2gf34szdTF2Onw3+76alNnaAExlUmHvkc7cL+cmaS5NzHzDP/BvFHJruueQ9XAeNOdpI+CH+PWid8RA7KCwAQ==",
+ "dependencies": {
+ "base64url": "3.x.x",
+ "oauth": "0.9.x",
+ "passport-strategy": "1.x.x",
+ "uid2": "0.0.x",
+ "utils-merge": "1.x.x"
+ },
+ "engines": {
+ "node": ">= 0.4.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/jaredhanson"
+ }
+ },
+ "node_modules/passport-strategy": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
+ "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/path-to-regexp": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+ "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
+ },
+ "node_modules/pause": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
+ "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
+ },
+ "node_modules/pg": {
+ "version": "8.11.3",
+ "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz",
+ "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==",
+ "dependencies": {
+ "buffer-writer": "2.0.0",
+ "packet-reader": "1.0.0",
+ "pg-connection-string": "^2.6.2",
+ "pg-pool": "^3.6.1",
+ "pg-protocol": "^1.6.0",
+ "pg-types": "^2.1.0",
+ "pgpass": "1.x"
+ },
+ "engines": {
+ "node": ">= 8.0.0"
+ },
+ "optionalDependencies": {
+ "pg-cloudflare": "^1.1.1"
+ },
+ "peerDependencies": {
+ "pg-native": ">=3.0.1"
+ },
+ "peerDependenciesMeta": {
+ "pg-native": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/pg-cloudflare": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz",
+ "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==",
+ "optional": true
+ },
+ "node_modules/pg-connection-string": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz",
+ "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA=="
+ },
+ "node_modules/pg-int8": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
+ "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/pg-pool": {
+ "version": "3.6.1",
+ "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz",
+ "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==",
+ "peerDependencies": {
+ "pg": ">=8.0"
+ }
+ },
+ "node_modules/pg-protocol": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz",
+ "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q=="
+ },
+ "node_modules/pg-types": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
+ "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
+ "dependencies": {
+ "pg-int8": "1.0.1",
+ "postgres-array": "~2.0.0",
+ "postgres-bytea": "~1.0.0",
+ "postgres-date": "~1.0.4",
+ "postgres-interval": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/pgpass": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
+ "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
+ "dependencies": {
+ "split2": "^4.1.0"
+ }
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postgres-array": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
+ "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postgres-bytea": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
+ "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/postgres-date": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
+ "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/postgres-interval": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
+ "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
+ "dependencies": {
+ "xtend": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/pstree.remy": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
+ "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
+ "dev": true
+ },
+ "node_modules/qs": {
+ "version": "6.11.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
+ "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
+ "dependencies": {
+ "side-channel": "^1.0.4"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/random-bytes": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
+ "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
+ "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
+ "dependencies": {
+ "bytes": "3.1.2",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.4.24",
+ "unpipe": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
+ "node_modules/semver": {
+ "version": "7.5.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+ "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/send": {
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
+ "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
+ "dependencies": {
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "2.0.0",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "2.4.1",
+ "range-parser": "~1.2.1",
+ "statuses": "2.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/send/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+ },
+ "node_modules/serve-static": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
+ "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
+ "dependencies": {
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "0.18.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/set-function-length": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
+ "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==",
+ "dependencies": {
+ "define-data-property": "^1.1.1",
+ "get-intrinsic": "^1.2.1",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
+ },
+ "node_modules/side-channel": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+ "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+ "dependencies": {
+ "call-bind": "^1.0.0",
+ "get-intrinsic": "^1.0.2",
+ "object-inspect": "^1.9.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/simple-update-notifier": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
+ "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
+ "dev": true,
+ "dependencies": {
+ "semver": "^7.5.3"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/split2": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
+ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+ "engines": {
+ "node": ">= 10.x"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/touch": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
+ "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
+ "dev": true,
+ "dependencies": {
+ "nopt": "~1.0.10"
+ },
+ "bin": {
+ "nodetouch": "bin/nodetouch.js"
+ }
+ },
+ "node_modules/type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "dependencies": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/uid-safe": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
+ "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
+ "dependencies": {
+ "random-bytes": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/uid2": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz",
+ "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA=="
+ },
+ "node_modules/undefsafe": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
+ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
+ "dev": true
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "engines": {
+ "node": ">=0.4"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ }
+ }
+}
diff --git a/server/package.json b/server/package.json
new file mode 100644
index 000000000..ad41ba56e
--- /dev/null
+++ b/server/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "server",
+ "version": "1.0.0",
+ "type": "module",
+ "main": "server.js",
+ "scripts": {
+ "start": "npm run reset && nodemon --require dotenv/config server.js",
+ "dev": "nodemon --require dotenv/config server.js",
+ "reset": "cd config && node --require dotenv/config reset.js"
+ },
+ "dependencies": {
+ "cors": "^2.8.5",
+ "dotenv": "^16.3.1",
+ "express": "^4.18.2",
+ "express-session": "^1.17.3",
+ "passport": "^0.6.0",
+ "passport-github2": "^0.1.12",
+ "pg": "^8.11.3",
+ "connect-pg-simple": "^9.0.1"
+ },
+ "devDependencies": {
+ "nodemon": "^3.0.1"
+ }
+ }
+
\ No newline at end of file
diff --git a/server/routes/CommentRoutes.js b/server/routes/CommentRoutes.js
new file mode 100644
index 000000000..2b6fe2231
--- /dev/null
+++ b/server/routes/CommentRoutes.js
@@ -0,0 +1,12 @@
+import express from 'express';
+import { getAllComments, createComment } from '../controllers/CommentController.js';
+
+const router = express.Router();
+
+router.get('/', getAllComments);
+router.post('/', createComment);
+// Uncomment and implement these in CommentController.js if needed
+// router.patch('/:id', updateComment);
+// router.delete('/:id', deleteComment);
+
+export default router;
diff --git a/server/routes/PostRoutes.js b/server/routes/PostRoutes.js
new file mode 100644
index 000000000..8ea7706d0
--- /dev/null
+++ b/server/routes/PostRoutes.js
@@ -0,0 +1,12 @@
+import express from 'express';
+import { getAllPosts, createPost, getPostById, updatePost, deletePost } from '../controllers/PostController.js';
+
+const router = express.Router();
+
+router.get('/', getAllPosts);
+router.post('/', createPost);
+router.get('/:id', getPostById);
+router.put('/:id', updatePost);
+router.delete('/:id', deletePost);
+
+export default router;
\ No newline at end of file
diff --git a/server/routes/ResourceRoutes.js b/server/routes/ResourceRoutes.js
new file mode 100644
index 000000000..b319c094d
--- /dev/null
+++ b/server/routes/ResourceRoutes.js
@@ -0,0 +1,13 @@
+import express from 'express';
+import { getAllResources, createResource, shareResource } from '../controllers/ResourceController.js';
+
+const router = express.Router();
+
+router.get('/', getAllResources);
+router.post('/', createResource);
+router.post('/share', shareResource);
+// Uncomment and implement these in ResourceController.js if needed
+// router.patch('/:id', updateResource);
+// router.delete('/:id', deleteResource);
+
+export default router;
diff --git a/server/routes/TypeRoutes.js b/server/routes/TypeRoutes.js
new file mode 100644
index 000000000..2f88c170e
--- /dev/null
+++ b/server/routes/TypeRoutes.js
@@ -0,0 +1,12 @@
+import express from 'express';
+import { getAllTypes, createType } from '../controllers/TypeController.js';
+
+const router = express.Router();
+
+router.get('/', getAllTypes);
+router.post('/', createType);
+// Uncomment and implement these in TypeController.js if needed
+// router.patch('/:id', updateType);
+// router.delete('/:id', deleteType);
+
+export default router;
diff --git a/server/routes/UserRoutes.js b/server/routes/UserRoutes.js
new file mode 100644
index 000000000..33219f868
--- /dev/null
+++ b/server/routes/UserRoutes.js
@@ -0,0 +1,12 @@
+import express from 'express';
+import { getAllUsers } from '../controllers/UserController.js';
+
+const router = express.Router();
+
+router.get('/', getAllUsers);
+// router.post('/', createUser);
+// Uncomment and implement these in UserController.js if needed
+// router.patch('/:id', updateUser);
+// router.delete('/:id', deleteUser);
+
+export default router;
diff --git a/server/routes/auth.js b/server/routes/auth.js
new file mode 100644
index 000000000..44fc76c34
--- /dev/null
+++ b/server/routes/auth.js
@@ -0,0 +1,57 @@
+import express from 'express'
+import passport from 'passport'
+
+const router = express.Router()
+
+router.get('/login/success', (req, res) => {
+ if (req.isAuthenticated()) {
+ res.json({ success: true, message: 'Login successful', user: req.user });
+ } else {
+ res.status(401).json({ success: false, message: 'Not authenticated' });
+ }
+});
+
+router.get('/login/failed', (req, res) => {
+ res.status(401).json({ success: true, message: 'failure' })
+})
+
+router.get('/logout', (req, res, next) => {
+ req.logout((err) => {
+ if (err) {
+ return next(err)
+ }
+
+ req.session.destroy((err) => {
+ res.clearCookie('connect.sid')
+ res.json({ status: 'logout', user: {} })
+ })
+ })
+})
+
+router.get('/github', passport.authenticate('github', {
+ scope: ['read:user']
+}))
+
+router.get('/github/callback', (req, res, next) => {
+ passport.authenticate('github', (err, user, info) => {
+ console.log('GitHub Callback Hit');
+ if (err) {
+ console.error('Authentication Error:', err);
+ return next(err);
+ }
+ if (!user) {
+ console.error('No User Returned:', info);
+ return res.redirect('/login/failed');
+ }
+ req.logIn(user, (err) => {
+ if (err) {
+ console.error('Login Error:', err);
+ return next(err);
+ }
+ // Redirect to the frontend URL after successful login
+ return res.redirect('http://localhost:5173/');
+ });
+ })(req, res, next);
+});
+
+export default router
\ No newline at end of file
diff --git a/server/server.js b/server/server.js
new file mode 100644
index 000000000..a40ef8c05
--- /dev/null
+++ b/server/server.js
@@ -0,0 +1,106 @@
+import express from 'express';
+import cors from 'cors';
+import { fileURLToPath } from 'url';
+import path from 'path';
+import UserRoutes from './routes/UserRoutes.js';
+import PostRoutes from './routes/PostRoutes.js';
+import CommentRoutes from './routes/CommentRoutes.js';
+import ResourceRoutes from './routes/ResourceRoutes.js';
+import TypeRoutes from './routes/TypeRoutes.js';
+import authRoutes from './routes/auth.js';
+import { pool } from './config/database.js';
+import passport, { GitHub } from './config/auth.js'; // import modified passport with GitHub strategy
+import session from 'express-session'
+import connectPgSimple from 'connect-pg-simple'; // Import the module
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+console.log('Environment Variables:');
+console.log('GITHUB_CLIENT_ID:', process.env.GITHUB_CLIENT_ID);
+console.log('GITHUB_CLIENT_SECRET:', process.env.GITHUB_CLIENT_SECRET);
+console.log('GITHUB_CALLBACK_URL:', process.env.GITHUB_CALLBACK_URL);
+console.log('API_URL:', process.env.API_URL);
+console.log("Environment: ", process.env.NODE_ENV);
+
+const CLIENT_URL = process.env.NODE_ENV === 'production' ? 'https://codefm-client-production.up.railway.app' : 'http://localhost:5173';
+console.log("Client URL: ", CLIENT_URL);
+
+
+const app = express();
+
+const PgSession = connectPgSimple(session);
+
+app.use(session({
+ secret: 'myeshafarnaz',
+ resave: false,
+ saveUninitialized: true,
+}));
+
+app.use(express.json());
+// app.use(cors({
+// origin: 'http://localhost:5173',
+// methods: 'GET,POST,PUT,DELETE,PATCH',
+// credentials: true,
+// }));
+
+app.use(cors({
+ origin: CLIENT_URL,
+ methods: 'GET,POST,PUT,DELETE,PATCH',
+ credentials: true,
+}));
+
+
+app.use(passport.initialize());
+app.use(passport.session());
+
+passport.use(GitHub);
+
+passport.serializeUser((user, done) => {
+ done(null, user.id);
+});
+
+passport.deserializeUser(async (id, done) => {
+ try {
+ const result = await pool.query('SELECT * FROM GITHUBUSER WHERE id = $1', [id]);
+ if (result.rows.length > 0) {
+ done(null, result.rows[0]);
+ } else {
+ done(new Error('User not found'), null);
+ }
+ } catch (error) {
+ done(error, null);
+ }
+});
+
+// app.get('/', (req, res) => {
+// res.redirect(CLIENT_URL);
+// });
+
+app.get('/login/success', (req, res) => {
+ if (req.isAuthenticated()) {
+ res.json({ success: true, user: req.user });
+ } else {
+ res.status(401).json({ success: false, message: 'Not authenticated' });
+ }
+});
+
+
+app.use('/auth', authRoutes);
+app.use('/api/users', UserRoutes);
+app.use('/api/posts', PostRoutes);
+app.use('/api/comments', CommentRoutes);
+app.use('/api/resources', ResourceRoutes);
+app.use('/api/types', TypeRoutes);
+
+const PORT = process.env.PORT || 3001;
+
+app.use(express.static(path.join(__dirname, 'public')));
+
+app.get('*', (req, res) => {
+ res.sendFile(path.join(__dirname, 'public', 'index.html'));
+});
+
+app.listen(PORT, () => {
+ console.log(`🚀 Server running on http://localhost:${PORT}`);
+});
diff --git a/wireframes/Discussion page (after login).png b/wireframes/Discussion page (after login).png
new file mode 100644
index 000000000..d15e8824f
Binary files /dev/null and b/wireframes/Discussion page (after login).png differ
diff --git a/wireframes/Event_page.png b/wireframes/Event_page.png
new file mode 100644
index 000000000..193a46509
Binary files /dev/null and b/wireframes/Event_page.png differ
diff --git a/wireframes/Signup.png b/wireframes/Signup.png
new file mode 100644
index 000000000..ef159b379
Binary files /dev/null and b/wireframes/Signup.png differ
diff --git a/wireframes/User_profile.png b/wireframes/User_profile.png
new file mode 100644
index 000000000..aa1538a36
Binary files /dev/null and b/wireframes/User_profile.png differ
diff --git a/wireframes/badges.png b/wireframes/badges.png
new file mode 100644
index 000000000..02bf48011
Binary files /dev/null and b/wireframes/badges.png differ
diff --git a/wireframes/homepage(before).png b/wireframes/homepage(before).png
new file mode 100644
index 000000000..0c04a919d
Binary files /dev/null and b/wireframes/homepage(before).png differ
diff --git a/wireframes/login.png b/wireframes/login.png
new file mode 100644
index 000000000..772791d10
Binary files /dev/null and b/wireframes/login.png differ
diff --git a/wireframes/resources.png b/wireframes/resources.png
new file mode 100644
index 000000000..f02d09009
Binary files /dev/null and b/wireframes/resources.png differ