Compare commits
11 Commits
3bcfeb1dee
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| a5dbf762b6 | |||
| 1ccca077fa | |||
| 5524cabab4 | |||
| d3586f8cab | |||
| 3f73ca3b44 | |||
| 40fb916437 | |||
| fbbadd6fbf | |||
| 845d0eea7b | |||
| f2c4f790ca | |||
| 27fe7a03e2 | |||
| 2f130478db |
@@ -0,0 +1 @@
|
||||
node_modules
|
||||
@@ -1,28 +1,99 @@
|
||||
# This project is a toy project for training and quality assurance purposes
|
||||
|
||||
# node_task
|
||||
|
||||
1. Clone repo into your github
|
||||
2. Use https://expressjs.com/
|
||||
3. Use this library to connect to mysql https://sequelize.org/
|
||||
4. Integrate the following api into this project
|
||||
2. Create a page on route / that match the figma file https://www.figma.com/file/wDRDYmVG1qZI3qff32631b/Untitled?node-id=0%3A1
|
||||
3. Call the weather api https://www.weatherapi.com/pricing.aspx to show the temperature in celsius and draw a sun if sunny. If its raining show clouds (choose an image from google). If its snowing, choose a snowflake image from google. This widget should update every 5 minute calling teh weather api. Call weather api from backend route. Do error handling.
|
||||
4. Given the current UTC time, create 4 time widget where you convert the UTC time to local time of london, EST, Nigeria and Pakistan time. Every second, the time should be updated like regular clock. Create backend route to return the time.
|
||||
5. Create a widget that call autocomplete api /airports?search= that dropdown the airports that match the search terms partially. Minimum number of character to trigger autocomplete is 3. Create backend route to handle this.
|
||||
6. Show map widget for airport chosen using latitude and longitude from autocomplete chosen airport. Use this map api https://openlayers.org/doc/quickstart.html
|
||||
7. Create a widget that calculate the distance from artic circle to airport https://stackoverflow.com/questions/27928/calculate-distance-between-two-latitude-longitude-points-haversine-formula
|
||||
|
||||
https://www.weatherapi.com/pricing.aspx
|
||||
8. Use tailwindcss for ui
|
||||
|
||||
https://github.com/stripe/stripe-php
|
||||
9. Create db table called analytic (id, create_at, widget_name, browser_type, ). Everytime user clicks on a widget, call api /analytic and send the widget name to log it in the db.
|
||||
|
||||
5. Create your own airport api to retrieve from the autocomplete below. We have given you an airport.csv to use to get the data fields.
|
||||
10. Number of Click widget call an api every minute that count the # of rows in analytic.
|
||||
|
||||
6. Use bootstrap 4 for ui
|
||||
11. Create widget export xml that will export the analytic database as xml file. Create backend route to handle this.
|
||||
|
||||
7. Make the following page by end of day, no exceptions:
|
||||
- Make a main page where we we call an api to get current weather in your country and display it on frontend (degree, the type of weather, humidity)
|
||||
- Make an 2 autocomplete dropdown where we call airport api where we filter by country and show the airports in the dropdown
|
||||
- Calculate the distance between the 2 airports using the lat and lng and multiple it by $10. Make a stripe form to ask user to pay that amount. Payment must be processed.
|
||||
- Save the order placed into an order database table (id, from_airport, from_country, to_airport, to_country, total, stripe_id, status(paid, failed))
|
||||
- Create thank you page showing their order in a table
|
||||
(b) add route to import the analytic xml file.
|
||||
|
||||
12. Query https://www.reddit.com/r/programming.json and create a reddit widget where we show the top 4 even post as cards in the widget (title, link, who posted it). Create backend route to handle this.
|
||||
|
||||
13. Count # of coin widget. Once user type in a money amount, and click calculate, we show how many bills to add up to the money amount. The bills allowed are: $20 bill, $10 bill, $5 bill, $1 bill, $25 cent, $10 cent, $5 cent, $1 cent. Create backend route to handle this.
|
||||
|
||||
14. Rate limit analytic api to only be able to be called 10 times a minute.
|
||||
|
||||
15. If the rate limit is exceeded, redirect to a page that requires user to pay for the service ($5) with stripe. Free to use stripe checkout or payment element.
|
||||
|
||||
15. Create upload widget where we upload image to server. Save image to db table. Always show the latest image uploaded above upload button.
|
||||
|
||||
16. Read this documentation https://www.npmjs.com/package/speakeasy and implement a modal popup that blocks the dashboard. Unless 2FA is verified, cannot see dashboard.
|
||||
|
||||
17. Implement a single long polling chat. Here's a document explaining it
|
||||
|
||||
https://www.enjoyalgorithms.com/blog/long-polling-in-system-design
|
||||
|
||||
https://javascript.info/long-polling
|
||||
|
||||
https://www.technouz.com/4879/long-polling-explained-with-an-example/
|
||||
|
||||
Redis:
|
||||
|
||||
https://redis.io/docs/connect/clients/nodejs/
|
||||
|
||||
https://www.npmjs.com/package/redis
|
||||
|
||||
In demo what should happen:
|
||||
If I open 2 browsers to /chat, both users should connect to chat room. Use redis to store state of chatroom.
|
||||
Have basic html ui showing the chat log using UL and single input box with send button to send messages.
|
||||
When I type a message in send message, call /send POST api to send message to backend. The chat room is updated with new message.
|
||||
All client browsers have an api called /poll that check if chat room on redis is updated. If updated, it will return 200 and frontend
|
||||
need to call GET /chat/all to pull all the messages in chat room which you will update the UL. Have a button called save where it will save the current chat room chat messages to database table chat with fields (id, create_at, chat_messages).
|
||||
|
||||
|
||||
|
||||
Stripe key:
|
||||
Stripe key:
|
||||
|
||||
pk_test_51IWQUwH8oljXErmdg6L4MhsuB6tDdmumlHFfyNaopty2U27pmRcqMX1c868zn838lGQtU1eYV6bKRSQtMFWf36VT00aNsvnTOE
|
||||
|
||||
sk_test_51IWQUwH8oljXErmds28KftkL6o6jYIcPgYbBdfEmCPSuAlIh0fgoS4NADcCmsIZbdQ3p5nbAeCOcGkSmo38U9BIe00BdOenrqo
|
||||
|
||||
18. ### Simple Flow Builder
|
||||
|
||||
#### Core Features
|
||||
|
||||
##### Flow Creation:
|
||||
|
||||
* Users can create a flow with a name and description.
|
||||
* Each flow consists of one or more tasks (each task should have a default input on creation).
|
||||
* Tasks are executed sequentially (non-branching).
|
||||
|
||||
##### Task Actions:
|
||||
|
||||
* Each task performs a specific action with a single input.
|
||||
* Supported action types:
|
||||
+ Send Test Mail: Sends an email to the provided address.
|
||||
- Input: Email address (e.g., `test@example.com`).
|
||||
+ HTTP GET Request: Performs a GET request to the provided URL.
|
||||
- Input: URL (e.g., `https://api.example.com/data`).
|
||||
+ MySQL Select: Executes a SELECT query on a specified table and ID.
|
||||
- Input: Table name and ID (e.g., `users|123` [select from users where id = 123]).
|
||||
+ Drive Upload: Writes text to a file and uploads it to Google Drive.
|
||||
- Input: Text content (e.g., `Hello, World!`).
|
||||
- Hint: Create a google drive service account, enable drive api, generate credentials and utilize google sdk or api to upload.
|
||||
|
||||
##### Triggers:
|
||||
|
||||
* Webhook Trigger:
|
||||
+ A GET request with the action input in the query parameter called `payload`.
|
||||
+ Example: `localhost:3001?payload=test@example.com` (triggers the "Send Test Mail" action).
|
||||
* Click Trigger:
|
||||
+ On clicking a button, show a popup to receive the action input.
|
||||
+ Example: Click a button, enter `test@example.com`, and trigger the "Send Test Mail" action.
|
||||
|
||||
##### Execution:
|
||||
|
||||
* When a flow is triggered, execute all tasks in sequence.
|
||||
* Log the results of each task execution in a database table (`flow_logs`).
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,41 @@
|
||||
var createError = require('http-errors');
|
||||
var express = require('express');
|
||||
var path = require('path');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var logger = require('morgan');
|
||||
|
||||
var indexRouter = require('./routes/index');
|
||||
var usersRouter = require('./routes/users');
|
||||
|
||||
var app = express();
|
||||
|
||||
// view engine setup
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
app.set('view engine', 'pug');
|
||||
|
||||
app.use(logger('dev'));
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: false }));
|
||||
app.use(cookieParser());
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
|
||||
app.use('/', indexRouter);
|
||||
app.use('/users', usersRouter);
|
||||
|
||||
// catch 404 and forward to error handler
|
||||
app.use(function (req, res, next) {
|
||||
next(createError(404));
|
||||
});
|
||||
|
||||
// error handler
|
||||
app.use(function (err, req, res, next) {
|
||||
// set locals, only providing error in development
|
||||
res.locals.message = err.message;
|
||||
res.locals.error = req.app.get('env') === 'development' ? err : {};
|
||||
|
||||
// render the error page
|
||||
res.status(err.status || 500);
|
||||
res.render('error');
|
||||
});
|
||||
|
||||
module.exports = app;
|
||||
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var app = require('../app');
|
||||
var debug = require('debug')('node-task-2a:server');
|
||||
var http = require('http');
|
||||
|
||||
/**
|
||||
* Get port from environment and store in Express.
|
||||
*/
|
||||
|
||||
var port = normalizePort(process.env.PORT || '3000');
|
||||
app.set('port', port);
|
||||
|
||||
/**
|
||||
* Create HTTP server.
|
||||
*/
|
||||
|
||||
var server = http.createServer(app);
|
||||
|
||||
/**
|
||||
* Listen on provided port, on all network interfaces.
|
||||
*/
|
||||
|
||||
server.listen(port);
|
||||
server.on('error', onError);
|
||||
server.on('listening', onListening);
|
||||
|
||||
/**
|
||||
* Normalize a port into a number, string, or false.
|
||||
*/
|
||||
|
||||
function normalizePort(val) {
|
||||
var port = parseInt(val, 10);
|
||||
|
||||
if (isNaN(port)) {
|
||||
// named pipe
|
||||
return val;
|
||||
}
|
||||
|
||||
if (port >= 0) {
|
||||
// port number
|
||||
return port;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event listener for HTTP server "error" event.
|
||||
*/
|
||||
|
||||
function onError(error) {
|
||||
if (error.syscall !== 'listen') {
|
||||
throw error;
|
||||
}
|
||||
|
||||
var bind = typeof port === 'string'
|
||||
? 'Pipe ' + port
|
||||
: 'Port ' + port;
|
||||
|
||||
// handle specific listen errors with friendly messages
|
||||
switch (error.code) {
|
||||
case 'EACCES':
|
||||
console.error(bind + ' requires elevated privileges');
|
||||
process.exit(1);
|
||||
break;
|
||||
case 'EADDRINUSE':
|
||||
console.error(bind + ' is already in use');
|
||||
process.exit(1);
|
||||
break;
|
||||
default:
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event listener for HTTP server "listening" event.
|
||||
*/
|
||||
|
||||
function onListening() {
|
||||
var addr = server.address();
|
||||
var bind = typeof addr === 'string'
|
||||
? 'pipe ' + addr
|
||||
: 'port ' + addr.port;
|
||||
debug('Listening on ' + bind);
|
||||
}
|
||||
Generated
+2786
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "node-task-2a",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "node ./bin/www",
|
||||
"commit": "git add . && git commit -m \"Update Project purpose\" && git push"
|
||||
},
|
||||
"dependencies": {
|
||||
"cookie-parser": "~1.4.4",
|
||||
"debug": "~2.6.9",
|
||||
"express": "~4.16.1",
|
||||
"http-errors": "~1.6.3",
|
||||
"mariadb": "^3.0.1",
|
||||
"morgan": "~1.9.1",
|
||||
"mysql2": "^2.3.3",
|
||||
"ol": "^7.1.0",
|
||||
"pug": "^3.0.2",
|
||||
"sequelize": "^6.21.6"
|
||||
}
|
||||
}
|
||||
Vendored
BIN
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
body {
|
||||
padding: 50px;
|
||||
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #00B7FF;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
var express = require('express');
|
||||
var router = express.Router();
|
||||
|
||||
/* GET home page. */
|
||||
router.get('/', function(req, res, next) {
|
||||
res.render('index', { title: 'Express' });
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -0,0 +1,9 @@
|
||||
var express = require('express');
|
||||
var router = express.Router();
|
||||
|
||||
/* GET users listing. */
|
||||
router.get('/', function(req, res, next) {
|
||||
res.send('respond with a resource');
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -0,0 +1,6 @@
|
||||
extends layout
|
||||
|
||||
block content
|
||||
h1= message
|
||||
h2= error.status
|
||||
pre #{error.stack}
|
||||
@@ -0,0 +1,5 @@
|
||||
extends layout
|
||||
|
||||
block content
|
||||
h1= title
|
||||
p Welcome to #{title}
|
||||
@@ -0,0 +1,7 @@
|
||||
doctype html
|
||||
html
|
||||
head
|
||||
title= title
|
||||
link(rel='stylesheet', href='/stylesheets/style.css')
|
||||
body
|
||||
block content
|
||||
Reference in New Issue
Block a user