From 28245db88d803791f62a231f0e185e5a461c5562 Mon Sep 17 00:00:00 2001 From: bnonni Date: Sat, 7 May 2022 12:58:57 -0400 Subject: [PATCH] push latest for jordan --- backend/.github/workflows/deploy.yml | 49 ++++++ backend/API.md | 183 ++++++++++++++++++++++ backend/Dockerfile | 11 ++ backend/api/Admin.js | 85 ---------- backend/api/Child.js | 26 --- backend/api/admin/adminController.js | 58 +++++++ backend/api/admin/adminService.js | 73 +++++++++ backend/api/admin/index.js | 8 + backend/api/node/index.js | 9 ++ backend/api/node/nodeController.js | 35 +++++ backend/api/node/nodeService.js | 18 +++ backend/app.js | 31 ++++ backend/dataModel.json | 37 +++++ backend/db/collection.js | 9 +- backend/db/db.js | 33 ++-- backend/package.json | 27 ++++ {frontend/src => backend}/sensei/admin.js | 50 +++--- backend/server.js | 7 + backend/utils/debug.js | 20 +++ backend/utils/logger.js | 19 +++ 20 files changed, 627 insertions(+), 161 deletions(-) create mode 100644 backend/.github/workflows/deploy.yml create mode 100644 backend/API.md create mode 100644 backend/Dockerfile delete mode 100644 backend/api/Admin.js delete mode 100644 backend/api/Child.js create mode 100644 backend/api/admin/adminController.js create mode 100644 backend/api/admin/adminService.js create mode 100644 backend/api/admin/index.js create mode 100644 backend/api/node/index.js create mode 100644 backend/api/node/nodeController.js create mode 100644 backend/api/node/nodeService.js create mode 100644 backend/app.js create mode 100644 backend/dataModel.json create mode 100644 backend/package.json rename {frontend/src => backend}/sensei/admin.js (76%) create mode 100644 backend/server.js create mode 100644 backend/utils/debug.js create mode 100644 backend/utils/logger.js diff --git a/backend/.github/workflows/deploy.yml b/backend/.github/workflows/deploy.yml new file mode 100644 index 0000000..864187b --- /dev/null +++ b/backend/.github/workflows/deploy.yml @@ -0,0 +1,49 @@ +name: park-ln + +on: + push: + branches: + - master + - devops/deploy/backend + +env: + CLOUD_RUN_PROJECT_ID: park-lightning + CLOUD_RUN_REGION: us-east1 + REPO_NAME: park-lightning + +jobs: + deploy-backend: + name: Deploy Backend + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - uses: google-github-actions/setup-gcloud@master + with: + project_id: $CLOUD_RUN_PROJECT_ID + credentials_json: ${{ secrets.GCP_CREDENTIALS_JSON }} + + - name: test gcloud CLI + run: gcloud info + + - name: Enable the necessary APIs and enable docker auth + run: |- + gcloud services enable containerregistry.googleapis.com + gcloud services enable run.googleapis.com + gcloud --quiet auth configure-docker + - name: Build and tag image + run: |- + docker build . --tag "gcr.io/$CLOUD_RUN_PROJECT_ID/$REPO_NAME:$GITHUB_SHA" + - name: Push image to GCR + run: |- + docker push gcr.io/$CLOUD_RUN_PROJECT_ID/$REPO_NAME:$GITHUB_SHA + - name: Deploy + run: |- + gcloud components install beta --quiet + gcloud beta run deploy $REPO_NAME --image gcr.io/$CLOUD_RUN_PROJECT_ID/$REPO_NAME:$GITHUB_SHA \ + --project $CLOUD_RUN_PROJECT_ID \ + --platform managed \ + --region $CLOUD_RUN_REGION \ + --allow-unauthenticated \ + --quiet \ No newline at end of file diff --git a/backend/API.md b/backend/API.md new file mode 100644 index 0000000..6fcf284 --- /dev/null +++ b/backend/API.md @@ -0,0 +1,183 @@ +# API Route Spec +Below is an outline of the API routes available to both users (parking and paying) and admins (enforcing parking rules). + +## Routes +User Types +1. Drivers +2. Parking Enforcement + +API Data Objects +1. [Spot](./api/spot/) +2. [Invoice](./api/invoice/) + +5 Routes +1. Route: `/api/v1/spot/details` + - Method: `GET` + - URL: https://park-lightning-foiudx76uq-ue.a.run.app/api/v1/spot/details?uuid=d8d05dce-dbae-421b-9bdd-ea3ce75b7a77 +```json +Body: None + +Returns: + { + "success": true, + "message": { + "address": "691 John Wesley Dobbs Ave, Atlanta, GA 30312", + "expirationTime": 1648319420, + "startTime": 1648317620, + "expired": false, + "licensePlate": "SC39133", + "occupied": true, + "duration": 1800 + } + } +``` +2. Route: `/api/v1/spot/reserve` + - Method: `POST` + - URL: https://park-lightning-foiudx76uq-ue.a.run.app/api/v1/spot/reserve +```json +Body: + { + "uuid": "da1c0d1b-1ecf-4fe0-9acd-dc3d49640f8f", + "licensePlate": "BOE8359", + "duration": 1800 + } + +Returns: + { + "success": true, + "message": { + "_writeTime": { + "_seconds": 1648324950, + "_nanoseconds": 756013000 + } + } + } +``` +3. Route: `/api/v1/invoice/create` + - Method: `POST` + - URL: https://park-lightning-foiudx76uq-ue.a.run.app/api/v1/invoice/create +```json +Body: + { + "amount": 1, + "memo": { + "licensePlate": "BOE8359", + "uuid": "da1c0d1b-1ecf-4fe0-9acd-dc3d49640f8f", + "duration": 1800 + } + } + +Returns: + { + "success": true, + "message": { + "id": "dd610b73-aed9-456f-9cd6-7d4aeff29407", + "description": "{\"licensePlate\":\"BOE8359\",\"uuid\":\"da1c0d1b-1ecf-4fe0-9acd-dc3d49640f8f\",\"duration\":1800}", + "amount": 2261, + "missing_amt": 2261, + "status": "unpaid", + "fiat_value": 1, + "source_fiat_value": 1, + "currency": "USD", + "created_at": 1648392719, + "order_id": null, + "address": "31zEqCfzvvT8kBr9fh4U3bZj1AZM5Q4vsb", + "metadata": {}, + "expires_at": "2022-03-27T15:01:59.758Z", + "auto_settle": false, + "lightning_invoice": { + "created_at": 1648392719, + "expires_at": 1648393318, + "payreq": "lnbc22610n1p3yq7s0pp5nfzpah463t9v25c5ja0gpy4uq5ga3lq96a6pe6adsw76dvqgv8zqdyq0v3xc6trv4h8xe2sd3shgefz8g3yyn698qen2wfz9s382atfvs3r5gnyvyckxvryx93z6vt9vdnz6drxv5cz6wtpvdjz6errxdjrgwfkxscxvwrxygkzyer4wfsjut3wcqzpgxqzjhsp58x3y8h4npmp9ws76qrrhgadsr2pa9ls6p7zxtd4k2k9p87cqjq6q9qyyssqp65meh539k2ungxpq7vw9j8s37slhg3gjcxvmnpe0fw67xuey2xpu7pcgxt5c8c5kecr4l9y5a4rd8dt0flrg98m5r2w9rkyqgc4mzsqhvm98w", + "settled_at": null + }, + "chain_invoice": { + "address": "31zEqCfzvvT8kBr9fh4U3bZj1AZM5Q4vsb" + }, + "transactions": [], + "uri": "bitcoin:31zEqCfzvvT8kBr9fh4U3bZj1AZM5Q4vsb?amount=0.00002261&label={\"licensePlate\":\"BOE8359\",\"uuid\":\"da1c0d1b-1ecf-4fe0-9acd-dc3d49640f8f\",\"duration\":1800}&lightning=LNBC22610N1P3YQ7S0PP5NFZPAH463T9V25C5JA0GPY4UQ5GA3LQ96A6PE6ADSW76DVQGV8ZQDYQ0V3XC6TRV4H8XE2SD3SHGEFZ8G3YYN698QEN2WFZ9S382ATFVS3R5GNYVYCKXVRYX93Z6VT9VDNZ6DRXV5CZ6WTPVDJZ6ERRXDJRGWFKXSCXVWRXYGKZYER4WFSJUT3WCQZPGXQZJHSP58X3Y8H4NPMP9WS76QRRHGADSR2PA9LS6P7ZXTD4K2K9P87CQJQ6Q9QYYSSQP65MEH539K2UNGXPQ7VW9J8S37SLHG3GJCXVMNPE0FW67XUEY2XPU7PCGXT5C8C5KECR4L9Y5A4RD8DT0FLRG98M5R2W9RKYQGC4MZSQHVM98W" + } + } + +``` +4. Route: `/api/v1/invoice/check` + - Method: `POST` + - URL: https://park-lightning-foiudx76uq-ue.a.run.app/api/v1/invoice/create +```json +Body: + { + "id": "dd610b73-aed9-456f-9cd6-7d4aeff29407" + } + +Returns: + { + "success": true, + "message": { + "id": "dd610b73-aed9-456f-9cd6-7d4aeff29407", + "description": "{\"licensePlate\":\"BOE8359\",\"uuid\":\"da1c0d1b-1ecf-4fe0-9acd-dc3d49640f8f\",\"duration\":1800}", + "amount": 2261, + "missing_amt": 2261, + "status": "unpaid", + "fiat_value": 1, + "source_fiat_value": 1, + "currency": "USD", + "created_at": 1648392719, + "order_id": null, + "address": "31zEqCfzvvT8kBr9fh4U3bZj1AZM5Q4vsb", + "metadata": {}, + "expires_at": "2022-03-27T15:01:59.758Z", + "auto_settle": false, + "lightning_invoice": { + "created_at": 1648392719, + "expires_at": 1648393318, + "payreq": "lnbc22610n1p3yq7s0pp5nfzpah463t9v25c5ja0gpy4uq5ga3lq96a6pe6adsw76dvqgv8zqdyq0v3xc6trv4h8xe2sd3shgefz8g3yyn698qen2wfz9s382atfvs3r5gnyvyckxvryx93z6vt9vdnz6drxv5cz6wtpvdjz6errxdjrgwfkxscxvwrxygkzyer4wfsjut3wcqzpgxqzjhsp58x3y8h4npmp9ws76qrrhgadsr2pa9ls6p7zxtd4k2k9p87cqjq6q9qyyssqp65meh539k2ungxpq7vw9j8s37slhg3gjcxvmnpe0fw67xuey2xpu7pcgxt5c8c5kecr4l9y5a4rd8dt0flrg98m5r2w9rkyqgc4mzsqhvm98w", + "settled_at": null + }, + "chain_invoice": { + "address": "31zEqCfzvvT8kBr9fh4U3bZj1AZM5Q4vsb" + }, + "transactions": [], + "uri": "bitcoin:31zEqCfzvvT8kBr9fh4U3bZj1AZM5Q4vsb?amount=0.00002261&label={\"licensePlate\":\"BOE8359\",\"uuid\":\"da1c0d1b-1ecf-4fe0-9acd-dc3d49640f8f\",\"duration\":1800}&lightning=LNBC22610N1P3YQ7S0PP5NFZPAH463T9V25C5JA0GPY4UQ5GA3LQ96A6PE6ADSW76DVQGV8ZQDYQ0V3XC6TRV4H8XE2SD3SHGEFZ8G3YYN698QEN2WFZ9S382ATFVS3R5GNYVYCKXVRYX93Z6VT9VDNZ6DRXV5CZ6WTPVDJZ6ERRXDJRGWFKXSCXVWRXYGKZYER4WFSJUT3WCQZPGXQZJHSP58X3Y8H4NPMP9WS76QRRHGADSR2PA9LS6P7ZXTD4K2K9P87CQJQ6Q9QYYSSQP65MEH539K2UNGXPQ7VW9J8S37SLHG3GJCXVMNPE0FW67XUEY2XPU7PCGXT5C8C5KECR4L9Y5A4RD8DT0FLRG98M5R2W9RKYQGC4MZSQHVM98W" + } + } +``` + +5. Route: `/api/v1/spot/empty` + - Method: `GET` + - URL: https://park-lightning-foiudx76uq-ue.a.run.app/api/v1/spot/empty + ```json + Body: None + + Returns: + { + "success": true, + "message": { + "unoccupiedSpots": [ + { + "address": "691 John Wesley Dobbs Ave, Atlanta, GA 30312", + "occupied": false + } + ], + "expiredSpots": [ + { + "expired": true, + "expirationTime": 1648319420, + "duration": 1800, + "address": "691 John Wesley Dobbs Ave, Atlanta, GA 30312", + "occupied": true, + "licensePlate": "SC39133", + "startTime": 1648317620 + }, + { + "occupied": true, + "address": "79 5th St NW, Atlanta, GA 30308", + "expirationTime": 1648327169, + "expired": true, + "duration": 1800, + "licensePlate": "BOE8359", + "startTime": 1648325369 + } + ] + } + } +``` diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..4366065 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,11 @@ +FROM node:16.14.2 + +WORKDIR /usr/src/app + +COPY . . + +RUN yarn + +EXPOSE 8080 4000 + +CMD [ "yarn", "start" ] \ No newline at end of file diff --git a/backend/api/Admin.js b/backend/api/Admin.js deleted file mode 100644 index fc5ec0f..0000000 --- a/backend/api/Admin.js +++ /dev/null @@ -1,85 +0,0 @@ -//Parent Account Requests - -// CreateNewAccount -// - ManageChildren -// - GetChildren -// - InviteChild -// - Role (1-4) -// - TransitionChildRole -// - SetChildAllowance -// - TransferFunds - -import db, { getFamily } from "../db/db"; -import { - collection, - doc, - setDoc, - updateDoc, - arrayUnion, - getDocs, - query, - where, -} from "firebase/firestore"; - -export const GetFamily = async (familyName) => { - console.log("Getting Family"); - return await getFamily(familyName); -}; - -export const CreateNewFamily = async (familyName) => { - await setDoc(doc(db, "families", familyName), { - familyName: familyName, - children: [], - }); - return { success: true }; -}; -export const GetChildren = async (familyId) => { - return { children }; -}; - -export const InviteChild = async (familyId, childName, role) => { - return { success: true }; -}; - -export const AddChild = async ( - familyName, - childName, - role, - balance, - allowance -) => { - const coll = collection(db, "families"); - const docs = await getDocs(coll); - console.log("getDocs", docs); - let famDoc = null; - await docs.forEach((doc) => { - console.log("snap", doc); - famDoc = doc; - }); - - await updateDoc(famDoc, { - children: arrayUnion({ - allowance: allowance, - name: childName, - role: role, - balance: balance, - }), - }); - return { success: true, familyDoc }; -}; - -export const TransitionChildRole = async (familyId, childId, newRole) => { - return { success: true }; -}; - -export const SetChildAllowance = async (familyId, childId, allowance) => { - return { success: true }; -}; - -export const TransferFunds = async (familyId, childId, amount) => { - return { success: true }; -}; - -export const ExportChild = async (familyId, childId) => { - return { success: true }; -}; diff --git a/backend/api/Child.js b/backend/api/Child.js deleted file mode 100644 index 021a07a..0000000 --- a/backend/api/Child.js +++ /dev/null @@ -1,26 +0,0 @@ -// Children Account Requests -// - GetBalance -// - GetTransactions -// - GetAllowance -// - SendPayment -// - RequestOneTimeAllowance - -export const GetBalance = async (familyId, childId) => { - return { balance: 0 }; -}; - -export const GetTransactions = async (familyId, childId) => { - return { transactions: [] }; -}; - -export const GetAllowance = async (familyId, childId) => { - return { allowance: 0 }; -}; - -export const SendPayment = async (familyId, childId, amount) => { - return { success: true }; -}; - -export const RequestOneTimeAllowance = async (familyId, childId, amount) => { - return { success: true }; -}; diff --git a/backend/api/admin/adminController.js b/backend/api/admin/adminController.js new file mode 100644 index 0000000..513162d --- /dev/null +++ b/backend/api/admin/adminController.js @@ -0,0 +1,58 @@ +const debug = require('../../utils/debug'); +const spotService = require('./adminService'); + +const getParkingSpotDetails = async (req, res) => { + try { + const uuid = req._parsedUrl.query.split('=')[1]; + const response = await spotService.getParkingSpotDetails(uuid); + + debug.info(`Spot Details Response: ${JSON.stringify(response)}`); + + if (!response.success) res.status(500).json(response); + else res.status(200).json(response); + + } catch (error) { + debug.error(error.stack); + res.status(500).json({ message: error.message, error: error.stack }); + } +}; + +const reserveParkingSpot = async (req, res) => { + try { + const uuid = req.body.uuid; + const licensePlate = req.body.licensePlate; + const duration = req.body.duration; + + const response = await spotService.reserveParkingSpot( + uuid, + licensePlate, + duration + ); + + debug.info(`Spot Reservation Response: ${JSON.stringify(response)}`); + + if (!response.success) res.status(500).json(response); + else res.status(200).json(response); + + } catch (error) { + debug.error(error.stack); + res.status(500).json({ message: error.message, error: error.stack }); + } +}; + +const shouldBeEmpty = async (req, res) => { + try { + const response = await spotService.shouldBeEmpty(); + + debug.info(`Spot Details Response: ${JSON.stringify(response)}`); + + if (!response.success) res.status(500).json(response); + else res.status(200).json(response); + + } catch (error) { + debug.error(error.stack); + res.status(500).json({ message: error.message, error: error.stack }); + } +} + +module.exports = { getParkingSpotDetails, reserveParkingSpot, shouldBeEmpty }; diff --git a/backend/api/admin/adminService.js b/backend/api/admin/adminService.js new file mode 100644 index 0000000..6e5c1f9 --- /dev/null +++ b/backend/api/admin/adminService.js @@ -0,0 +1,73 @@ +const debug = require('../../utils/debug'); +const spots = require('../../db/collection'); + +const getParkingSpotDetails = async (uuid) => { + try { + return { success: true, message: (await spots.doc(uuid).get()).data() }; + } catch (error) { + debug.error(error.stack); + throw new Error(error); + } +}; + +const reserveParkingSpot = async (uuid, licensePlate, duration) => { + try { + const spot = spots.doc(uuid); + const startTime = Math.floor(new Date().getTime() / 1000); + if (!spot.occupied) { + var response = await spot.update({ + duration: duration, + expirationTime: startTime + duration, + expired: false, + licensePlate: licensePlate, + occupied: true, + startTime: startTime, + }); + + return { success: true, message: response }; + } + } catch (error) { + debug.error(error.stack); + throw new Error(error); + } +}; + +const shouldBeEmpty = async () => { + try { + const shouldBeEmptySpots = {} + const expiredSpots = []; + const emptySpots = []; + const now = Math.floor(new Date().getTime() / 1000); + const unoccupiedSpots = await spots + .where('occupied', '==', false) + .get(); + + unoccupiedSpots.forEach(spot => { + emptySpots.push(spot.data()) + }) + shouldBeEmptySpots.unoccupiedSpots = emptySpots; + + const occupiedSpots = await spots + .where('occupied', '==', true) + .get(); + + occupiedSpots.forEach((spot) => { + const spotData = spot.data() + if((spotData.expirationTime - now) <= 0){ + spots.doc(spot.id).update({ + expired: true + }) + spotData.expired = true; + expiredSpots.push(spotData) + } + }) + shouldBeEmptySpots.expiredSpots = expiredSpots; + + return { success: true, message: shouldBeEmptySpots }; + } catch (error) { + debug.error(error.stack); + throw new Error(error); + } +}; + +module.exports = { getParkingSpotDetails, reserveParkingSpot, shouldBeEmpty }; diff --git a/backend/api/admin/index.js b/backend/api/admin/index.js new file mode 100644 index 0000000..7c82cfd --- /dev/null +++ b/backend/api/admin/index.js @@ -0,0 +1,8 @@ +const express = require('express'); +const router = express(); + +const { getAllBalances } = require('./adminController'); + +router.get('/details', getAllBalances); + +module.exports = router; diff --git a/backend/api/node/index.js b/backend/api/node/index.js new file mode 100644 index 0000000..66b09d4 --- /dev/null +++ b/backend/api/node/index.js @@ -0,0 +1,9 @@ +const express = require('express'); +const router = express(); + +const { getAllowance } = require('./node'); + + +router.get('/allowance', getAllowance); + +module.exports = router; diff --git a/backend/api/node/nodeController.js b/backend/api/node/nodeController.js new file mode 100644 index 0000000..1c7bd1c --- /dev/null +++ b/backend/api/node/nodeController.js @@ -0,0 +1,35 @@ +const debug = require('../../utils/debug'); +const nodeService = require('./nodeService'); + +const getAllowance = async (req, res) => { + try { + const amount = req.body.amount; + const memo = req.body.memo; + const response = await nodeService.getAllowance(amount, memo); + + debug.info(`Invoice Creation Response: ${JSON.stringify(response)}`); + + if (!response.success) res.status(500).json(response); + else res.status(200).json(response); + } catch (error) { + debug.error(error.stack); + res.status(500).json({ message: error.message, error: error.stack }); + } +}; + +const checkInvoice = async (req, res) => { + try { + const id = req.body.id + const response = await nodeService.checkInvoice(id); + + debug.info(`Invoice Creation Response: ${JSON.stringify(response)}`); + + if (!response.success) res.status(500).json(response); + else res.status(200).json(response); + } catch (error) { + debug.error(error.stack); + res.status(500).json({ message: error.message, error: error.stack }); + } +}; + +module.exports = { createInvoice, checkInvoice }; diff --git a/backend/api/node/nodeService.js b/backend/api/node/nodeService.js new file mode 100644 index 0000000..e05b952 --- /dev/null +++ b/backend/api/node/nodeService.js @@ -0,0 +1,18 @@ +const debug = require('../../utils/debug'); +const sensei = require('../../sensei/admin'); + +const getAllowance = async (amount, memo) => { + try { + const allowance = await sensei.getAllowance({ + amount: amount, + currency: 'USD', + description: JSON.stringify(memo) + }); + return { success: true, message: charge }; + } catch (error) { + debug.error(error.stack, error.status, error.message); + throw new Error(error); + } +}; + +module.exports = { getAllowance }; diff --git a/backend/app.js b/backend/app.js new file mode 100644 index 0000000..21d13fe --- /dev/null +++ b/backend/app.js @@ -0,0 +1,31 @@ +const dotenv = require('dotenv'); +dotenv.config({ path: `.env.${process.env.NODE_ENV}` }); + +const PORT = process.env.PORT || 4000; +const cors = require('cors'); +const morgan = require('morgan'); +const express = require('express'); +const app = express(); + +app.use(morgan('dev')); +app.use(express.json({ limit: '50mb' })); +app.use(express.urlencoded({ limit: '50mb', extended: true })); +app.use(cors({ credentials: true, origin: true })); +app.use( + express.text({ + type: () => { + return { text: 'text' }; + }, + }) +); + +app.get('/', async (req, res) => { + res.send(`Health check! Server running on port ${PORT}!`); +}); + +const spot = require('./api/spot'); +const invoice = require('./api/invoice'); +app.use('/api/v1/spot', spot); +app.use('/api/v1/invoice', invoice); + +module.exports = app; diff --git a/backend/dataModel.json b/backend/dataModel.json new file mode 100644 index 0000000..8d228f9 --- /dev/null +++ b/backend/dataModel.json @@ -0,0 +1,37 @@ +{ + "d8d05dce-dbae-421b-9bdd-ea3ce75b7a77": { + "address": "691 John Wesley Dobbs Ave, Atlanta, GA 30312", + "occupied": true, + "licensePlate": "SC39133", + "start": 1648317620, + "expiration": 1648319420, + "duration": 1800, + "expired": true + }, + "da1c0d1b-1ecf-4fe0-9acd-dc3d49640f8f": { + "address": "79 5th St NW, Atlanta, GA 30308", + "occupied": false + }, + "e74d1791-1ef9-4a5a-b98f-0d7d67e533e1": { + "address": "817 W Peachtree St NE, Atlanta, GA 30308", + "occupied": true, + "licensePlate": "8BQN612", + "startTime": 1648321556, + "expiration": 1649014216, + "duration": 604800, + "expired": false + }, + "4420c88b-a0dd-470c-925c-c713a73b8f17": { + "address": "79 5th St NW, Atlanta, GA 30308", + "licensePlate": "7MSW817", + "occupied": true, + "startTime": 1648317607, + "expiration": 1648324807, + "duration": 7200, + "expired": true + }, + "f1056ff9-e0bd-47a0-adba-5e11670897e7": { + "address": "675 Ponce de Leon Ave NE, Atlanta, GA 30308", + "occupied": false + } +} diff --git a/backend/db/collection.js b/backend/db/collection.js index 1a9d283..d852dc3 100644 --- a/backend/db/collection.js +++ b/backend/db/collection.js @@ -1,7 +1,8 @@ -const db = require("./db"); -const DB_COLLECTION = "families"; +const debug = require('../utils/debug'); +const db = require('./db'); +const DB_COLLECTION = 'parkLightning'; const collection = db.collection(DB_COLLECTION); -console.log(`Connected to collection ${db.projectId}/${DB_COLLECTION}`); +debug.info(`Connected to collection ${db.projectId}/${DB_COLLECTION}`); -export default collection; +module.exports = collection; \ No newline at end of file diff --git a/backend/db/db.js b/backend/db/db.js index 84aca95..4c714e9 100644 --- a/backend/db/db.js +++ b/backend/db/db.js @@ -1,22 +1,13 @@ -import { initializeApp } from "firebase/app"; -import { getFirestore, collection, getDocs } from "firebase/firestore"; - -const firebaseConfig = require("../service-account.json"); -console.log(firebaseConfig); - -const app = initializeApp(firebaseConfig); - -const db = getFirestore(app); +const utils = require('../utils/debug'); +const admin = require('firebase-admin'); +const GOOGLE_APPLICATION_CREDENTIALS = JSON.parse( + Buffer.from(process.env.GOOGLE_APPLICATION_CREDENTIALS, 'base64').toString( + 'utf-8' + ) +); +admin.initializeApp({ + credential: admin.credential.cert(GOOGLE_APPLICATION_CREDENTIALS), +}); +const db = admin.firestore(); utils.info(`Connection to GCP Project ${db.projectId} successful!`); -export default db; - -export const getFamily = async (familyName) => { - const querySnapshot = await getDocs(collection(db, "families")); - let res = null; - const docs = await querySnapshot.forEach((doc) => { - if (doc.data()["family-name"] === familyName) { - res = doc.data(); - } - }); - return res; -}; +module.exports = db; \ No newline at end of file diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 0000000..4385734 --- /dev/null +++ b/backend/package.json @@ -0,0 +1,27 @@ +{ + "name": "park-lightning-api", + "version": "1.0.0", + "description": "Parking at the speed of lightning ⚡️", + "main": "server.js", + "repository": "git@github.com:atlantabitdevs/park-lightning.git", + "author": "atlantabitdevs@gmail.com", + "license": "MIT", + "private": false, + "scripts": { + "start": "NODE_ENV=prod node server.js", + "dev": "NODE_ENV=local nodemon server.js" + }, + "dependencies": { + "cors": "^2.8.5", + "debug": "^4.3.4", + "dotenv": "^16.0.0", + "express": "^4.17.3", + "firebase-admin": "^10.0.2", + "http-errors": "^2.0.0", + "morgan": "^1.10.0", + "nodemon": "^2.0.15", + "opennode": "^1.3.0", + "uuid": "^8.3.2", + "winston": "^3.6.0" + } +} diff --git a/frontend/src/sensei/admin.js b/backend/sensei/admin.js similarity index 76% rename from frontend/src/sensei/admin.js rename to backend/sensei/admin.js index eb74053..83d6a99 100644 --- a/frontend/src/sensei/admin.js +++ b/backend/sensei/admin.js @@ -1,7 +1,7 @@ -const fetch = require('node'); +const fetch = require('node-fetch'); const BASE_URL = process.env.BASE_URL; -const InitSensei = async (username, passphrase, alias, electrum_url, start) => { +const initSensei = async (username, passphrase, alias, electrum_url, start) => { const res = await fetch(`${BASE_URL}/v1/init`, { method: 'POST', body: { @@ -17,7 +17,7 @@ const InitSensei = async (username, passphrase, alias, electrum_url, start) => { return await res.json(); }; -const ListNodes = async (page, take, query) => { +const listNodes = async (page, take, query) => { const res = await fetch(`${BASE_URL}/v1/nodes`, { method: 'GET', }); @@ -26,7 +26,7 @@ const ListNodes = async (page, take, query) => { return await res.json(); }; -const CreateNode = async (username, passphrase, alias, start) => { +const createNode = async (username, passphrase, alias, start) => { const res = await fetch(`${BASE_URL}/v1/nodes`, { method: 'POST', body: { @@ -41,7 +41,7 @@ const CreateNode = async (username, passphrase, alias, start) => { return await res.json(); }; -const StartNode = async (pubkey, passphrase) => { +const startNode = async (pubkey, passphrase) => { const res = await fetch(`${BASE_URL}/v1/nodes/start`, { method: 'POST', body: { @@ -54,7 +54,7 @@ const StartNode = async (pubkey, passphrase) => { return await res.json(); }; -const StopNode = async (pubkey) => { +const stopNode = async (pubkey) => { const res = await fetch(`${BASE_URL}/v1/nodes/stop`, { method: 'POST', body: { @@ -66,7 +66,7 @@ const StopNode = async (pubkey) => { return await res.json(); }; -const DeleteNode = async (pubkey) => { +const deleteNode = async (pubkey) => { const res = await fetch(`${BASE_URL}/v1/nodes/delete`, { method: 'POST', body: { @@ -78,7 +78,7 @@ const DeleteNode = async (pubkey) => { return await res.json(); }; -const NodeStatus = async () => { +const nodeStatus = async () => { const res = await fetch(`${BASE_URL}/v1/status`, { method: 'GET', }); @@ -87,7 +87,7 @@ const NodeStatus = async () => { return await res.json(); }; -const StartSensi = async (passphrase) => { +const startSensi = async (passphrase) => { const res = await fetch(`${BASE_URL}/v1/start`, { method: 'POST', body: { @@ -99,7 +99,7 @@ const StartSensi = async (passphrase) => { return await res.json(); }; -const Login = async (username, passphrase) => { +const login = async (username, passphrase) => { const res = await fetch(`${BASE_URL}/v1/login`, { method: 'POST', body: { @@ -112,7 +112,7 @@ const Login = async (username, passphrase) => { return await res.json(); }; -const Logout = async () => { +const logout = async () => { const res = await fetch(`${BASE_URL}/v1/logout`, { method: 'POST', }); @@ -121,7 +121,7 @@ const Logout = async () => { return await res.json(); }; -const GetConfig = async () => { +const getConfig = async () => { const res = await fetch(`${BASE_URL}/v1/config`, { method: 'GET', }); @@ -130,7 +130,7 @@ const GetConfig = async () => { return await res.json(); }; -const UpdateConfig = async (electrum_url) => { +const updateConfig = async (electrum_url) => { const res = await fetch(`${BASE_URL}/v1/config`, { method: 'POST', body: { @@ -143,16 +143,16 @@ const UpdateConfig = async (electrum_url) => { }; export { - InitSensei, - ListNodes, - CreateNode, - StartNode, - StopNode, - DeleteNode, - NodeStatus, - StartSensi, - Login, - Logout, - GetConfig, - UpdateConfig, + initSensei, + listNodes, + createNode, + startNode, + stopNode, + deleteNode, + nodeStatus, + startSensi, + login, + logout, + getConfig, + updateConfig, }; diff --git a/backend/server.js b/backend/server.js new file mode 100644 index 0000000..d070c96 --- /dev/null +++ b/backend/server.js @@ -0,0 +1,7 @@ +const app = require('./app'); +const PORT = process.env.PORT || 4000; +const debug = require('./utils/debug'); + +app.listen(PORT, () => { + debug.info(`API Server listening on http://127.0.0.1:${PORT}`); +}); diff --git a/backend/utils/debug.js b/backend/utils/debug.js new file mode 100644 index 0000000..251c032 --- /dev/null +++ b/backend/utils/debug.js @@ -0,0 +1,20 @@ +const logger = require('./logger'); + +const info = (i) => { + logger.info(i); +}; + +const verbose = (v) => { + logger.verbose(v); +}; + +const error = (e) => { + logger.error(e); +}; + +module.exports = { + verbose, + info, + error, +}; + diff --git a/backend/utils/logger.js b/backend/utils/logger.js new file mode 100644 index 0000000..b2f2667 --- /dev/null +++ b/backend/utils/logger.js @@ -0,0 +1,19 @@ +const { createLogger, format, transports } = require('winston'); + +module.exports = createLogger({ + transports: [ + new transports.File({ + filename: 'log/server.log', + format: format.combine( + format.timestamp({ format: 'MMM-DD-YYYY HH:mm:ss' }), + format.align(), + format.printf( + (level) => `${level.level}: ${[level.timestamp]}: ${level.message}` + ) + ), + }), + new transports.Console({ + format: format.combine(format.colorize(), format.simple()), + }), + ], +}); \ No newline at end of file