Dev Branch Update #45

Merged
LAX18 merged 30 commits from npease-auth-refactor into npease-profiles-v1 2024-03-22 11:12:23 -09:00
52 changed files with 1613 additions and 3789 deletions
-26
View File
@@ -1,26 +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
+4 -1
View File
@@ -131,4 +131,7 @@ dist
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
.pnp.*
# Am I the only mac user
**/.DS_Store
+2
View File
@@ -15,6 +15,8 @@ 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"
}
}
+708 -2058
View File
File diff suppressed because it is too large Load Diff
-1
View File
@@ -10,7 +10,6 @@
},
"dependencies": {
"firebase": "^10.6.0",
"firebase-admin": "^12.0.0",
"next": "^14.1.0",
"pigeon-maps": "^0.21.3",
"react": "^18.2.0",
@@ -1,22 +0,0 @@
import { initializeApp, getApps, cert } from "firebase-admin/app";
import admin from "firebase-admin";
export function customInitApp() {
if (getApps().length <= 0) {
initializeApp({
credential: admin.credential.cert({
type: process.env.FIREBASE_ADMIN_TYPE,
projectId: process.env.FIREBASE_ADMIN_PROJECT_ID,
privateKeyId: process.env.FIREBASE_ADMIN_PRIV_KEY_ID,
privateKey: process.env.FIREBASE_ADMIN_PRIV_KEY?.replace(/\\n/g, "\n"),
clientEmail: process.env.FIREBASE_ADMIN_CLIENT_EMAIL,
clientId: process.env.FIREBASE_ADMIN_CLIENT_ID,
authUri: process.env.FIREBASE_ADMIN_AUTH_URI,
tokenUri: process.env.FIREBASE_ADMIN_TOKEN_URL,
authProviderX509CertUrl: process.env.FIREBASE_ADMIN_AUTH_PROVIDER_X509_CERT_URL,
clientC509CertUrl: process.env.FIREBASE_ADMIN_CLIENT_X509_CERT_URL,
universe_domain: process.env.FIREBASE_ADMIN_UNIVERSE_DOMAIN,
}),
});
}
}
+3 -1
View File
@@ -1,5 +1,6 @@
import { initializeApp, getApps, getApp } from "firebase/app";
import { getAuth } from "firebase/auth";
import { getDatabase} from "firebase/database"
var config = {
apiKey: "AIzaSyDbDPjQGt-lIjNPeTG-Q5AECM1m0vtOr2c",
@@ -13,5 +14,6 @@ var config = {
var app = getApps().length > 0 ? getApp() : initializeApp(config);
var auth = getAuth(app);
var database = getDatabase(app);
export { auth, app };
export { auth, app, database };
-95
View File
@@ -1,95 +0,0 @@
import { cookies } from "next/headers";
import { NextResponse } from "next/server";
// Firebase Imports
import { auth } from "firebase-admin";
import { signInWithEmailAndPassword } from "firebase/auth";
// Lib Imports
import { app, auth as authConfig } from "../firebase-config";
import { customInitApp } from "../firebase-admin";
import { getDatabase, ref, get as firebaseGet } from "firebase/database";
// Needs to "init" on each call to the API
customInitApp();
// Login with Email/Password
async function handleEmailAndPassword(email, password) {
try {
var userCredential = await signInWithEmailAndPassword(authConfig,email,password);
if (userCredential.user.accessToken) {
var token = await auth().verifyIdToken(userCredential.user.accessToken);
var expiresIn = 20 * 60 * 1000; // 20 minutes
var sessionCookie = await auth().createSessionCookie(userCredential.user.accessToken, {expiresIn,});
if (token) {
var database = getDatabase(app)
var user = await firebaseGet(ref(database, `users/${userCredential.user.uid}`));
if (!user.exists()) {
var userOptions = {
name: "user",
value: JSON.stringify({defined: false, uid: userCredential.user.uid}),
maxAge: expiresIn, // 20 mins
httpOnly: true,
secure: true,
};
} else {
var userData = user.val()
userData.uid = userCredential.user.uid
userData.defined = true
var userOptions = {
name: "user",
value: JSON.stringify(userData),
maxAge: expiresIn, // 20 mins
httpOnly: true,
secure: true,
};
}
cookies().set(userOptions);
var options = {
name: "session",
value: sessionCookie,
maxAge: expiresIn, // 20 mins
httpOnly: true,
secure: true,
};
cookies().set(options);
cookies().set({
name: "uid",
value: userCredential.user.uid,
maxAge: expiresIn, // 20 mins
httpOnly: true,
secure: true,
});
return NextResponse.json({ options }, { status: 200 });
}
}
} catch (error) {
return NextResponse.json({ error: error.code }, { status: 401 });
}
}
// Handles POST requests (login requests)
export async function POST(req, res) {
try {
var { email, password } = await req?.json()
return await handleEmailAndPassword(email, password); // need session token
} catch (error) {
return NextResponse.json({ error: "Internal Server Error" },{ status: 500 });
}
}
// Handles GET requests (is session still valid requests)
export async function GET(req) {
var session = cookies().get("session")?.value || "";
//Validate if the cookie exist in the request
if (!session) {
return NextResponse.json({ isLogged: false }, { status: 401 });
} else {
// Validate session cookie
try {
var validation = await auth().verifySessionCookie(session, true);
return NextResponse.json({ isLogged: true, uid: validation.uid, email: validation.email }, { status: 200 });
} catch (error) {
return NextResponse.json({ isLogged: false}, { status: 401 });
}
}
}
@@ -1,48 +0,0 @@
import { NextResponse } from "next/server";
// Lib Imports
import { app } from "../firebase-config";
import { getDatabase, ref, set as firebaseSet } from "firebase/database";
import { cookies } from "next/headers";
async function onboard(onboardingJSON, req) {
var session = req.cookies.get("session");
//Call the authentication endpoint
var res = await fetch(new URL("/api/login", req.url), {headers: {Cookie: `session=${session?.value}`}})
// Login if unauthorized
if (res.status !== 200) {
return NextResponse.json({}, { status: 401 });
}
try {
var expiresIn = 20 * 60 * 1000; // 20 minutes
var { uid, email } = await res.json()
onboardingJSON.email = email
onboardingJSON.uid = uid
onboardingJSON.defined = true
var database = getDatabase(app)
await firebaseSet(ref(database, `users/${uid}`), onboardingJSON);
var userOptions = {
name: "user",
value: JSON.stringify(onboardingJSON),
maxAge: expiresIn, // 20 mins
httpOnly: true,
secure: true,
};
cookies().set(userOptions);
return NextResponse.json({}, { status: 200 });
} catch(error) {
return NextResponse.json({ error: "Internal Server Error: "+error },{ status: 500 });
}
}
// Handles POST requests (login requests)
export async function POST(req, res) {
try {
var onboardingJSON = await req?.json()
return await onboard(onboardingJSON, req);
} catch (error) {
return NextResponse.json({ error: "Internal Server Error" },{ status: 500 });
}
}
@@ -1,42 +0,0 @@
// Import necessary functions
import { createUserWithEmailAndPassword } from "firebase/auth";
import { auth } from "../firebase-config";
import { NextResponse } from "next/server";
// Function to register a new user using Firebase Authentication
export async function registerUser(email, password) {
try {
var userCredential = await createUserWithEmailAndPassword(auth,email,password);
// You can perform additional actions after successful registration, if needed.
return { success: true, userCredential };
} catch (error) {
return { success: false, error: error.message };
}
}
// POST request handler
export async function POST(req, res) {
try {
// Extract email and password from the request body
var { email, password } = await req?.json();
// Check if email and password are provided
if (!email || !password) {
return NextResponse.json(
{ error: "Email and password are required." },
{ status: 400 }
);
}
// Register the user
try {
var userCredential = await createUserWithEmailAndPassword(auth,email,password);
return NextResponse.json({message: "Registration successful.",user: userCredential.user,});
} catch {
return NextResponse.json({ error: registrationResult.error },{ status: 500 });
}
} catch (error) {
// Handle unexpected errors
return NextResponse.json({ error: "Internal Server Error" },{ status: 500 });
}
}
@@ -1,10 +0,0 @@
import { cookies } from "next/headers";
import { NextResponse } from "next/server";
export async function GET(req) {
cookies().delete('user')
cookies().delete('session')
cookies().delete('uid')
return NextResponse.redirect(new URL("/",req.url))
}
-7
View File
@@ -1,7 +0,0 @@
import { NextResponse } from "next/server";
import { cookies } from "next/headers";
export async function GET(req) {
var userData = cookies().get("user")?.value || false
return userData != false? NextResponse.json(JSON.parse(userData)): NextResponse.json({},{status: 203})
}
+229 -470
View File
@@ -1,533 +1,292 @@
"use client"
import { useState, useEffect } from 'react'
import {Map, Marker, ZoomControl} from "pigeon-maps"
import { Form, useForm } from "react-hook-form";
import { app } from "../api/firebase-config";
import { getDatabase, ref, onValue, get, set, remove} from "firebase/database";
import { useBeforeunload } from 'react-beforeunload';
var database = getDatabase(app)
"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";
// Data Types
// Header Import
import { Header } from "../../components/app/header";
// Chat Message
function Chat({chatObj}) {
let dateOptions = {
weekday: 'long',
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
};
return (
<div className='width-[100%] bg-white rounded-lg mt-1 text-left p-1 grid grid-cols-2 mr-2'>
<div>
{chatObj.user}: {chatObj.body}
</div>
<div className='text-right text-[#d1d1d1]'>
{new Date(chatObj.timestamp).toLocaleString(dateOptions)}
</div>
</div>
)
}
// Main Tab Imports
import { MainTabChatRoom } from "../../components/app/main_tab/chat";
import { MainTabHome } from "../../components/app/main_tab/home";
// System Chat Message
function SystemMessage({chatObj}) {
let dateOptions = {
weekday: 'long',
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
};
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]'>
{chatObj.user} 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
function Member({memberObj}) {
return (
<div className='cursor-pointer g-[aliceblue] rounded-lg m-3 shadow-xl p-2'>
{memberObj.username}
</div>
)
}
// Chat Room for myRooms and Nearby in sidebar
function ChatRoomSidebar({roomObj, click}) {
// TODO: Gross fix but it works
function clicker() {
click(roomObj.name+"-"+roomObj.timestamp, 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
function Geo({loc, zoom, movable, 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}/>
)
}
}
// Module for Welcome Message on main tab landing page
function WelcomeMessage() {
const [data, setData] = useState(null)
const [isLoading, setLoading] = useState(true)
useEffect(() => {
fetch('/api/user')
.then((res) => res.json())
.then((data) => {
setData(data)
setLoading(false)
})
}, [])
if (isLoading) return <div></div>
if (!data) return <div></div>
return (
<div className="bg-white rounded-lg m-2 mt-4 text-left p-2 pl-5">
<div>
Welcome, {data.firstName} {data.lastName} ({data.username})
</div>
<div>
Lets see what&apos;s happening in your area.
</div>
</div>
)
}
// Main Tabs
// Primary App Landing Page
function MainTabHome({loc, markers}) {
return (
<>
<WelcomeMessage />
<div className='h-[calc(100%-110px)] m-5 rounded-lg'>
<Geo loc={loc} zoom={14} movable={true} locMarker={true} markers={markers}/>
</div>
</>
)
}
// Chatroom Module for Primary Tab
function MainTabChatRoom({roomObj}) {
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()
fetch('/api/user').then((res) => res.json())
.then((user) => {
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>
)
}
// 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 [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 [users, setUsers] = useState(null) // all users from firebase
const [alreadyLeft, setAlreadyLeft] = useState(false) // if already left from room
const [markers, setMarkers] = useState([])
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(() => {
fetch('/api/user').then((res) => res.json())
.then((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)
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);
});
}
setMarkers(markerArr)
setRoomData(roomArr)
setRoomLoading(false)
})
})
}, [])
},[user]);
// Grabs the user location
useEffect(() => {
if('geolocation' in navigator) {
if ("geolocation" in navigator && user) {
// Retrieve latitude & longitude coordinates from `navigator.geolocation` Web API
navigator.geolocation.getCurrentPosition(({ coords }) => {
setLocation(coords)
setLoadingLoc(false)
var nearbyArr = []
var path = String(coords.latitude.toFixed(2)).replace(".","")+"/"+String(coords.longitude.toFixed(2)).replace(".","")
var markersArr = markers
get(ref(database, `/rooms/${path}`)).then((snapshot) => {
onValue(ref(database, `/rooms/${path}`), (snapshot) => {
var nearbyArr = []
if (snapshot.exists()) {
var data = snapshot.val()
var data = snapshot.val();
for (var room in data) {
nearbyArr.push(<ChatRoomSidebar roomObj={data[room]} click={selectChatRoom}/>)
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"/>)
markersArr.push(
<Marker
width={30}
anchor={[data[room].latitude, data[room].longitude]}
color="blue"
/>
);
}
setMarkers(markersArr)
setLoadingNearby(false)
setNearby(nearbyArr)
setMarkers(markersArr);
setLoadingNearby(false);
setNearby(nearbyArr);
} else {
setLoadingNearby(false)
setLoadingNearby(false);
}
})
})
});
});
}
}, []);
},[user]);
// Grab list of all users
useEffect(() => {
get(ref(database, `/users`)).then((snapshot) => {
setUsers(snapshot.val())
})
}, []);
// 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])
// 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
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);
}
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>
)
}
}, [chatRoomObj]);
// Selects chat room
function selectChatRoom(roomName, roomObj) {
fetch('/api/user').then((res) => res.json())
.then((user) => {
// Path of chatroom
var path = roomObj.path+"/"+roomObj.name+"-"+roomObj.timestamp
function selectChatRoom(roomObj) {
// Path of chatroom
var path = roomObj.path + "/" + roomObj.name + "-" + roomObj.timestamp;
setChatRoomObj(roomObj)
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) => {
// 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
);
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)
}
// Code for Room Data
set(ref(database, `/rooms/${path}/users/online/${user.uid}`), user);
onValue(ref(database, `/rooms/${path}`), (snapshot) => {
setChatRoomOnline(null);
setChatroomUsers(null);
// 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)
// 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);
}
}
})
setMainTab("chat")
setAlreadyLeft(false)
})
}
// Closes chat room
function closeChatRoom(roomObj) {
fetch('/api/user').then((res) => res.json())
.then((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)
setAlreadyLeft(true)
setMainTab("home")
})
}
// Adds room to myRooms
function addToMyRooms() {
fetch('/api/user').then((res) => res.json())
.then((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() {
fetch('/api/user').then((res) => res.json())
.then((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)
// 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(() => {
fetch('/api/user').then((res) => res.json())
.then((user) => {
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/${user.uid}`))
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 className="grid grid-cols-4 auto-cols-max overflow-hidden">
{/* Left Side of Page */}
<div className="col-span-3 h-dvh">
{/* Header */}
<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()}} 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()}} 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)}} className="p-2 cursor-pointer bg-[#dee0e0] bg-cyan-500 text-white font-bold rounded-full mr-5">Close Chat</a>}
<a href="/api/signout" className="p-2 cursor-pointer bg-[#dee0e0] bg-cyan-500 text-white font-bold rounded-full">Sign Out</a>
</div>
</div>
{/* Main Page Section */}
<div className="mr-2 h-[calc(100%-110px)]">
{(mainTab == "home" && !loadingLoc) && <MainTabHome loc={location} markers={markers}/>}
{(mainTab == "home" && loadingLoc) && <MainTabHome loc={null} markers={markers}/>}
{mainTab == "chat" && <MainTabChatRoom roomObj={chatRoomObj}/>}
</div>
</div>
{/* Sidebar (Right Side of Page) */}
{mainTab == "home" &&
<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> }
{(mainTab == "chat") &&
<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>
{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>
{chatroomOnline}
</div>
<div className='bg-white rounded-lg m-2 shadow-2xl'>
<div>
All Members
</div>
{!chatroomUsersLoading && chatroomUsers}
</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>
}
{(mainTab == "profile") &&
<div className="h-dvh">
<div className=" bg-white m-2 h-[98%]">
Profile
</div>
</div>
}
</div>
)
)}
</div>
);
}
export default Home;
+24 -11
View File
@@ -3,10 +3,32 @@ 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, formState: { errors, isSubmitting, isSubmitted } } = 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">
@@ -18,16 +40,7 @@ function Login() {
<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 action="/api/login" encType={'application/json'}
onSuccess={() => {
router.push("/app");
}}
onError={() => {
const formError = { type: "server", message: "Username or Password Incorrect" }
// set same error in both:
setError('password', formError)
setError('email', formError)
}}
<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/>
+25 -12
View File
@@ -2,22 +2,35 @@
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();
async function Onboard(data) {
const res = await fetch("/api/onboard", {
method: "POST",
body: JSON.stringify(data ? data : {}),
});
if (res.ok) {
router.push("/app");
} else {
router.push("/login");
}
function Onboard(data) {
createUser(data)
router.push("/app");
}
return (
<div>
@@ -34,7 +47,7 @@ function Onboarding() {
<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="bg-[#dee0e0] m-5">Save</button>
<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>
+21 -17
View File
@@ -1,25 +1,28 @@
"use client"
import { useState, useEffect } from 'react'
import { app } from "./api/firebase-config";
import { getDatabase, ref, get} from "firebase/database";
var database = getDatabase(app)
import { auth, database } from "./api/firebase-config";
import { ref, get} from "firebase/database";
import {onAuthStateChanged} from "firebase/auth"
function Home() {
const [statusCode, setData] = useState(null)
const [isLoading, setLoading] = useState(true)
const [isLoadingLoc, setLoadingLoc] = useState(true)
const [roomCount, setRoomCount] = useState(null)
useEffect(() => {
fetch('/api/user')
.then((res) => res.status)
.then((status) => {
setData(status)
setLoading(false)
})
}, [])
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)
}
})
}, [])
useEffect(() => {
if('geolocation' in navigator) {
@@ -51,14 +54,15 @@ function Home() {
Chat with friends!
</span>
<div className="m-5">
{(statusCode == 203 || isLoading) &&
{(!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>
}
{statusCode == 200 && <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 != 0) && <div className='text-[24px] pt-10'>Join others in {roomCount} rooms near you!</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>
+26 -10
View File
@@ -4,21 +4,41 @@ 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 { register, control, setError, handleSubmit, formState: { errors } } = useForm()
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;
};
const onSubmit = (data) => {
function onSubmit({data}) {
if (passwordMatch(data)) {
setPasswordMismatch(false);
router.push("/success");
if (Signup(data)) {
router.push("/onboarding");
}
} else{
setPasswordMismatch(true);
@@ -36,11 +56,7 @@ function Register() {
</span>
<div>
<h3 className="text-[24px] mt-[15px]">Register</h3>
<Form onSubmit={handleSubmit(onSubmit)}
onSuccess={() => {
router.push("/app");
}}
action="/api/register"
<Form onSubmit={onSubmit}
encType={'application/json'}
control={control}
>
@@ -1,26 +0,0 @@
import { Inter } from "next/font/google";
import "../../globals.css";
import { Header, Sidebar } from "./shared"
const inter = Inter({ subsets: ["latin"] });
export const metadata = {
title: "ChatMaps",
description: "ChatMaps: Social Media for College Students",
};
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={inter.className}>
<div className="grid grid-cols-4 auto-cols-max">
<div className="col-span-3 h-dvh">
<Header/>
{children}
</div>
<Sidebar/>
</div>
</body>
</html>
);
}
-28
View File
@@ -1,28 +0,0 @@
function MessagesDisplayBox() {
return (
<div className="h-[98%]">
Messages Stream in Here
</div>
)
}
function MessageSendBox() {
return (
<div className="bg-white rounded-lg shadow-2xl w-[98%] m-2">
Message Sender
</div>
)
}
function Home() {
return (
<div className="">
<MessagesDisplayBox/>
<MessageSendBox/>
</div>
)
}
export default Home;
@@ -1,18 +0,0 @@
export function Header() {
return (
<div className="m-2 rounded-lg h-[60px] bg-white shadow-2xl">
<img src="/logos/logo_transparent_inverse.png" className="h-[60px]"></img>
</div>
)
}
export function Sidebar() {
return (
<div className="h-dvh">
<div className="bg-white shadow-2xl rounded-lg m-2 h-[98%]">
Sidebar
</div>
</div>
)
}
@@ -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>
)
}
-57
View File
@@ -1,57 +0,0 @@
// src/middleware.js
import { NextResponse } from "next/server";
import { cookies } from "next/headers";
export async function middleware(req, res) {
const session = await req.cookies.get("session");
if (req.nextUrl.pathname !== "/login" && req.nextUrl.pathname != "/register") {
// Login if not logged in
if (!session) {
return NextResponse.redirect(new URL("/login", req.url));
}
//Call the authentication endpoint
const responseAPI = await fetch(new URL("/api/login", req.url), {
headers: {
Cookie: `session=${session?.value}`,
},
});
// Login if unauthorized
if (responseAPI.status !== 200) {
return NextResponse.redirect(new URL("/login", req.url));
}
// If new user, redirect to onboarding
var user = JSON.parse(req.cookies.get("user").value)
if (user.defined) {
return NextResponse.next();
} else {
return NextResponse.redirect(new URL("/onboarding", req.url));
}
} else {
// Currently in the /login or /register, if user is authenticated, go ahead and direct them to the app
if (session) {
const responseAPI = await fetch(new URL("/api/login", req.url), {
headers: {
Cookie: `session=${session?.value}`,
},
});
if (responseAPI.status == 200) {
return NextResponse.redirect(new URL("/app", req.url))
} else {
return NextResponse.next() // Unauthenticated, continue
}
} else {
return NextResponse.next() // Not logged in, direct to login
}
}
}
//Protected routes
export const config = {
matcher: ['/((?!onboarding|api|_next/static|_next/image|auth|favicon.ico|robots.txt|images|logo|$).*)',],
missing: [
{ type: 'header', key: 'next-router-prefetch' },
{ type: 'header', key: 'purpose', value: 'prefetch' },
],
};