🚀 Initial commit for rzmk/smart-brain-api!
This commit is contained in:
commit
16e22cda96
10 changed files with 4048 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
node_modules
|
||||||
28
README.md
Normal file
28
README.md
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
<div align="center">
|
||||||
|
<a href="https://github.com/rzmk/smart-brain-api">
|
||||||
|
<img src="server.svg" alt="Server" width="80" height="80">
|
||||||
|
</a>
|
||||||
|
<h3 align="center">smart-brain-api</h3>
|
||||||
|
Backend server for <a href="https://github.com/rzmk/smart-brain"> rzmk/smart-brain</a>.
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## Endpoints
|
||||||
|
|
||||||
|
- `/signin`: Sign in verification with email and password.
|
||||||
|
- `/register`: Register with name, email, and password.
|
||||||
|
- `/profile/:id`: Get user profile (once logged in).
|
||||||
|
- `/image`: Side effects of image detection (increment entries count).
|
||||||
|
- `/imageurl`: Image URL upload handler ([Clarifai API](https://www.clarifai.com/) call).
|
||||||
|
|
||||||
|
## Stack
|
||||||
|
|
||||||
|
- [Node.js](https://nodejs.org/) (Javascript runtime)
|
||||||
|
- [Express](https://expressjs.com/) (web app framework)
|
||||||
|
- [PostgreSQL](https://www.postgresql.org/) (database)
|
||||||
|
- [Heroku](https://www.heroku.com/) (cloud hosting)
|
||||||
|
- [Knex.js](https://knexjs.org/) (SQL query builder)
|
||||||
|
|
||||||
|
## Acknowledgements
|
||||||
|
|
||||||
|
- [Zero to Mastery Academy](https://academy.zerotomastery.io/p/complete-web-developer-zero-to-mastery)
|
||||||
29
controllers/image.js
Normal file
29
controllers/image.js
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
const Clarifai = require("clarifai"); // Face detection model API
|
||||||
|
|
||||||
|
const app = new Clarifai.App({
|
||||||
|
apiKey: process.env.API_CLARIFAI,
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleApiCall = (req, res) => {
|
||||||
|
app.models
|
||||||
|
.predict(Clarifai.FACE_DETECT_MODEL, req.body.input)
|
||||||
|
.then((data) => {
|
||||||
|
res.json(data);
|
||||||
|
})
|
||||||
|
.catch((err) => res.status(400).json("Unable to work with API."));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleImage = (req, res, db) => {
|
||||||
|
const { id } = req.body;
|
||||||
|
db("users")
|
||||||
|
.where("id", "=", id)
|
||||||
|
.increment("entries", 1) // "entries" is the total number of image uploads the user has made
|
||||||
|
.returning("entries")
|
||||||
|
.then((entries) => res.json(entries[0]))
|
||||||
|
.catch((err) => res.status(400).json("Unable to get entries."));
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
handleImage: handleImage,
|
||||||
|
handleApiCall: handleApiCall,
|
||||||
|
};
|
||||||
20
controllers/profile.js
Normal file
20
controllers/profile.js
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
const handleProfileGet = (req, res, db) => {
|
||||||
|
const { id } = req.params;
|
||||||
|
db.select("*")
|
||||||
|
.from("users")
|
||||||
|
.where({
|
||||||
|
id: id,
|
||||||
|
})
|
||||||
|
.then((user) => {
|
||||||
|
if (user.length) {
|
||||||
|
res.json(user[0]);
|
||||||
|
} else {
|
||||||
|
res.status(400).json("Not found.");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => res.status(400).json("Error getting user."));
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
handleProfileGet: handleProfileGet,
|
||||||
|
};
|
||||||
43
controllers/register.js
Normal file
43
controllers/register.js
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
const handleRegister = (req, res, db, bcrypt) => {
|
||||||
|
const { name, email, password } = req.body;
|
||||||
|
// Validate the input to not be empty
|
||||||
|
if (!email || !name || !password) {
|
||||||
|
return res.status(400).json("Incorrect form submission.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash (encrypt) the password
|
||||||
|
const hash = bcrypt.hashSync(password);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Database transaction to make sure all the queries are executed together
|
||||||
|
* First query is to insert the user into the login table
|
||||||
|
* Second query is to insert the user into the users table
|
||||||
|
* Then we return the user
|
||||||
|
*/
|
||||||
|
db.transaction((trx) => {
|
||||||
|
trx.insert({
|
||||||
|
hash: hash,
|
||||||
|
email: email,
|
||||||
|
})
|
||||||
|
.into("login")
|
||||||
|
.returning("email")
|
||||||
|
.then((loginEmail) => {
|
||||||
|
return trx("users")
|
||||||
|
.returning("*")
|
||||||
|
.insert({
|
||||||
|
email: loginEmail[0],
|
||||||
|
name: name,
|
||||||
|
joined: new Date(),
|
||||||
|
})
|
||||||
|
.then((user) => {
|
||||||
|
res.json(user[0]);
|
||||||
|
})
|
||||||
|
.then(trx.commit);
|
||||||
|
})
|
||||||
|
.catch(trx.rollback);
|
||||||
|
}).catch((err) => res.status(400).json("Unable to register."));
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
handleRegister: handleRegister,
|
||||||
|
};
|
||||||
34
controllers/signin.js
Normal file
34
controllers/signin.js
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
const handleSignIn = (req, res, db, bcrypt) => {
|
||||||
|
const { email, password } = req.body;
|
||||||
|
// Validate the input to not be empty
|
||||||
|
if (!email || !password) {
|
||||||
|
return res.status(400).json("Incorrect form submission.");
|
||||||
|
}
|
||||||
|
// Check if the user exists in the database
|
||||||
|
db.select("email", "hash")
|
||||||
|
.from("login")
|
||||||
|
.where("email", "=", email)
|
||||||
|
.then((data) => {
|
||||||
|
const isValid = bcrypt.compareSync(password, data[0].hash);
|
||||||
|
// If the user exists and the password is correct, return the user
|
||||||
|
if (isValid) {
|
||||||
|
return db
|
||||||
|
.select("*")
|
||||||
|
.from("users")
|
||||||
|
.where("email", "=", email)
|
||||||
|
.then((user) => {
|
||||||
|
res.json(user[0]);
|
||||||
|
})
|
||||||
|
.catch((err) =>
|
||||||
|
res.status(400).json("Unable to get user.")
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
res.status(400).json("Invalid credentials.");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => res.status(400).json("Invalid credentials."));
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
handleSignIn: handleSignIn,
|
||||||
|
};
|
||||||
3816
package-lock.json
generated
Normal file
3816
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
24
package.json
Normal file
24
package.json
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"name": "smart-brain-api",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "server.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node server.js",
|
||||||
|
"start:dev": "nodemon server.js"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"bcrypt-nodejs": "^0.0.3",
|
||||||
|
"clarifai": "^2.9.1",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"express": "^4.17.2",
|
||||||
|
"knex": "^0.95.15",
|
||||||
|
"pg": "^8.7.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"nodemon": "^2.0.15"
|
||||||
|
}
|
||||||
|
}
|
||||||
52
server.js
Normal file
52
server.js
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
const express = require("express");
|
||||||
|
const bcrypt = require("bcrypt-nodejs"); // bcrypt-nodejs is a module that we can use to hash passwords
|
||||||
|
const cors = require("cors");
|
||||||
|
const knex = require("knex"); // knex is a database library
|
||||||
|
|
||||||
|
// Import endpoint controllers
|
||||||
|
const register = require("./controllers/register");
|
||||||
|
const signin = require("./controllers/signin");
|
||||||
|
const profile = require("./controllers/profile");
|
||||||
|
const image = require("./controllers/image");
|
||||||
|
|
||||||
|
// Set up database connection
|
||||||
|
const db = knex({
|
||||||
|
client: "pg",
|
||||||
|
connection: {
|
||||||
|
connectionString: process.env.DATABASE_URL,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
app.use(cors());
|
||||||
|
app.use(express.urlencoded({ extended: false }));
|
||||||
|
app.use(express.json());
|
||||||
|
|
||||||
|
app.get("/", (req, res) => {
|
||||||
|
res.send("Success!");
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/signin", (req, res) => {
|
||||||
|
signin.handleSignIn(req, res, db, bcrypt);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/register", (req, res) => {
|
||||||
|
register.handleRegister(req, res, db, bcrypt);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/profile/:id", (req, res) => {
|
||||||
|
profile.handleProfileGet(req, res, db);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.put("/image", (req, res) => {
|
||||||
|
image.handleImage(req, res, db);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/imageurl", (req, res) => {
|
||||||
|
image.handleApiCall(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(process.env.PORT || 3000, () => {
|
||||||
|
console.log(`app is running on port ${process.env.PORT}!`);
|
||||||
|
});
|
||||||
1
server.svg
Normal file
1
server.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 154.58 141.53"><defs><style>.cls-1{fill:#ecebeb;}.cls-2{fill:#d8d2d2;}.cls-3{fill:#637582;}.cls-4{fill:#94a5af;}.cls-5{fill:#455760;}.cls-6{fill:#ffc754;}.cls-7{fill:#a2fce2;}.cls-10,.cls-11,.cls-8,.cls-9{fill:none;stroke:#3e3127;stroke-miterlimit:10;}.cls-11,.cls-8{stroke-width:3px;}.cls-9{stroke-width:5px;}.cls-10{stroke-width:4px;}.cls-11{stroke-linecap:round;}.cls-12{fill:#fff;}</style></defs><title>Asset 29</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><rect class="cls-1" x="16.84" y="68.67" width="120.91" height="26.11"/><rect class="cls-2" x="17.78" y="78.06" width="118.56" height="5.04"/><path class="cls-1" d="M22,2H24.2a4.37,4.37,0,0,1,4.37,4.37V26.91a0,0,0,0,1,0,0h-11a0,0,0,0,1,0,0V6.37A4.37,4.37,0,0,1,22,2Z"/><rect class="cls-3" x="3.46" y="26.9" width="149.58" height="51.13" rx="5"/><path class="cls-4" d="M13.51,78H7.24c-2.46,0-4.47-2.3-4.47-5.14V32c0-2.83,2-5.13,4.47-5.13h6.27C11,26.9,9,29.2,9,32V72.9C9,75.74,11,78,13.51,78Z"/><path class="cls-5" d="M151.32,30V72.26a4.41,4.41,0,0,1-4.41,4.41H73.54c31.92-9.53,44.71-37,49.29-51.13h24.08A4.41,4.41,0,0,1,151.32,30Z"/><circle class="cls-6" cx="23.45" cy="49.59" r="5.78"/><circle class="cls-7" cx="58.43" cy="49.59" r="5.78"/><circle class="cls-6" cx="40.94" cy="49.59" r="5.78"/><circle class="cls-8" cx="40.94" cy="49.59" r="5.78"/><rect class="cls-9" x="2.5" y="26.91" width="149.58" height="51.13" rx="5"/><rect class="cls-10" x="16.84" y="78.06" width="120.91" height="24.77"/><circle class="cls-8" cx="23.4" cy="49.59" r="5.78"/><rect class="cls-3" x="3.46" y="87.89" width="149.58" height="51.13" rx="5"/><path class="cls-5" d="M152.08,92.31v42.3a4.4,4.4,0,0,1-4.41,4.41H74.3c31.92-9.52,44.71-37,49.29-51.12h24.08A4.4,4.4,0,0,1,152.08,92.31Z"/><line class="cls-11" x1="21.02" y1="103.74" x2="36.19" y2="103.74"/><line class="cls-11" x1="21.02" y1="113.46" x2="36.19" y2="113.46"/><line class="cls-11" x1="21.02" y1="123.18" x2="36.19" y2="123.18"/><line class="cls-11" x1="53.48" y1="103.74" x2="68.65" y2="103.74"/><line class="cls-11" x1="53.48" y1="113.46" x2="68.65" y2="113.46"/><line class="cls-11" x1="53.48" y1="123.18" x2="68.65" y2="123.18"/><line class="cls-11" x1="85.94" y1="103.74" x2="101.1" y2="103.74"/><line class="cls-11" x1="85.94" y1="113.46" x2="101.1" y2="113.46"/><line class="cls-11" x1="85.94" y1="123.18" x2="101.1" y2="123.18"/><line class="cls-11" x1="118.39" y1="103.74" x2="133.56" y2="103.74"/><line class="cls-11" x1="118.39" y1="113.46" x2="133.56" y2="113.46"/><line class="cls-11" x1="118.39" y1="123.18" x2="133.56" y2="123.18"/><polygon class="cls-12" points="27.86 9.43 17.67 18.25 17.67 14.92 27.86 6.1 27.86 9.43"/><polygon class="cls-12" points="27.86 15.23 17.67 24.05 17.67 20.72 27.86 11.9 27.86 15.23"/><path class="cls-10" d="M23.1,2h0a5.48,5.48,0,0,1,5.48,5.48V26.91a0,0,0,0,1,0,0h-11a0,0,0,0,1,0,0V7.48A5.48,5.48,0,0,1,23.1,2Z"/><circle class="cls-8" cx="58.43" cy="49.59" r="5.78"/><circle class="cls-5" cx="104.77" cy="54.05" r="6.56"/><circle class="cls-5" cx="95.18" cy="44.31" r="2.98"/><path class="cls-4" d="M13.51,139H7.24c-2.46,0-4.47-2.3-4.47-5.13V93c0-2.83,2-5.13,4.47-5.13h6.27C11,87.89,9,90.19,9,93V133.9C9,136.73,11,139,13.51,139Z"/><rect class="cls-9" x="2.5" y="87.89" width="149.58" height="51.13" rx="5"/></g></g></svg>
|
||||||
|
After Width: | Height: | Size: 3.3 KiB |
Loading…
Add table
Add a link
Reference in a new issue