114 Commits

Author SHA1 Message Date
Nicholas Pease 584510d0f3 Refactor Authentication Flow (#44) 2024-03-23 16:54:19 -04:00
npease 12cfc6f6a0 Remove Dependency 2024-03-22 20:05:11 +00:00
npease 2ec3b93190 Polished Off, Bloatware Reduced 2024-03-22 06:24:12 +00:00
npease 961e2b4587 Polishing Touches, Remove of Unnecessary Files, Register -> Onboard 2024-03-22 05:42:35 +00:00
npease eaadade0ed Mostly There, Fine Tuning Required 2024-03-22 04:59:40 +00:00
npease 564524772e Login Refactored, TODO: Delete old API and other references 2024-03-21 22:55:41 +00:00
npease b2c21d0782 Finalize THIS refactor 2024-03-21 22:28:27 +00:00
npease fa29041489 Cleanup 2024-03-21 18:28:57 +00:00
npease 296f21a082 Initial Refactor 2024-03-21 18:22:43 +00:00
ClarkLach ed3449a1f7 Added Username Colors in Chats (#41) 2024-03-19 14:25:16 -04:00
ClarkLach 9f84ee233d Merge branch 'main' into clarkl-mar14 2024-03-19 14:23:57 -04:00
Nicholas Pease 1396d03b76 Fix Borked Nearby Update (#40) 2024-03-19 14:20:00 -04:00
Nicholas Pease ba0575f315 Merge branch 'main' into npease-hotfix-39 2024-03-14 18:39:09 -04:00
ClarkLach 644206635f Added username colors in chats
Ideally this will be customizable at some point
2024-03-14 18:31:05 -04:00
ClarkLach 2b6bc641d4 Update .gitignore
DS_store activity
2024-03-14 17:13:16 -04:00
ClarkLach 0962df2187 Uploaded Deliverables and Deleted Old Files (#35) 2024-03-14 16:54:32 -04:00
npease 7212a532f6 Fix Borked Nearby Update 2024-03-12 04:28:46 +00:00
Nicholas Pease 85cd7c20f4 Add Nearby Room Counter for Unauthenticated / New Users (#38) 2024-03-12 00:22:01 -04:00
Nicholas Pease 844bb14040 Add Online, Offline, Saving Capabilities (#36) 2024-03-12 00:21:36 -04:00
Nicholas Pease 2e0ac0ba0f Merge branch 'npease-profiles-v1' into npease-chatrooms-profile 2024-03-12 00:19:43 -04:00
npease ea3647ecd3 Rooms appear on big map 2024-03-10 20:25:20 +00:00
npease 2796172d30 Fix Maps for Chatroom 2024-03-10 20:04:10 +00:00
npease 0c8ca18326 Add counter for unauthenticated users on homepage to show rooms nearby 2024-03-10 17:39:08 +00:00
npease b1009b186e Fix Added Members, onValue for Room List, Nearby 2024-03-10 16:43:08 +00:00
Nicholas Pease 72ae128975 Merge branch 'main' into npease-chatrooms-profile 2024-03-10 01:29:55 -05:00
npease 9802ea8096 Final myRooms Fix 2024-03-10 06:27:31 +00:00
npease aa49341937 myRooms Working 2024-03-10 06:10:39 +00:00
npease 29d6833e42 Initial Working Online/Offline Members 2024-03-10 04:22:42 +00:00
npease d26e7d4290 WIP 2024-03-06 04:26:36 +00:00
npease 1f22895904 Prelim Chat Sidebar Data Working 2024-03-05 06:01:28 +00:00
npease 319f066edf Main App Page Refactor, Commenting, Chat Room Sidebar Work 2024-03-05 05:38:23 +00:00
npease c77f16d3fc UI Worked On, Need to Pass Chatroom Path Data to sidebar somehow 2024-03-04 05:30:42 +00:00
ClarkLach c8d7e40203 Update README.md
fixed missing command
2024-03-03 23:56:01 -05:00
ClarkLach e1572cd84a Delete .github/workflows directory 2024-03-03 23:55:09 -05:00
ClarkLach 1e5f462abd Delete deliverables/deliverable_2/filler.txt 2024-03-03 23:53:40 -05:00
ClarkLach 973096d480 Delete deliverables/deliverable_1/filler.txt 2024-03-03 23:53:28 -05:00
ClarkLach 5aa1c888e1 Delete deliverables/deliverable_0/filler.txt 2024-03-03 23:53:15 -05:00
ClarkLach 594c09ba65 Delete deliverables/filler.txt 2024-03-03 23:53:04 -05:00
ClarkLach a883e3112e Uploaded deliverable 1 files 2024-03-03 23:52:29 -05:00
ClarkLach b87c6ee600 Uploaded Deliverable 0 Files 2024-03-03 23:50:40 -05:00
ClarkLach 94b8591643 Create deliverable 0 2024-03-03 23:50:11 -05:00
ClarkLach b7a30c3d6d Create deliverable 1 2024-03-03 23:49:56 -05:00
ClarkLach daa9de4142 Uploaded Deliverable 2 files 2024-03-03 23:48:15 -05:00
ClarkLach 08b57dc2b2 Create deliverable 2 2024-03-03 23:47:42 -05:00
ClarkLach e179e8cdef Create deliverables directory 2024-03-03 23:46:58 -05:00
ClarkLach 57e61e362a Delete backend directory 2024-03-03 23:45:55 -05:00
npease 0042244b80 Prelim Profile 2024-03-03 03:17:53 +00:00
Nicholas Pease e8dfc7bf88 Fix Homepage Buttons Loading Slow (#31) 2024-03-02 21:23:07 -05:00
Nicholas Pease 72bcccfe2f Add Preliminary Chatroom Support (#32)
Add Preliminary Chatroom Support
2024-03-02 21:22:53 -05:00
Sgoodridge96 2c457ab531 Fixed bugs from last PR: Updated register page for password confirmation (#30) 2024-02-27 15:27:54 -05:00
Nicholas Pease 1120f5eb6e Merge branch 'main' into sgoodridge-edit1 2024-02-26 21:12:43 -05:00
Nicholas Pease f16b77c94b Merge branch 'main' into npease-homepage-fix 2024-02-26 18:34:29 -05:00
Nicholas Pease 62a813d0ad Add Username to Onboarding, Fix Onboarding (#28) 2024-02-26 14:59:27 -05:00
Stephen 151de4ebfd updated register page for password confirmation: Fixed bugs with last PR 2024-02-26 14:22:04 -05:00
Sgoodridge96 199c283b0e Update page.js
Updated errors from pull request:
Added a password confirmation to register page
2024-02-26 13:37:34 -05:00
npease 60bdf36274 Chatrooms: Nearby Lookup Working 2024-02-26 05:40:11 +00:00
npease a7eb9b942b Minimal Functional Chatrooms 2024-02-25 23:46:41 +00:00
npease 7cd1e9b5da WIP: Message Fetch Works 2024-02-25 08:38:40 +00:00
npease afd5dbaa9f Restructure App, Created Chat UI, Ready for Firebase Integration / Backend 2024-02-25 03:19:12 +00:00
npease eeb6b856e6 UI Changes, Prepare for Chatrooms 2024-02-24 06:35:23 +00:00
npease 9ee8bf3376 Add username, fix onboarding 2024-02-24 02:15:53 +00:00
npease 848d588bf4 Fix Homepage Buttons Loading Slow 2024-02-23 20:02:08 +00:00
Stephen 7ca4b62848 Added password confirmation to register page 2024-02-23 14:05:14 -05:00
Stephen 8cd5fd8783 Added password confimation during registration 2024-02-23 14:02:34 -05:00
Stephen afd72ec72b Added password confirmation on register page 2024-02-23 13:52:38 -05:00
Nicholas Pease 0895e93f6c Optimize Authentication Flow, Better Visual Feedback (#25) 2024-02-23 12:09:52 -05:00
Stephen 67ec566728 Added re enter password to register page 2024-02-23 11:58:09 -05:00
npease 8737d10a1e If user is authenticated, redirect to app from /login and /register pages 2024-02-23 04:37:03 +00:00
npease d9bca7f1ff Loading Icon on Login/Register button press 2024-02-23 04:22:36 +00:00
npease 4b9b46f10d Improved Onboarding User Verification, Removed Login/Register Buttons on Homepage for Logged In Users 2024-02-23 04:09:45 +00:00
npease bafcd88fa1 Optimize User Info Storage & Reduce API Calls to DB 2024-02-23 03:44:05 +00:00
Nicholas Pease ac7317a0b7 Validation on Register / Remove Download Button From Homepage (#23) 2024-02-22 14:08:14 -05:00
Nicholas Pease 034b217916 Update README.md with new URL (#21)
Update README.md with new URL
2024-02-22 14:06:53 -05:00
Nicholas Pease ef3f7fa174 Login: Invalid Username / Password Prompts User (#22) 2024-02-22 14:06:31 -05:00
npease 17d2ce436a Delete Download Button 2024-02-22 19:00:47 +00:00
npease 6dbc0a2e8e Register Page Validation 2024-02-22 18:59:57 +00:00
npease 43e9045b0a Final CSS Changes 2024-02-22 18:24:25 +00:00
npease 5ca9c4222c Slight CSS changes to keep login page from scrolling 2024-02-22 18:22:55 +00:00
npease 5e8a5c89b3 Add red border on error to login 2024-02-22 18:18:59 +00:00
Nicholas Pease dc5469fc70 Update README.md with even newer URL 2024-02-22 12:09:38 -05:00
npease ac19919c51 Login: Invalid Username / Password Prompts User 2024-02-22 06:17:53 +00:00
Sgoodridge96 7d2b653953 Change button Colors (#20) 2024-02-21 09:29:54 -05:00
Nicholas Pease 6dea6cc168 Update README.md with new URL 2024-02-21 09:14:05 -05:00
Nicholas Pease 75e3476d48 Load default map then center on users computed location (#16) 2024-02-21 09:08:53 -05:00
Stephen c73f85a411 Merge branch 'main' of https://github.com/ChatMaps/ChatMaps into sgoodridge 2024-02-20 23:33:18 -05:00
Nicholas Pease 5aba1549d4 Deployment Fixes (#19) 2024-02-20 22:56:49 -05:00
npease 3f68e43efd Merge branch 'main' of https://github.com/LAX18/ChatMaps 2024-02-20 22:55:22 -05:00
npease b24a2b5254 Remove Logging 2024-02-20 22:55:20 -05:00
Nicholas Pease b38f12a6ab Merge branch 'ChatMaps:main' into main 2024-02-20 22:54:51 -05:00
npease b07a333459 Cookie Deployment Fix 2024-02-20 22:51:09 -05:00
Stephen 13e5f319c7 changes to buttons 2024-02-20 22:17:35 -05:00
npease 4391a072cc Different way of setting cookies 2024-02-20 17:33:06 -05:00
npease 66c9de922d New way of setting cookies 2024-02-20 17:27:01 -05:00
npease 245ae616b1 Testing fixes with cookie on middleware 2024-02-20 17:05:01 -05:00
Nicholas Pease 3afbe17a21 Fix Deployment Authentication Problems (#18) 2024-02-20 16:49:23 -05:00
Nicholas Pease 8d3283ef04 Merge branch 'ChatMaps:main' into main 2024-02-20 16:48:13 -05:00
npease 8862f7b94c Fix login on deployment 2024-02-20 16:47:51 -05:00
Nicholas Pease 6e5af78586 Merge branch 'main' into npease-ui-maps-fix 2024-02-20 16:39:24 -05:00
Nicholas Pease ca731147f2 Move Firebase Auth to .env files to support Vercel deployments (#17) 2024-02-20 16:33:12 -05:00
npease a8dc4d8460 Move to .env.local files instead of files on dir 2024-02-20 16:26:01 -05:00
npease 6a0d3f3834 Add marker on user location (keep?) 2024-02-20 15:08:59 -05:00
npease 04cdc500b2 Load default map then center on users computed location 2024-02-20 14:59:33 -05:00
Nicholas Pease 8cd7cafdb5 Add Initial UI and Authentication (#11) 2024-02-20 14:34:40 -05:00
npease ec2fc15a3f Add onboarding, dashboard with relevant API's 2024-02-20 01:08:12 -05:00
Nicholas Pease d7a2382cb5 Restore Dependency Installation 2024-02-19 16:37:16 -05:00
npease 69d5bfe9a9 Remove build stage to remove conflict with local files 2024-02-19 21:34:08 +00:00
npease c555a59cf8 Spaces Matter 2024-02-19 21:26:32 +00:00
npease d20aecdbe8 Use GH Secrets with Workflow 2024-02-19 21:24:45 +00:00
npease 57a8415e52 Update Build Test 2024-02-19 21:02:14 +00:00
npease f19b09c5fd Update Import Paths 2024-02-19 08:17:17 +00:00
npease c6056c385b Favicon Fixes / Cleanup 2024-02-19 08:06:16 +00:00
npease 7420cc63fb Cleanup/condense package.json files 2024-02-19 07:59:18 +00:00
npease c528c6bacf Refactor / Commentate 2024-02-19 07:50:37 +00:00
npease daedd0b068 Initial UI, Login/Register Flow Prelim, Icons 2024-02-19 06:40:11 +00:00
59 changed files with 2643 additions and 1264 deletions
-29
View File
@@ -1,29 +0,0 @@
name: Frontend Next.Js Build Validation
on:
pull_request:
push:
branches:
- "main"
jobs:
nextjs:
runs-on: ubuntu-latest
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v2
with:
node-version: 21.6.2
- name: Install dependencies
run: npm install frontend-next/
- name: Lint
run: npm --prefix frontend-next/ run lint
- name: Build Frontend
run: npm --prefix frontend-next/ run build
+8 -1
View File
@@ -1,3 +1,7 @@
# Firebase Stuff
firebase-admin-key.json
firebase*.json
# Logs
logs
*.log
@@ -127,4 +131,7 @@ dist
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
.pnp.*
# Am I the only mac user
**/.DS_Store
+4 -2
View File
@@ -1,4 +1,4 @@
# ChatMaps
![](/frontend-next/public/logos/logo_transparent.png)
Main repo for ChatMaps, our COS420 Project.
ChatMaps is a web-based social networking service that allows users to connect to others in their local geographic area. It will implement an interactable mapping utility to show general user locations relative to other users, as well as a chat room feature that allows users to start public conversations based on a specified topic. ChatMaps is primarily intended for use in densely populated areas, such as college campuses or metropolitan areas, so people of similar interests can start conversations. The goal of this project is to create a web app that plots locations, gives a radius of the local area, and connects users into different topic-based chat rooms.
@@ -9,12 +9,14 @@ This app shares some similarities to other social networks that implement locati
The live version of this app can be found at:
http://chatmaps.nicholaspease.com
https://chatma.ps/
A local version can be run with:
cd frontend-next/
npm run build
npm run dev
then navigating to:
-1
View File
@@ -1 +0,0 @@
node_modules/
-16
View File
@@ -1,16 +0,0 @@
## Server Update Hook
The purpose of this file is to enable automatic server updates when a pull request to the main branch succeeds. This is accomplished via a workflow sent from Github to the server which then reloads both itself and the frontend.
## API Endpoints
Base Url: ```https://chatmaps.nicholaspease.com/api/v1/```
|Route|Method|Response Type|Use|Responses|
|-----|------|-------------|---|---------|
|```/``` |GET |Plain Text |Heartbeat|200 - "OK"|
|```/deploy```|POST|Plain Text |Server Update Trigger|200 - "OK" - Server Online / Updated
## Files
|File|Purpose|
|----|-------|
|api.js|NodeJS API using Express|
|README.md|API Reference|
-32
View File
@@ -1,32 +0,0 @@
// Physical Server Update Hook
// Imports
// Requires express, body-parser, child_process
const express = require("express")
const bodyParser = require("body-parser")
const { exec } = require("child_process")
// Start the express app and initialize
const app = express()
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json()); // This processes all the POST data as JSON
// Uses nodejs and express as "basic" backend server
// Recieves webhook from Github at https://chatmaps.nicholaspease.com/
app.post("/api/v1/deploy", function (req, res) {
// Webhook returns all pull request actions
// Limit to "closed", "merged", and the target branch being "main"
if (req.body.action == "closed" && req.body.pull_request.merged == true && req.body.pull_request.base.ref == "main") {
// (re)Start all the systemd files
exec("systemctl restart frontend-next.service", (error, stdout, stderr) => {});
exec("systemctl restart server_update_hook.service", (error, stdout, stderr) => {});
}
res.send("OK")
})
// Generic endpoint, useful to test if updater is alive
app.get('/api/v1', (req, res) => {
res.send('OK')
})
// Server runs on port 8000, exposed on server at /api/v1
app.listen(8000)
-728
View File
@@ -1,728 +0,0 @@
{
"name": "backend",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"body-parser": "^1.20.2",
"express": "^4.18.2"
}
},
"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/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/body-parser": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"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.7",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"set-function-length": "^1.2.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"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/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.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"gopd": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"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/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/es-define-property": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
"dependencies": {
"get-intrinsic": "^1.2.4"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"engines": {
"node": ">= 0.4"
}
},
"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/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/express/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/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/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.4",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3",
"hasown": "^2.0.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"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-property-descriptors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"dependencies": {
"es-define-property": "^1.0.0"
},
"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.1",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz",
"integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==",
"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/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/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/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/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/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/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/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/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/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.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"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/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.2.1",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz",
"integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==",
"dependencies": {
"define-data-property": "^1.1.2",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.3",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.1"
},
"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.5",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz",
"integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==",
"dependencies": {
"call-bind": "^1.0.6",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.4",
"object-inspect": "^1.13.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"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/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/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/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"
}
}
}
}
-6
View File
@@ -1,6 +0,0 @@
{
"dependencies": {
"body-parser": "^1.20.2",
"express": "^4.18.2"
}
}
-8
View File
@@ -1,8 +0,0 @@
## systemd (systemctl) Files
This folder contains all the files required by systemctl on the server for updating and running the backend/frontend. These are copied onto the server and placed in the ```/etc/systemd/system``` folder.
## Files
|Filename|Connected Service|
|--------|-----------------|
|frontend-next.service|Folder: frontend-next|
|server_update_hook.service|Folder: backend/server_update_hook|
-19
View File
@@ -1,19 +0,0 @@
[Unit]
Description=ChatMaps Frontend Server
After=network.target
[Service]
Type=simple
User=root
Group=root
Restart=on-failure
RestartSec=10
WorkingDirectory=/root/ChatMaps/frontend-next
ExecStartPre=/usr/bin/git pull
ExecStartPre=/usr/bin/git checkout main
ExecStartPre=/usr/bin/npm install
ExecStartPre=/usr/bin/npm run build
ExecStart=/usr/bin/npm run start
[Install]
WantedBy=multi-user.target
@@ -1,18 +0,0 @@
[Unit]
Description=ChatMaps Backend Server Updater / Restarter
After=network.target
[Service]
Type=simple
User=root
Group=root
Restart=on-failure
RestartSec=10
WorkingDirectory=/root/ChatMaps/backend/server_update_hook
ExecStartPre=/usr/bin/git pull
ExecStartPre=/usr/bin/git checkout main
ExecStartPre=/usr/bin/npm install
ExecStart=/usr/bin/node api.js
[Install]
WantedBy=multi-user.target
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 MiB

+6 -1
View File
@@ -1,3 +1,8 @@
{
"extends": "next/core-web-vitals"
"extends": "next/core-web-vitals",
"rules": {
"no-unused-vars": ["warn", { "vars": "all", "args": "after-used", "ignoreRestSiblings": false }],
"jsx-a11y/alt-text": "off",
"@next/next/no-img-element": "off"
}
}
+1375 -365
View File
File diff suppressed because it is too large Load Diff
+5 -1
View File
@@ -9,9 +9,13 @@
"lint": "next lint"
},
"dependencies": {
"firebase": "^10.6.0",
"next": "^14.1.0",
"pigeon-maps": "^0.21.3",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-beforeunload": "^2.6.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.50.1"
},
"devDependencies": {
"autoprefixer": "^10.0.1",
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 964 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>

Before

Width:  |  Height:  |  Size: 629 B

@@ -0,0 +1,19 @@
import { initializeApp, getApps, getApp } from "firebase/app";
import { getAuth } from "firebase/auth";
import { getDatabase} from "firebase/database"
var config = {
apiKey: "AIzaSyDbDPjQGt-lIjNPeTG-Q5AECM1m0vtOr2c",
authDomain: "chatmaps-3e7fa.firebaseapp.com",
projectId: "chatmaps-3e7fa",
storageBucket: "chatmaps-3e7fa.appspot.com",
messagingSenderId: "771010649524",
appId: "1:771010649524:web:b6e66d3457820c817b26e1",
databaseURL: "https://chatmaps-3e7fa-default-rtdb.firebaseio.com/",
}
var app = getApps().length > 0 ? getApp() : initializeApp(config);
var auth = getAuth(app);
var database = getDatabase(app);
export { auth, app, database };
+19
View File
@@ -0,0 +1,19 @@
import { Inter } from "next/font/google";
import "../globals.css";
const inter = Inter({ subsets: ["latin"] });
export const metadata = {
title: "ChatMaps: Home",
description: "ChatMaps: Social Media for College Students",
};
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={inter.className}>
{children}
</body>
</html>
);
}
+292
View File
@@ -0,0 +1,292 @@
"use client";
// System Imports
import { useState, useEffect } from "react";
import { auth, database } from "../api/firebase-config";
import { ref, onValue, set, remove, get } from "firebase/database";
import { useBeforeunload } from "react-beforeunload";
import {useRouter} from "next/navigation";
import {Marker} from "pigeon-maps";
import {onAuthStateChanged, signOut} from "firebase/auth"
// Refactored Component Imports
// Data Structure Imports
import { ChatRoomSidebar, Member } from "../../components/app/datatypes";
// Header Import
import { Header } from "../../components/app/header";
// Main Tab Imports
import { MainTabChatRoom } from "../../components/app/main_tab/chat";
import { MainTabHome } from "../../components/app/main_tab/home";
// Sidebar Imports
import {Home_Sidebar} from "../../components/app/sidebar/home";
import { Chat_Sidebar } from "../../components/app/sidebar/chat";
import { Profile_Sidebar } from "../../components/app/sidebar/profile";
// Contains most everything for the app homepage
function Home() {
// It's time to document and change these awful variable names
// State variables for app page
const [mainTab, setMainTab] = useState("home"); // Primary tab
const [tab, setTab] = useState("nearby"); // Sidebar Tab
const [chatRoomObj, setChatRoomObj] = useState(null); // Current chatroom object
const [myRoomsObj, setMyRoomsObj] = useState(null); // My Rooms Object
const [myRooms, setRoomData] = useState(null); // Current user saved rooms list
const [isRoomLoading, setRoomLoading] = useState(true); // myRooms loading variable, true = myRooms loading, false = finished loading
const [isMyRoom, setIsMyRoom] = useState(false); // Is current room in myRooms? true, false
const [location, setLocation] = useState(null); // location variable [lat,long]
const [loadingLoc, setLoadingLoc] = useState(true); // location variable loading, true = loading, false = finished loading
const [nearby, setNearby] = useState(null); // nearby rooms array
const [loadingNearby, setLoadingNearby] = useState(true); // loading nearby rooms array, true = loading, false = finished loading
const [chatroomOnline, setChatRoomOnline] = useState(null); // holds online users
const [chatroomUsers, setChatroomUsers] = useState(null); // holds all chatroom users
const [chatroomUsersLoading, setChatroomUsersLoading] = useState(true);
const [markers, setMarkers] = useState([]);
const [isAuthenticated, setAuth] = useState(false)
const [user, setUser] = useState(null)
// Authentication
useEffect(() => {
onAuthStateChanged(auth, (user) => {
if (user) {
get(ref(database, `users/${user.uid}`))
.then((userData) => {
userData = userData.val()
console.log(userData)
if (userData) {
setUser(userData)
setAuth(true)
} else {
window.location.href = "/onboarding"
}
})
} else {
setAuth(false)
window.location.href = "/login"
}
})
}, [])
// Grabs user data, saves to user, then lists the users saved rooms
useEffect(() => {
if (user) {
onValue(ref(database, "/users/" + user.uid + "/rooms"), (snapshot) => {
setRoomLoading(true);
var rooms = snapshot.val();
setMyRoomsObj(rooms);
var roomArr = [];
var markerArr = markers;
for (var room in rooms) {
var newRoom = (
<ChatRoomSidebar
roomObj={rooms[room]}
key={rooms[room].timestamp}
click={selectChatRoom}
/>
);
markerArr.push(
<Marker
width={30}
anchor={[rooms[room].latitude, rooms[room].longitude]}
color="blue"
/>
);
roomArr.push(newRoom);
}
setMarkers(markerArr);
setRoomData(roomArr);
setRoomLoading(false);
});
}
},[user]);
// Grabs the user location
useEffect(() => {
if ("geolocation" in navigator && user) {
// Retrieve latitude & longitude coordinates from `navigator.geolocation` Web API
navigator.geolocation.getCurrentPosition(({ coords }) => {
setLocation(coords)
setLoadingLoc(false)
var path = String(coords.latitude.toFixed(2)).replace(".","")+"/"+String(coords.longitude.toFixed(2)).replace(".","")
var markersArr = markers
onValue(ref(database, `/rooms/${path}`), (snapshot) => {
var nearbyArr = []
if (snapshot.exists()) {
var data = snapshot.val();
for (var room in data) {
nearbyArr.push(
<ChatRoomSidebar roomObj={data[room]} click={selectChatRoom} />
);
// TODO: RANDOM LAST DIGIT TO MOVE AROUND THE MAP
markersArr.push(
<Marker
width={30}
anchor={[data[room].latitude, data[room].longitude]}
color="blue"
/>
);
}
setMarkers(markersArr);
setLoadingNearby(false);
setNearby(nearbyArr);
} else {
setLoadingNearby(false);
}
});
});
}
},[user]);
// Dont Double Send Leaving Message
useEffect(() => {
if (myRoomsObj && chatRoomObj) {
var roomName = chatRoomObj.name + "-" + chatRoomObj.timestamp;
if (myRooms != null && roomName in myRoomsObj) {
// its in there
setIsMyRoom(true);
} else {
// its not in there
setIsMyRoom(false);
}
}
}, [chatRoomObj]);
// Selects chat room
function selectChatRoom(roomObj) {
// Path of chatroom
var path = roomObj.path + "/" + roomObj.name + "-" + roomObj.timestamp;
setChatRoomObj(roomObj);
// Send entered message
var payload = {
body: "entered",
user: user.username,
isSystem: true,
timestamp: new Date().getTime(),
};
set(
ref(
database,
`/rooms/${path}/chats/${new Date().getTime()}-${user.username}`
),
payload
);
// Code for Room Data
set(ref(database, `/rooms/${path}/users/online/${user.uid}`), user);
onValue(ref(database, `/rooms/${path}`), (snapshot) => {
setChatRoomOnline(null);
setChatroomUsers(null);
// Active users list
if (
snapshot.val().hasOwnProperty("users") &&
snapshot.val().users.hasOwnProperty("online")
) {
var activeUsers = [];
var activeUsersJSON = snapshot.val().users.online;
for (var user in activeUsersJSON)
activeUsers.push(<Member memberObj={activeUsersJSON[user]} />);
setChatRoomOnline(activeUsers);
}
// Users who added to "my rooms"
console.log(
snapshot.val().hasOwnProperty("users") &&
snapshot.val().users.hasOwnProperty("all")
);
if (
snapshot.val().hasOwnProperty("users") &&
snapshot.val().users.hasOwnProperty("all")
) {
setChatroomUsersLoading(true);
var allUsers = [];
var allUsersJSON = snapshot.val().users.all;
for (var user in allUsersJSON)
allUsers.push(<Member memberObj={allUsersJSON[user]} />);
setChatroomUsers(allUsers);
setChatroomUsersLoading(false);
}
});
setMainTab("chat");
}
// Fires to tell other uses that you are leaving the room
useBeforeunload(() => {
if (chatRoomObj && mainTab == "chat") {
var payload = {
body: "left",
user: user.username,
isSystem: true,
timestamp: new Date().getTime(),
};
set(
ref(
database,
`/rooms/${
chatRoomObj.path +
"/" +
chatRoomObj.name +
"-" +
chatRoomObj.timestamp
}/chats/${new Date().getTime()}-${user.username}`
),
payload
);
remove(
ref(
database,
`/rooms/${
chatRoomObj.path +
"/" +
chatRoomObj.name +
"-" +
chatRoomObj.timestamp
}/users/online/${userID}`
)
);
}
});
return (
<div>
{isAuthenticated && (
<div className="grid grid-cols-4 auto-cols-max overflow-hidden">
{/* Left Side of Page */}
<div className="col-span-3 h-dvh">
{/* Header */}
<Header mainTab={mainTab} isMyRoom={isMyRoom} chatRoomObj={chatRoomObj} setChatRoomObj={setChatRoomObj} setMainTab={setMainTab} setIsMyRoom={setIsMyRoom} user={user}/>
{/* Main Page Section */}
<div className="mr-2 h-[calc(100%-110px)]">
{mainTab == "home" && !loadingLoc && (
<MainTabHome loc={location} markers={markers} user={user}/>
)}
{mainTab == "home" && loadingLoc && (
<MainTabHome loc={null} markers={markers} user={user}/>
)}
{mainTab == "chat" && <MainTabChatRoom roomObj={chatRoomObj} user={user}/>}
</div>
</div>
{/* Sidebar (Right Side of Page) */}
{mainTab == "home" && (
<Home_Sidebar tab={tab} nearby={nearby} loadingNearby={loadingNearby} setTab={setTab} isRoomLoading={isRoomLoading} myRooms={myRooms} loadingLoc={loadingLoc} location={location}/>
)}
{mainTab == "chat" && (
<Chat_Sidebar chatRoomObj={chatRoomObj} chatroomOnline={chatroomOnline} chatroomUsersLoading={chatroomUsersLoading} chatroomUsers={chatroomUsers}/>
)}
{mainTab == "profile" && (
<Profile_Sidebar/>
)}
</div>
)}
</div>
);
}
export default Home;
Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

+12 -2
View File
@@ -2,17 +2,20 @@
@tailwind components;
@tailwind utilities;
main {
body {
background-color: aliceblue;
text-align: center;
text-wrap:pretty;
}
button {
background-color: rgb(205, 205, 205);
background: #dee0e0;
border-color: black;
border: 5px;
border-radius: 5px;
padding: 5px;
margin: 5px;
filter: drop-shadow(0 25px 25px rgb(0 0 0 / 0.15));
}
button:hover {
@@ -23,5 +26,12 @@ input {
border: 1px solid black;
border-radius: 4px;
padding: 10px 10px;
margin: 5px;
}
input.err {
border: 2px solid red;
border-radius: 4px;
padding: 10px 10px;
margin: 5px;
}
+4 -2
View File
@@ -5,13 +5,15 @@ const inter = Inter({ subsets: ["latin"] });
export const metadata = {
title: "ChatMaps",
description: "ChatMaps: Social Media for College Students",
description: "ChatMaps: Social Media for College Students"
};
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
<body className={inter.className}>
{children}
</body>
</html>
);
}
+19
View File
@@ -0,0 +1,19 @@
import { Inter } from "next/font/google";
import "../globals.css";
const inter = Inter({ subsets: ["latin"] });
export const metadata = {
title: "ChatMaps: Login",
description: "ChatMaps: Social Media for College Students",
};
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={inter.className}>
{children}
</body>
</html>
);
}
+66
View File
@@ -0,0 +1,66 @@
"use client";
import { useForm, Form } from "react-hook-form";
import { useRouter } from "next/navigation";
import "../globals.css"
// Firebase imports
import {auth} from "../api/firebase-config";
import { setPersistence, signInWithEmailAndPassword, browserSessionPersistence } from "firebase/auth";
function Login() {
var router = useRouter();
//var { register, handleSubmit } = useForm();
var { register, control, setError, handleSubmit, formState: { errors, isSubmitting, isSubmitted } } = useForm()
function authenticate(data) {
setPersistence(auth, browserSessionPersistence)
.then(() => {
signInWithEmailAndPassword(auth,data.email,data.password)
.then((userCredential) => {
if (userCredential.user) {
router.push("/app")
} else {
const formError = { type: "server", message: "Username or Password Incorrect" }
// set same error in both:
setError('password', formError)
setError('email', formError)
}
})
})
}
return (
<div>
<div className="grid h-screen place-items-center">
<div>
<a href="/"><img src="logos/logo_transparent_inverse.png"/></a>
<span className="text-[36px]">
Chat with friends!
</span>
<div>
<h3 className="text-[24px] mt-[25px] mb-2">Login</h3>
{(errors.email && errors.password) && <div className="text-[red] mb-2 text-[18px] text-bold">Invalid Email or Password.</div>}
<Form onSubmit={handleSubmit(authenticate)} encType={'application/json'}
control={control}
>
<input type="email" id="email" className={(errors.email && errors.password) && "err"} {...register("email", { required: true })} placeholder="Enter Email Address"/><br/>
<input type="password" id="password" name="password" className={(errors.email && errors.password) && "err"} {...register("password", { required: true })} placeholder="Enter Password"/><br/>
<button className="inline-flex items-center px-4 py-2 transition ease-in-out duration-150 bg-[#dee0e0] m-5 bg-cyan-500 text-white font-bold py-2 px-4 rounded-full">
{(isSubmitting || isSubmitted) && <span className="inline-block">
<svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" strokeWidth="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
</span> }
Log In
</button>
<br/>Need an account? <a href="/register">Sign Up</a><br/>
</Form>
</div>
</div>
</div>
</div>
)
}
export default Login;
@@ -0,0 +1,19 @@
import { Inter } from "next/font/google";
import "../globals.css";
const inter = Inter({ subsets: ["latin"] });
export const metadata = {
title: "ChatMaps: Onboarding",
description: "ChatMaps: Social Media for College Students"
};
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={inter.className}>
{children}
</body>
</html>
);
}
+58
View File
@@ -0,0 +1,58 @@
"use client";
import "../globals.css"
import { useForm } from "react-hook-form";
import { useRouter } from "next/navigation";
import { ref, set } from "firebase/database";
import {auth, database} from "../api/firebase-config"
import {onAuthStateChanged} from "firebase/auth"
function createUser(data) {
onAuthStateChanged(auth, (user) => {
if (user.uid) {
console.log(user)
data.uid = user.uid
data.defined = true
data.email = user.email
set(ref(database, `users/${user.uid}`), data);
return true
} else {
return false
}
})
}
function Onboarding() {
var router = useRouter();
var { register, handleSubmit } = useForm();
function Onboard(data) {
createUser(data)
router.push("/app");
}
return (
<div>
<div className="grid h-screen place-items-center">
<div>
<img src="logos/logo_transparent_inverse.png"/>
<span className="text-[36px]">
Chat with friends!
</span>
<div className="m-5">
Welcome to ChatMaps! We are excited to have you join our community!<br/>First we just need a little bit of information from you to get started.
</div>
<form action="#" onSubmit={handleSubmit(Onboard)}>
<input type="text" {...register("username")} placeholder="Display Name"/><br/>
<input type="text" {...register("firstName")} placeholder="First Name"/><br/>
<input type="text" {...register("lastName")} placeholder="Last Name"/><br/>
<button type="submit" className="inline-flex items-center px-4 py-2 transition ease-in-out duration-150 bg-[#dee0e0] m-5 bg-cyan-500 text-white font-bold py-2 px-4 rounded-full">Save</button>
</form>
</div>
</div>
</div>
)
}
export default Onboarding;
+69 -22
View File
@@ -1,27 +1,74 @@
export default function Home() {
return (
<main className="flex min-h-screen flex-col justify-between p-24">
<h1>Welcome to ChatMaps.</h1>
"use client"
import { useState, useEffect } from 'react'
import { auth, database } from "./api/firebase-config";
import { ref, get} from "firebase/database";
import {onAuthStateChanged} from "firebase/auth"
<div id="room">
<label>Room </label>
<select>
<option>Room 1</option>
<option>Room 2</option>
<option>Room 3</option>
</select>
<button>Join Room</button>
</div>
function Home() {
const [isLoadingLoc, setLoadingLoc] = useState(true)
const [roomCount, setRoomCount] = useState(null)
const [isAuthenticated, setAuth] = useState(false)
const [userID, setUserID] = useState(null)
// Authentication
useEffect(() => {
onAuthStateChanged(auth, (user) => {
if (user) {
setUserID(user.uid)
setAuth(true)
} else {
setAuth(false)
}
})
}, [])
<div id="message">
<label>Enter a message</label>
<input />
<button>Send</button>
</div>
</main>
);
useEffect(() => {
if('geolocation' in navigator) {
// Retrieve latitude & longitude coordinates from `navigator.geolocation` Web API
navigator.geolocation.getCurrentPosition(({ coords }) => {
var path = String(coords.latitude.toFixed(2)).replace(".","")+"/"+String(coords.longitude.toFixed(2)).replace(".","")
get(ref(database, `/rooms/${path}`)).then((snapshot) => {
if (snapshot.exists()) {
var count = 0
for (var room in snapshot.val()) {
count += 1
}
setRoomCount(count)
} else {
console.log("No rooms nearby")
setRoomCount(0)
}
setLoadingLoc(false)
});
});
}
})
return (
<div>
<div className="grid h-screen place-items-center">
<div>
<img src="logos/logo_transparent_inverse.png"/>
<span className="text-[36px]">
Chat with friends!
</span>
<div className="m-5">
{(!isAuthenticated) &&
<div>
<a href="/login"><button className="bg-cyan-500 text-white font-bold py-2 px-4 rounded-full">Login</button></a>
<a href="/register"><button className="bg-cyan-500 text-white font-bold py-2 px-4 rounded-full">Sign Up</button></a>
</div>
}
{isAuthenticated && <a href="/app"><button className="bg-cyan-500 text-white font-bold py-2 px-4 rounded-full">Continue to App</button></a>}
{(!isLoadingLoc && roomCount == 1) && <div className='text-[24px] pt-10'>Join others in {roomCount} room near you!</div>}
{(!isLoadingLoc && roomCount != 1 && roomCount != 0) && <div className='text-[24px] pt-10'>Join others in {roomCount} rooms near you!</div>}
{(isLoadingLoc || (roomCount == 0 && !isLoadingLoc)) && <div className='text-[24px] pt-10'>Start the conversation today!</div>}
</div>
</div>
</div>
</div>
)
}
export default Home;
+19
View File
@@ -0,0 +1,19 @@
import { Inter } from "next/font/google";
import "../globals.css";
const inter = Inter({ subsets: ["latin"] });
export const metadata = {
title: "ChatMaps: Register",
description: "ChatMaps: Social Media for College Students",
};
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={inter.className}>
{children}
</body>
</html>
);
}
+78
View File
@@ -0,0 +1,78 @@
"use client";
import { useRouter } from "next/navigation";
import { useForm, Form } from "react-hook-form";
import "../globals.css"
import { useState } from "react";
import { createUserWithEmailAndPassword, signInWithEmailAndPassword, setPersistence, browserSessionPersistence } from "firebase/auth";
import {auth} from "../api/firebase-config";
async function Signup(data) {
var userCredential = await createUserWithEmailAndPassword(auth,data.email,data.password);
if (userCredential.user) {
setPersistence(auth, browserSessionPersistence)
.then(() => {
signInWithEmailAndPassword(auth,data.email,data.password)
.then((res) => {
console.log(res)
return true
})
})
} else {
return false
}
}
function Register() {
var router = useRouter();
var { register, control, formState: { errors } } = useForm()
var emailRegex = /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/
var [passwordMismatch, setPasswordMismatch] = useState(false);
const passwordMatch = (data) => {
return data.password === data.passwordCheck;
};
function onSubmit({data}) {
if (passwordMatch(data)) {
setPasswordMismatch(false);
if (Signup(data)) {
router.push("/onboarding");
}
} else{
setPasswordMismatch(true);
return;
}
}
return (
<div>
<div className="grid h-screen place-items-center">
<div>
<a href="/"><img src="logos/logo_transparent_inverse.png"/></a>
<span className="text-[36px]">
Chat with friends!
</span>
<div>
<h3 className="text-[24px] mt-[15px]">Register</h3>
<Form onSubmit={onSubmit}
encType={'application/json'}
control={control}
>
<input type="email" {...register("email", {required: true, pattern: emailRegex})} className={errors.email && "err"} placeholder="Enter Email Address"/><br/>
<input type="password" {...register("password", {required: true})} className={errors.password && errors.password.type == 'required' && "err"} placeholder="Enter Password"/><br/>
<input type ="password" {...register("passwordCheck", {required: false})} className ={errors.passwordCheck && errors.passwordCheck.type == 'required' && "err"} placeholder="Re-enter Password"/><br/>
{passwordMismatch && <p className="text-red-500">Passwords do not match</p>}
<button type="submit" className="bg-[#dee0e0] m-5 bg-cyan-500 text-white font-bold py-2 px-4 rounded-full">
Register</button><br/>
Have an account? <a href="/login">Log In</a>
</Form>
</div>
</div>
</div>
</div>
)
}
export default Register;
@@ -0,0 +1,142 @@
import { Map, Marker, ZoomControl } from "pigeon-maps";
// Colors for Messages
const userColors = [
"#ff80ed",
"#065535",
"#133337",
"#ffc0cb",
"#e6e6fa",
"#ffd700",
"#ffa500",
"#0000ff",
"#1b85b8",
"#5a5255",
"#559e83",
"#ae5a41",
"#c3cb71",
];
// Chat Message
export function Chat({ chatObj }) {
let dateOptions = {
weekday: "long",
year: "numeric",
month: "short",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
};
const generateColor = (user_str) => {
// hashes username for consistent colors, maybe all functionality to pick color later
let hash = 0;
for (let i = 0; i < user_str.length; i++) {
hash = user_str.charCodeAt(i) + (hash * 32 - hash);
}
const index = Math.abs(hash) % userColors.length;
return index;
};
return (
<div className="width-[100%] bg-white rounded-lg mt-1 text-left p-1 grid grid-cols-2 mr-2">
<div>
<span style={{ color: userColors[generateColor(chatObj.user)] }}>
{chatObj.user}
</span>
: {chatObj.body}
</div>
<div className="text-right text-[#d1d1d1]">
{new Date(chatObj.timestamp).toLocaleString(dateOptions)}
</div>
</div>
);
}
// System Chat Message
export function SystemMessage({ chatObj }) {
let dateOptions = {
weekday: "long",
year: "numeric",
month: "short",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
};
const generateColor = (user_str) => {
// hashes username for consistent colors, maybe all functionality to pick color later
let hash = 0;
for (let i = 0; i < user_str.length; i++) {
hash = user_str.charCodeAt(i) + (hash * 32 - hash);
}
const index = Math.abs(hash) % userColors.length;
return index;
};
return (
<div className="width-[100%] bg-white rounded-lg mt-1 text-left p-1 grid grid-cols-2 mr-2">
<div className="text-[#d1d1d1]">
<span style={{ color: userColors[generateColor(chatObj.user)] }}>
{chatObj.user}
</span>{" "}
has {chatObj.body} the room.
</div>
<div className="text-right text-[#d1d1d1]">
{new Date(chatObj.timestamp).toLocaleString(dateOptions)}
</div>
</div>
);
}
// Member for Active/Room members in sidebar
export function Member({ memberObj }) {
return (
<div className="cursor-pointer g-[aliceblue] rounded-lg m-3 shadow-xl p-2">
{memberObj.username}
</div>
);
}
// Chat Room Object for myRooms and Nearby in sidebar
export function ChatRoomSidebar({ roomObj, click }) {
// TODO: Gross fix but it works
function clicker() {
click(roomObj);
}
return (
<div
onClick={clicker}
className="border-[black] border-1 shadow-lg p-2 m-2 rounded-lg cursor-pointer"
>
<div className="col-span-2">
<div className="font-bold">{roomObj.name}</div>
<div className="italic">{roomObj.description}</div>
</div>
</div>
);
}
// Map module for main page and chat room sidebar
// TODO: MAKE NOT MOVABLE
export function Geo({ loc, zoom, locMarker, markers }) {
if (loc) {
return (
<Map center={[loc.latitude, loc.longitude]} defaultZoom={zoom}>
{markers && markers}
{locMarker && (
<Marker
width={30}
anchor={[loc.latitude, loc.longitude]}
color="red"
/>
)}
{zoom && <ZoomControl />}
</Map>
);
} else {
return (
<Map className="rounded-lg" defaultCenter={[0, 0]} defaultZoom={zoom} />
);
}
}
+127
View File
@@ -0,0 +1,127 @@
import { auth, database } from "../../app/api/firebase-config";
import { ref, set, remove } from "firebase/database";
import {signOut} from "firebase/auth";
function logout() {
console.log("Fire")
signOut(auth)
}
// Closes chat room
function closeChatRoom(roomObj, setChatRoomObj, setMainTab, user) {
var path = roomObj.path + "/" + roomObj.name + "-" + roomObj.timestamp;
var payload = {
body: "left",
user: user.username,
isSystem: true,
timestamp: new Date().getTime(),
};
set(
ref(
database,
`/rooms/${path}/chats/${new Date().getTime()}-${user.username}`
),
payload
);
remove(ref(database, `/rooms/${path}/users/online/${user.uid}`));
setChatRoomObj(null);
setMainTab("home");
}
// Adds room to myRooms
function addToMyRooms(chatRoomObj, setIsMyRoom, user) {
set(
ref(
database,
`/users/${user.uid}/rooms/${chatRoomObj.name}-${chatRoomObj.timestamp}`
),
{
name: chatRoomObj.name,
path: chatRoomObj.path,
timestamp: chatRoomObj.timestamp,
description: chatRoomObj.description,
longitude: chatRoomObj.longitude,
latitude: chatRoomObj.latitude,
}
);
var path =
chatRoomObj.path +
"/" +
chatRoomObj.name +
"-" +
chatRoomObj.timestamp;
set(ref(database, `/rooms/${path}/users/all/${user.uid}`), user);
setIsMyRoom(true);
}
// Deletes saved room from myRooms
function removeFromMyRooms(chatRoomObj, setIsMyRoom, user) {
var path =
chatRoomObj.path +
"/" +
chatRoomObj.name +
"-" +
chatRoomObj.timestamp;
remove(
ref(
database,
`/users/${user.uid}/rooms/${chatRoomObj.name}-${chatRoomObj.timestamp}`
)
);
remove(ref(database, `/rooms/${path}/users/all/${user.uid}`));
setIsMyRoom(false);
}
export function Header({mainTab, isMyRoom, chatRoomObj, setChatRoomObj, setMainTab, setIsMyRoom, user}) {
return (
<div className="m-2 rounded-lg h-[63px] bg-white shadow-2xl grid grid-cols-2 p-1">
<div className="h-[60px]">
<a href="/">
<img
src="logos/logo_transparent_inverse.png"
className="h-[60px]"
/>
</a>
</div>
<div className="h-[60px] p-4">
{mainTab == "chat" && isMyRoom == false && (
<a
onClick={() => {
addToMyRooms(chatRoomObj, setIsMyRoom, user);
}}
className="p-2 cursor-pointer bg-[#dee0e0] bg-cyan-500 text-white font-bold rounded-full mr-5"
>
Add to &quot;My Rooms&quot;
</a>
)}
{mainTab == "chat" && isMyRoom == true && (
<a
onClick={() => {
removeFromMyRooms(chatRoomObj, setIsMyRoom, user);
}}
className="p-2 cursor-pointer bg-[#dee0e0] bg-cyan-500 text-white font-bold rounded-full mr-5"
>
Remove from &quot;My Rooms&quot;
</a>
)}
{mainTab == "chat" && (
<a
onClick={() => {
closeChatRoom(chatRoomObj, setChatRoomObj, setMainTab, user);
}}
className="p-2 cursor-pointer bg-[#dee0e0] bg-cyan-500 text-white font-bold rounded-full mr-5"
>
Close Chat
</a>
)}
<a
onClick={logout}
href="/"
className="p-2 cursor-pointer bg-[#dee0e0] bg-cyan-500 text-white font-bold rounded-full"
>
Sign Out
</a>
</div>
</div>
)
}
@@ -0,0 +1,94 @@
import { Chat, SystemMessage} from "../datatypes"
import { useState, useEffect } from "react";
import { Form, useForm } from "react-hook-form";
import { ref, onValue, set} from "firebase/database";
import { database } from "../../../app/api/firebase-config";
// Chatroom Module for Primary Tab
export function MainTabChatRoom({ roomObj, user }) {
var { register, control, reset, handleSubmit } = useForm();
const [chats, setData] = useState(null);
const [isLoading, setLoading] = useState(true);
// Message updater
useEffect(() => {
onValue(
ref(
database,
`/rooms/${
roomObj.path + "/" + roomObj.name + "-" + roomObj.timestamp
}/chats`
),
(snapshot) => {
var chatsArr = [];
var messages = snapshot.val();
for (var message in messages) {
if (messages[message].isSystem) {
chatsArr.push(
<SystemMessage
chatObj={messages[message]}
key={messages[message].timestamp}
/>
);
} else {
chatsArr.push(
<Chat
chatObj={messages[message]}
key={messages[message].timestamp}
/>
);
}
}
setData(chatsArr.reverse());
setLoading(false);
}
);
});
function sendMessage(data) {
reset();
var payload = {
body: data.message,
user: user.username,
isSystem: false,
timestamp: new Date().getTime(),
};
set(
ref(
database,
`/rooms/${
roomObj.path + "/" + roomObj.name + "-" + roomObj.timestamp
}/chats/${new Date().getTime()}-${user.username}`
),
payload
);
}
if (isLoading) return <div>Loading</div>;
if (!chats) return <div>No Chats</div>;
return (
<div className="m-1 h-[100%] rounded-lg">
<div className="h-[90%] m-4 overflow-y-auto flex flex-col-reverse">
{chats}
</div>
<div className="m-2 h-[10%] w-[100%] bg-white rounded-lg">
<Form
onSubmit={handleSubmit(sendMessage)}
control={control}
className="w-[100%] p-[0px]"
>
<input
type="text"
{...register("message")}
placeholder="Enter message"
className="w-[83%] border-[0px] mt-[8px] mb-[8px]"
/>
<button className="p-2 cursor-pointer bg-[#dee0e0] bg-cyan-500 text-white font-bold rounded-full mr-5 w-[8%]">
Send
</button>
</Form>
</div>
</div>
);
}
@@ -0,0 +1,31 @@
import {Geo} from "../datatypes"
// Module for Welcome Message on main tab landing page
function WelcomeMessage({user}) {
return (
<div className="bg-white rounded-lg m-2 mt-4 text-left p-2 pl-5">
<div>
Welcome, {user.firstName} {user.lastName} ({user.username})
</div>
<div>Lets see what&apos;s happening in your area.</div>
</div>
);
}
// Primary App Landing Page
export function MainTabHome({ loc, markers, user }) {
return (
<>
<WelcomeMessage user={user}/>
<div className="h-[calc(100%-110px)] m-5 rounded-lg">
<Geo
loc={loc}
zoom={14}
movable={true}
locMarker={true}
markers={markers}
/>
</div>
</>
);
}
@@ -0,0 +1,38 @@
import { Geo } from "../datatypes";
export function Chat_Sidebar({chatRoomObj, chatroomOnline, chatroomUsersLoading, chatroomUsers}) {
return (
<div className="h-dvh">
<div className="m-2 h-[98%] grid grid-cols-1">
<div className="bg-white rounded-lg m-2 shadow-2xl relative">
<div className="w-[100%] h-[100%] opacity-50 absolute rounded-lg z-10">
<Geo
loc={{
latitude: parseFloat(chatRoomObj.latitude.toFixed(2)),
longitude: parseFloat(chatRoomObj.longitude.toFixed(2)),
}}
zoom={12}
movable={false}
marker={false}
/>
</div>
<div className="z-10 top-0 left-0 w-[100%] h-[100%] absolute text-left pl-3 pt-2">
<span className="font-bold text-[24px]">
{chatRoomObj.name}
</span>
<br />
{chatRoomObj.description}
</div>
</div>
<div className="bg-white rounded-lg m-2 shadow-2xl">
<div>Online Members</div>
{chatroomOnline}
</div>
<div className="bg-white rounded-lg m-2 shadow-2xl">
<div>All Members</div>
{!chatroomUsersLoading && chatroomUsers}
</div>
</div>
</div>
)
}
@@ -0,0 +1,126 @@
import { Form, useForm } from "react-hook-form";
import { database } from "../../../app/api/firebase-config";
import { ref, set } from "firebase/database";
// CreateRoom Module for Sidebar Create Tab
function CreateRoom({ loc }) {
var { register, control, reset, handleSubmit } = useForm();
function createRoom(data) {
reset();
var path =
String(loc.latitude.toFixed(2)).replace(".", "") +
"/" +
String(loc.longitude.toFixed(2)).replace(".", "");
var timestamp = new Date().getTime();
var payload = {
name: data.name,
description: data.description,
timestamp: timestamp,
latitude: loc.latitude,
longitude: loc.longitude,
path: path,
};
set(ref(database, `/rooms/${path}/${data.name}-${timestamp}`), payload);
}
return (
<div className="overflow-y-auto h-[90%]">
<Form control={control} onSubmit={handleSubmit(createRoom)}>
<input
{...register("name")}
placeholder="Room Name"
className="mt-2"
/>
<input
{...register("description")}
placeholder="Room Description"
className="mt-2"
/>
<br />
<div className="mt-3 mb-2">
Creating room near ({loc.latitude.toFixed(2)},{" "}
{loc.longitude.toFixed(2)})
</div>
<button className="p-2 cursor-pointer bg-[#dee0e0] bg-cyan-500 text-white font-bold rounded-full mr-5">
Create
</button>
</Form>
</div>
);
}
export function Home_Sidebar({tab, nearby, loadingNearby, setTab, isRoomLoading, myRooms, loadingLoc, location}) {
return (
<div className="h-dvh">
<div className="bg-white shadow-2xl rounded-lg m-2 h-[98%]">
<div className="p-2">
<div className="p-1 rounded-lg grid grid-cols-3 bg-white">
<div
className={
tab == "nearby"
? "select-none p-1 cursor-pointer rounded-lg hover:bg-[#C0C0C0] bg-[#D3D3D3]"
: "select-none p-1 cursor-pointer rounded-lg hover:bg-[#C0C0C0]"
}
onClick={() => {
setTab("nearby");
}}
>
Nearby
</div>
<div
className={
tab == "rooms"
? "select-none p-1 cursor-pointer rounded-lg hover:bg-[#C0C0C0] bg-[#D3D3D3]"
: "select-none p-1 cursor-pointer rounded-lg hover:bg-[#C0C0C0]"
}
onClick={() => {
setTab("rooms");
}}
>
My Rooms
</div>
<div
className={
tab == "create"
? "select-none p-1 cursor-pointer rounded-lg hover:bg-[#C0C0C0] bg-[#D3D3D3]"
: "select-none p-1 cursor-pointer rounded-lg hover:bg-[#C0C0C0]"
}
onClick={() => {
setTab("create");
}}
>
Create
</div>
</div>
</div>
{tab == "nearby" && (
<div className="overflow-y-auto h-[90%]">
<div>
{!nearby && !loadingNearby && (
<div>
No Nearby Rooms
<br />
Create One?
</div>
)}
{loadingNearby && <div>Loading...</div>}
{nearby}
</div>
</div>
)}
{tab == "rooms" && (
<div className="overflow-y-auto h-[90%]">
<div>
{isRoomLoading && <div>Loading</div>}
{!myRooms && !isRoomLoading && <div>No User Saved Rooms</div>}
{myRooms}
</div>
</div>
)}
{tab == "create" && !loadingLoc && <CreateRoom loc={location} />}
{tab == "create" && loadingLoc && <div>Loading...</div>}
</div>
</div>
)
}
@@ -0,0 +1,7 @@
export function Profile_Sidebar() {
return (
<div className="h-dvh">
<div className=" bg-white m-2 h-[98%]">Profile</div>
</div>
)
}
-9
View File
@@ -5,14 +5,5 @@ module.exports = {
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
backgroundImage: {
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
"gradient-conic":
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
},
},
},
plugins: [],
};
+6
View File
@@ -0,0 +1,6 @@
{
"name": "ChatMaps",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}