Dev Branch Update #45
@@ -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
@@ -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
|
||||
@@ -15,6 +15,8 @@ A local version can be run with:
|
||||
|
||||
cd frontend-next/
|
||||
|
||||
npm run build
|
||||
|
||||
npm run dev
|
||||
|
||||
then navigating to:
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
node_modules/
|
||||
@@ -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|
|
||||
@@ -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
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"body-parser": "^1.20.2",
|
||||
"express": "^4.18.2"
|
||||
}
|
||||
}
|
||||
@@ -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|
|
||||
@@ -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.
Binary file not shown.
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 |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+708
-2058
File diff suppressed because it is too large
Load Diff
@@ -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,
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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 };
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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
@@ -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'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 "My Rooms"</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 "My Rooms"</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;
|
||||
@@ -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/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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} />
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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 "My Rooms"
|
||||
</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 "My Rooms"
|
||||
</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'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>
|
||||
)
|
||||
}
|
||||
@@ -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' },
|
||||
],
|
||||
};
|
||||
Reference in New Issue
Block a user