diff --git a/webapi/.dockerignore b/webapi/.dockerignore deleted file mode 100644 index 29d6828..0000000 --- a/webapi/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules -npm-debug.log - diff --git a/webapi/Dockerfile b/webapi/Dockerfile deleted file mode 100644 index 1891ac1..0000000 --- a/webapi/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -# Start image -FROM node:11 - -# Create app directory -WORKDIR /usr/src/app - -# Install app dependencies -# A wildcard is used to ensure both package.json AND package-lock.json are copied -# where available (npm@5+) -COPY package*.json ./ - -RUN npm install - -# Bundle app source -COPY . . \ No newline at end of file diff --git a/webapi/minitwit-api.js b/webapi/minitwit-api.js deleted file mode 100644 index 9665bc6..0000000 --- a/webapi/minitwit-api.js +++ /dev/null @@ -1,237 +0,0 @@ -const db = require('../app/persistence/sqlite'); - -// Express web framework https://expressjs.com/ -const express = require('express'); -const app = express(); -const bodyParser = require('body-parser'); - -let LATEST = 0; - -app.use(bodyParser.json()); - -// Get latest value -app.get('/latest', function (req, res) { - return res.json({"latest": LATEST}); -}); - -// Register handler -app.post('/register', async function(req, res) { - updateLatest(req); - const username = req.body.username; - const email = req.body.email; - const password = req.body.pwd; - console.log(username, email, password); - let error; - if(!username) { - error = 'You have to enter a username'; - } else if (!email || !email.includes('@')) { - error = 'You have to enter a valid email address'; - } else if (!password) { - error = 'You have to enter a password'; - } else if (await selectOne('SELECT 1 FROM user WHERE username = ?',[username])) { - error = 'The username is already taken'; - } else { - await insertOne( - 'INSERT INTO user(username, email, pw_hash) VALUES(?, ?, ?)', - [username, email, password.lameHash()]); - return res.status(204).send(); - - } - return res.json({"status": 400, "error_msg": error}).status(400).send(); -}); - -// Public timeline page -app.get('/msgs', async function(req, res) { - updateLatest(req); - let not_from_sim = notReqFromSimulator(req); - if (not_from_sim) { - let error = "You are not authorized to use this resource!"; - return res.json({"status": 403, "error_msg": error}).status(403).send(); - } - - let no_msgs = req.query.no ? req.query.no : 100; - let messages = await selectAll( - `select message.*, user.* from message, user - where message.author_id = user.user_id - order by message.pub_date desc limit ?`, - [no_msgs] - ); - - let filteredMsgs = []; - messages.forEach(function(msg) { - let filteredMsg = {}; - filteredMsg["content"] = msg["text"]; - filteredMsg["pub_date"] = msg["pub_date"]; - filteredMsg["user"] = msg["username"]; - filteredMsgs.push(filteredMsg); - }); - return res.json(filteredMsgs); -}); - -app.get('/msgs/:username', async function(req, res) { - updateLatest(req); - - let not_from_sim = notReqFromSimulator(req); - if (not_from_sim) { - let error = "You are not authorized to use this resource!"; - return res.json({"status": 403, "error_msg": error}).status(403).send(); - } - const { username } = req.params; - let no_msgs = req.query.no ? req.query.no : 100; - - let profile_user = await selectOne( - 'SELECT * FROM user WHERE username = ?', - [username]); - - if(!profile_user) { - return res.status(404).send(); - } - - let messages = await selectAll( - `SELECT message.*, user.* FROM message, user WHERE - user.user_id = message.author_id AND user.user_id = ? - ORDER BY message.pub_date DESC LIMIT ?`, - [profile_user.user_id, no_msgs]); - - let filteredMsgs = []; - messages.forEach(function(msg) { - let filteredMsg = {}; - filteredMsg["content"] = msg["text"]; - filteredMsg["pub_date"] = msg["pub_date"]; - filteredMsg["user"] = msg["username"]; - filteredMsgs.push(filteredMsg); - }); - - return res.json(filteredMsgs); -}); - -app.post('/msgs/:username', async function (req, res) { - updateLatest(req); - - let not_from_sim = notReqFromSimulator(req); - if (not_from_sim) { - let error = "You are not authorized to use this resource!"; - return res.json({"status": 403, "error_msg": error}).status(403).send(); - } - const { username } = req.params; - let profile_user = await selectOne( - 'SELECT * FROM user WHERE username = ?', - [username]); - - if(!profile_user) { - return res.status(404).send(); - } - - let content = req.body.content; - - await insertOne( - 'INSERT INTO message(author_id, text, pub_date) VALUES(?, ?, ?)', - [profile_user.user_id, content, Date.now()]); - - return res.status(204).send(); -}); - -app.get('/fllws/:username', async function (req, res) { - updateLatest(req); - - let not_from_sim = notReqFromSimulator(req); - if (not_from_sim) { - let error = "You are not authorized to use this resource!"; - return res.json({"status": 403, "error_msg": error}).status(403).send(); - } - const { username } = req.params; - - let profile_user = await selectOne( - 'SELECT * FROM user WHERE username = ?', - [username]); - - if(!profile_user) { - return res.status(404).send(); - } - - let no_followers = req.query.no ? req.query.no : 100; - - let followers = await selectAll( - `SELECT user.username FROM user - INNER JOIN follower ON follower.whom_id = user.user_id - WHERE follower.who_id = ? - LIMIT ?`, - [profile_user.user_id, no_followers] - ); - let followers_names = []; - followers.forEach((follower) => { - followers_names.push(follower["username"]) - }); - - return res.json({"follows": followers_names}); -}); - -app.post('/fllws/:username', async function (req, res) { - updateLatest(req); - - let not_from_sim = notReqFromSimulator(req); - if (not_from_sim) { - let error = "You are not authorized to use this resource!"; - return res.json({"status": 403, "error_msg": error}).status(403).send(); - } - const { username } = req.params; - - let profile_user = await selectOne( - 'SELECT * FROM user WHERE username = ?', - [username]); - - if(!profile_user) { - return res.status(404).send(); - } - - if (req.body.follow) { - let follow_username = req.body.follow; - let follows_user = await selectOne( - 'SELECT * FROM user WHERE username = ?', - [follow_username]); - - if (!follows_user) { - return res.status(404).send(); - } - - await insertOne( - 'INSERT INTO follower (who_id, whom_id) VALUES (?, ?)', - [profile_user.user_id, follows_user.user_id]); - - return res.status(204).send(); - } - - if (req.body.unfollow) { - let unfollow_username = req.body.unfollow; - let unfollows_user = await selectOne( - 'SELECT * FROM user WHERE username = ?', - [unfollow_username]); - - if (!unfollows_user){ - return res.status(404).send(); - } - - await deleteRows( - 'DELETE FROM follower WHERE who_id = ? AND whom_id = ?', - [profile_user.user_id, unfollows_user.user_id]); - - return res.status(204).send(); - } -}); - - -// Start application -app.listen(5001); -console.log('MiniTwit API is running on port 5001..'); - -// Checks if simulator -function notReqFromSimulator(request) { - let from_sim = request.headers.authorization; - return from_sim !== "Basic c2ltdWxhdG9yOnN1cGVyX3NhZmUh"; -} - -// Update latest -function updateLatest(request) { - let try_latest = request.query.latest ? request.query.latest : -1; - LATEST = try_latest !== -1 ? parseInt(try_latest) : LATEST; -} diff --git a/webapi/package.json b/webapi/package.json deleted file mode 100644 index 9e41a47..0000000 --- a/webapi/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "minitwit", - "version": "0.0.1", - "description": "Minitwit.py refactor in Nodejs/Express", - "main": "minitwit.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC", - "dependencies": { - "express": "^4.17.1", - "sqlite3": "^4.1.1", - "body-parser": "latest" - } -} diff --git a/webapp/.dockerignore b/webapp/.dockerignore deleted file mode 100644 index 29d6828..0000000 --- a/webapp/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules -npm-debug.log - diff --git a/webapp/Dockerfile b/webapp/Dockerfile deleted file mode 100644 index 1891ac1..0000000 --- a/webapp/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -# Start image -FROM node:11 - -# Create app directory -WORKDIR /usr/src/app - -# Install app dependencies -# A wildcard is used to ensure both package.json AND package-lock.json are copied -# where available (npm@5+) -COPY package*.json ./ - -RUN npm install - -# Bundle app source -COPY . . \ No newline at end of file diff --git a/webapp/minitwit.js b/webapp/minitwit.js deleted file mode 100644 index d95e44b..0000000 --- a/webapp/minitwit.js +++ /dev/null @@ -1,255 +0,0 @@ -const db = require('../app/persistence/sqlite'); - -// Express web framework https://expressjs.com/ -const express = require('express'); -const app = express(); - -// Sessions https://github.com/expressjs/session#readme -var cookieParser = require('cookie-parser'); -var session = require('express-session'); - -// Constants -const PER_PAGE = 20; -const SECRET = 'shhhhhhhhhh'; - -// EJS template engine https://ejs.co/ -app.set('view engine', 'ejs'); -// Static files -app.use(express.static('static')); -// Form data -app.use(express.urlencoded()) -// Sessions -app.use(cookieParser()); -app.use(session({secret: SECRET})); - -//// Endpoints - -// Home (User feed or redirect to public timeline) -app.get('/', async function(req, res) { - if(!req.session.user_id) { - return res.redirect('/public') - } - let messages = await db.selectAll( - `SELECT message.*, user.* FROM message, user - WHERE message.author_id = user.user_id AND ( - user.user_id = ? OR - user.user_id IN ( - SELECT whom_id FROM follower - WHERE who_id = ?)) - ORDER BY message.pub_date DESC LIMIT ?`, - [req.session.user_id, req.session.user_id, PER_PAGE] - ); - res.render('pages/timeline', { - session_username: req.session.username, - session_user_id: req.session.user_id, - messages: messages, - myFeed: true - }); -}); - -// Public timeline page -app.get('/public', async function(req, res) { - let messages = await db.selectAll( - `select message.*, user.* from message, user - where message.author_id = user.user_id - order by message.pub_date desc limit ?`, - [PER_PAGE] - ); - res.render('pages/timeline', { - session_username: req.session.username, - session_user_id: req.session.user_id, - messages: messages - }); -}); - -// Add message handler -app.post('/add_message', async function(req, res) { - const user_id = req.session.user_id; - const text = req.body.text; - if(!user_id) { - return res.status(401).send(); - } else { - await db.insertOne( - 'INSERT INTO message(author_id, text, pub_date) VALUES(?, ?, ?)', - [user_id, text, Date.now()]) - let messages = await db.selectAll( - `SELECT message.*, user.* FROM message, user - WHERE message.author_id = user.user_id AND ( - user.user_id = ? OR - user.user_id IN ( - SELECT whom_id FROM follower - WHERE who_id = ?)) - ORDER BY message.pub_date DESC LIMIT ?`, - [req.session.user_id, req.session.user_id, PER_PAGE] - ); - return res.render('pages/timeline', { - flashes: ['Your message was recorded'], - session_user_id: req.session.user_id, - session_username: req.session.username, - messages: messages, - myFeed: true - }); - } -}); - -// Register page -app.get('/register', function(req, res) { - res.render('pages/register', { - username: '', - email: '', - }); -}); - -// Register handler -app.post('/register', async function(req, res) { - const username = req.body.username; - const email = req.body.email; - const password = req.body.password; - const password2 = req.body.password2; - let error; - if(!username) { - error = 'You have to enter a username'; - } else if (!email || !email.includes('@')) { - error = 'You have to enter a valid email address'; - } else if (!password) { - error = 'You have to enter a password'; - } else if (password !== password2) { - error = 'The two passwords do not match'; - } else if (await db.selectOne('SELECT 1 FROM user WHERE username = ?',[username])) { - error = 'The username is already taken'; - } else { - await db.insertOne( - 'INSERT INTO user(username, email, pw_hash) VALUES(?, ?, ?)', - [username, email, password.lameHash()]); - return res.render('pages/login', { - username: username, - flashes: ['You were successfully registered and can login now'] - }); - - } - res.render('pages/register', { - username: username, - email: email, - error: error - }); -}); - -// Login page -app.get('/login', function(req, res) { - res.render('pages/login', { - username: '', - }); -}); - -// Login handler -app.post('/login', async function(req, res) { - const username = req.body.username; - const password = req.body.password; - let error; - if(!username) { - error = 'You have to enter a username'; - } else if (!password) { - error = 'You have to enter a password'; - } else { - const user = await db.selectOne('SELECT * FROM user WHERE username = ?',[username]) - if(!user) { - error = 'Invalid username'; - } else if (user.pw_hash !== password.lameHash()) { - error = 'Invalid password'; - } else { - req.session.user_id = user.user_id; - req.session.username = username; - let messages = await db.selectAll( - `SELECT message.*, user.* FROM message, user - WHERE message.author_id = user.user_id AND ( - user.user_id = ? OR - user.user_id IN ( - SELECT whom_id FROM follower - WHERE who_id = ?)) - ORDER BY message.pub_date DESC LIMIT ?`, - [req.session.user_id, req.session.user_id, PER_PAGE] - ); - return res.render('pages/timeline', { - flashes: ['You were logged in'], - session_user_id: req.session.user_id, - session_username: req.session.username, - messages - }); - } - } - res.render('pages/login', { - username: username, - error: error - }); -}); - -// Logout -app.get('/logout', function(req, res) { - req.session.user_id = null; - req.session.username = null; - res.render('pages/timeline', { - flashes: ['You were logged out'] - }); -}); - -// User timeline page -app.get('/:username', async function(req, res) { - const { username } = req.params; - let profile_user = await db.selectOne( - 'SELECT * FROM user WHERE username = ?', - [username]); - if(!profile_user) { - return res.status(404).send(); - } - - let followed; - if(req.session.user_id) { - followed = await db.selectOne( - 'SELECT 1 FROM follower WHERE follower.who_id = ? and follower.whom_id = ?', - [req.session.user_id, profile_user.user_id]); - } - - let messages = await db.selectAll( - `SELECT message.*, user.* FROM message, user WHERE - user.user_id = message.author_id AND user.user_id = ? - ORDER BY message.pub_date DESC LIMIT ?`, - [profile_user.user_id, PER_PAGE] - ); - - res.render('pages/timeline', { - session_username: req.session.username, - session_user_id: req.session.user_id, - profile_username: profile_user.username, - profile_user_id: profile_user.user_id, - followed: !!followed, - messages: messages - }); -}); - -app.get('/:username/follow', async function(req, res) { - const { username } = req.params; - if (!req.session.user_id) - res.status(401).send(); - let whom = await db.selectOne('SELECT * FROM user WHERE username = ?',[username]); - if (!whom) - res.status(404).send(); - await db.insertOne('INSERT INTO follower (who_id, whom_id) VALUES (?, ?)', [req.session.user_id, whom.user_id]) - - return res.redirect('/' + username); -}); - -app.get('/:username/unfollow', async function(req, res) { - const { username } = req.params; - if (!req.session.user_id) - res.status(401).send(); - let whom = await db.selectOne('SELECT * FROM user WHERE username = ?',[username]); - if (!whom) - res.status(404).send(); - await db.deleteRows('DELETE FROM follower WHERE who_id = ? AND whom_id = ?', [req.session.user_id, whom.user_id]); - - return res.redirect('/' + username); -}); - -// Start application -app.listen(8080); -console.log('MiniTwit is running on port 8080..'); diff --git a/webapp/package.json b/webapp/package.json deleted file mode 100644 index 61bbabf..0000000 --- a/webapp/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "minitwit", - "version": "0.0.1", - "description": "Minitwit.py refactor in Nodejs/Express", - "main": "minitwit.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC", - "dependencies": { - "cookie-parser": "^1.4.4", - "ejs": "^3.0.1", - "express": "^4.17.1", - "express-session": "^1.17.0", - "sqlite3": "^4.1.1", - "body-parser": "latest" - } -} diff --git a/webapp/static/avatar.png b/webapp/static/avatar.png deleted file mode 100644 index 0174d88..0000000 Binary files a/webapp/static/avatar.png and /dev/null differ diff --git a/webapp/static/style.css b/webapp/static/style.css deleted file mode 100755 index 25587c4..0000000 --- a/webapp/static/style.css +++ /dev/null @@ -1,183 +0,0 @@ -body { - background: #CAECE9; - font-family: 'Trebuchet MS', sans-serif; - font-size: 14px; -} - -a { - color: #26776F; -} - -a:hover { - color: #333; -} - -input[type="text"], -input[type="password"] { - background: white; - border: 1px solid #BFE6E2; - padding: 2px; - font-family: 'Trebuchet MS', sans-serif; - font-size: 14px; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - color: #105751; -} - -input[type="submit"] { - background: #105751; - border: 1px solid #073B36; - padding: 1px 3px; - font-family: 'Trebuchet MS', sans-serif; - font-size: 14px; - font-weight: bold; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - color: white; -} - -div.page { - background: white; - border: 1px solid #6ECCC4; - width: 700px; - margin: 30px auto; -} - -div.page h1 { - background: #6ECCC4; - margin: 0; - padding: 10px 14px; - color: white; - letter-spacing: 1px; - text-shadow: 0 0 3px #24776F; - font-weight: normal; -} - -div.page div.navigation { - background: #DEE9E8; - padding: 4px 10px; - border-top: 1px solid #ccc; - border-bottom: 1px solid #eee; - color: #888; - font-size: 12px; - letter-spacing: 0.5px; -} - -div.page div.navigation a { - color: #444; - font-weight: bold; -} - -div.page h2 { - margin: 0 0 15px 0; - color: #105751; - text-shadow: 0 1px 2px #ccc; -} - -div.page div.body { - padding: 10px; -} - -div.page div.footer { - background: #eee; - color: #888; - padding: 5px 10px; - font-size: 12px; -} - -div.page div.followstatus { - border: 1px solid #ccc; - background: #E3EBEA; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - padding: 3px; - font-size: 13px; -} - -div.page ul.messages { - list-style: none; - margin: 0; - padding: 0; -} - -div.page ul.messages li { - margin: 10px 0; - padding: 5px; - background: #F0FAF9; - border: 1px solid #DBF3F1; - -moz-border-radius: 5px; - -webkit-border-radius: 5px; - min-height: 48px; -} - -div.page ul.messages p { - margin: 0; -} - -div.page ul.messages li img { - float: left; - padding: 0 10px 0 0; -} - -div.page ul.messages li small { - font-size: 0.9em; - color: #888; -} - -div.page div.twitbox { - margin: 10px 0; - padding: 5px; - background: #F0FAF9; - border: 1px solid #94E2DA; - -moz-border-radius: 5px; - -webkit-border-radius: 5px; -} - -div.page div.twitbox h3 { - margin: 0; - font-size: 1em; - color: #2C7E76; -} - -div.page div.twitbox p { - margin: 0; -} - -div.page div.twitbox input[type="text"] { - width: 585px; -} - -div.page div.twitbox input[type="submit"] { - width: 70px; - margin-left: 5px; -} - -ul.flashes { - list-style: none; - margin: 10px 10px 0 10px; - padding: 0; -} - -ul.flashes li { - background: #B9F3ED; - border: 1px solid #81CEC6; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - padding: 4px; - font-size: 13px; -} - -div.error { - margin: 10px 0; - background: #FAE4E4; - border: 1px solid #DD6F6F; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; - padding: 4px; - font-size: 13px; -} - -.avatar{ - height: 3rem; - width: 3rem; -} diff --git a/webapp/views/pages/login.ejs b/webapp/views/pages/login.ejs deleted file mode 100644 index fd182e8..0000000 --- a/webapp/views/pages/login.ejs +++ /dev/null @@ -1,15 +0,0 @@ -<%- include('../partials/header.ejs'); %> -

Sign In

-<% if (locals.error) { %> -
Error: <%= error %>
-<% } %> -
-
-
Username: -
-
Password: -
-
-
-
-<%- include('../partials/footer.ejs'); %> diff --git a/webapp/views/pages/register.ejs b/webapp/views/pages/register.ejs deleted file mode 100644 index 126941f..0000000 --- a/webapp/views/pages/register.ejs +++ /dev/null @@ -1,20 +0,0 @@ -<%- include('../partials/header.ejs'); %> -

Sign Up

-<% if (locals.error) { %> -
Error: <%= error %>
-<% } %> - -
-
-
Username: -
-
E-Mail: -
-
Password: -
-
Password (repeat): -
-
-
-
-<%- include('../partials/footer.ejs'); %> \ No newline at end of file diff --git a/webapp/views/pages/timeline.ejs b/webapp/views/pages/timeline.ejs deleted file mode 100644 index f282571..0000000 --- a/webapp/views/pages/timeline.ejs +++ /dev/null @@ -1,58 +0,0 @@ -<%- include('../partials/header.ejs'); %> -<% if (locals.profile_user_id) { %> - <% if (locals.session_user_id) { %> - <% if (session_user_id == profile_user_id) { %> -

My timeline

- <% } else { %> -

<%= profile_username %>'s timeline

- <% } %> - <% } %> -<% } else { %> - <% if (locals.myFeed) { %> -

My feed

- <% } else { %> -

Public timeline

- <% } %> -<% } %> - -<% if (locals.session_user_id) { %> - <% if (locals.profile_user_id) { %> -
- <% if (session_user_id == profile_user_id) { %> - This is you! - <% } else if (followed) { %> - You are currently following this user. - Unfollow user - <% } else { %> - You are not yet following this user. - Follow user - <% } %> - <% } else { %> -
-

What's on your mind <%= session_username %>?

-
-

-

-
- <% } %> -<% } %> - - - -<%- include('../partials/footer.ejs'); %> \ No newline at end of file diff --git a/webapp/views/partials/footer.ejs b/webapp/views/partials/footer.ejs deleted file mode 100644 index 0b4316a..0000000 --- a/webapp/views/partials/footer.ejs +++ /dev/null @@ -1,5 +0,0 @@ -
- - \ No newline at end of file diff --git a/webapp/views/partials/header.ejs b/webapp/views/partials/header.ejs deleted file mode 100644 index cc9c5c0..0000000 --- a/webapp/views/partials/header.ejs +++ /dev/null @@ -1,44 +0,0 @@ - - - - - <% if (locals.profile_user_id) { %> - <% if (locals.session_user_id) { %> - <% if (session_user_id == profile_user_id) { %> - My timeline | MiniTwit - <% } else { %> - <%= profile_username %>'s timeline | MiniTwit - <% } %> - <% } %> - <% } else { %> - <% if (locals.myFeed) { %> - My feed | MiniTwit - <% } else { %> - Public timeline | MiniTwit - <% } %> - - <% } %> - - - -
-

MiniTwit

- - <% if (locals.flashes) { %> - - <% } %> -
\ No newline at end of file