//node js create api server without any dependency github autopilot // must use https and http module // https module is used for https server // http module is used for http server // https module is used for https server // npm install sqlite3 // sudo node index-local.js // sudo nodemon index-local.js // sudo nodemon index-local.js npm install nodemon -g /** * database layout * post - title, content, date, user_id, tag, url * user - name, color, background color, font size, font family, font weight, font style, data theme * comment on post - post_id, user_id, comment, date * tag - post_title, post_content, post_date, post_user_id, post_tag, post_comment * tag/post_title/comments */ const http = require('http'); const https = require('https'); const fs = require('fs'); const url = require('url'); const StringDecoder = require('string_decoder').StringDecoder; const crypto = require('crypto'); const console = require('console'); //const hostname = '0.0.0.0'; const hostname = '127.0.0.1'; //const port = 443; const port = 8000; const domain ='xxxxxxxxxxxxxxx' const MAX_CHARACTERS = 2048; //read the key and certificate /**const options = { key: fs.readFileSync('/home/xxxxxxxxxx/private_key.key'), cert: fs.readFileSync('/home/xxxxxxxx/ssl_certificate.cer') };**/ const user_file_path = './user.json'; const post_file_path = './post.json'; const tag_file_path = './tag.json'; const comment_file_path = './comment.json'; //check if the user.json file does not exist function isValidHttpsUrl(string) { let url; try { url = new URL(string); } catch (_) { return false; } return url.protocol === "https:"; } // Redirect from http port 80 to https /**http.createServer(function (req, res) { res.writeHead(301, { "Location": "https://www." + domain }); res.end(); }).listen(80); **/ //create vanila http server that handles buffer overflow and handles routes using regexp //create https server ssl tsl http.createServer(function (req, res) { //https.createServer(options, function (req, res) { if (!fs.existsSync(user_file_path)) { //file does not exist //create user.json file before starting the server fs.writeFileSync(user_file_path, JSON.stringify([]), 'utf8'); } //check if the user.json file does not exist if (!fs.existsSync(post_file_path)) { //file does not exist //create user.json file fs.writeFileSync(post_file_path, JSON.stringify([]), 'utf8'); } if(!fs.existsSync(tag_file_path)){ fs.writeFileSync(tag_file_path, JSON.stringify([]), 'utf8'); } if(!fs.existsSync(comment_file_path)){ fs.writeFileSync(comment_file_path, JSON.stringify([]), 'utf8'); } let buffer = ''; const decoder = new StringDecoder('utf-8'); req.on('data', function(data) { buffer += decoder.write(data); //check if the buffer is too large if (buffer.length > MAX_CHARACTERS) { // FLOOD ATTACK OR FAULTY CLIENT, NUKE REQUEST req.destroy(); } }); req.on('end', function() { buffer += decoder.end(); //get ip address of the client let ip_address = req.headers['x-forwarded-for'] || req.socket.remoteAddress; console.log(ip_address ); if(ip_address == '' || ip_address == null || ip_address == undefined){ ip_address = 'guest'; } //read the user.json file let users = JSON.parse(fs.readFileSync(user_file_path, 'utf8')); let user_id = -1; //check if the user is the json file using a loop for(let i = 0; i < users.length; i++){ //if the user is in the json file then get the user info from the database if(users[i]['ip_address'] == ip_address){ //get user info from the database using ip address user_id = users[i]['id']; } } //if the user id is not in the json file, then add the user to the json file starting at 0 if(user_id == -1){ users.push({'ip_address':ip_address, 'id': users.length, 'hash': crypto.createHash('sha256').update(ip_address).digest('hex'), 'date': new Date().toISOString().slice(0, 19).replace('T', ' '), 'post_count': 0, 'background_color': '#ffffff', 'text_color': '#000000', 'font_size': '12px', 'font_family': 'Arial', 'font_weight': 'normal', 'font_style': 'normal', 'data_theme': 'dark' }); //write the user.json file fs.writeFileSync(user_file_path, JSON.stringify(users), 'utf8'); //redirect to home page so will get user id again user_id = users.length; res.writeHead(301, { "Location": "/" }); res.end('first time user'); } const parsedUrl = url.parse(req.url, true); const path = parsedUrl.pathname; if(path == '/robots.txt'){ res.writeHead(200, {'Content-Type': 'text/plain'}); //allow all robots res.end('User-agent: *\nAllow: /'); } else if(path == '/favicon.ico'){ fs.readFile('./favicon.ico', function(err, data) { if(err) { console.log(err); res.writeHead(404, {'Content-Type': 'text/html'}); return res.end("404 Not Found"); } else { res.writeHead(200, {'Content-Type': 'image/x-icon'} ); res.end(data); } }); } else if(path == '/api/user/profile'){ //get the user profile let user_profile = users[user_id]; //send the user profile to the client res.writeHead(200, {'Content-Type': 'application/json'}); res.end(JSON.stringify(user_profile)); } else if(path == '/api/user/profile/update'){ //get json data from the request let json_data = JSON.parse(buffer); //get the user profile let user_profile = users[user_id]; //update the user profile user_profile['background_color'] = json_data['background_color']; user_profile['text_color'] = json_data['text_color']; user_profile['font_size'] = json_data['font_size']; user_profile['font_family'] = json_data['font_family']; user_profile['font_weight'] = json_data['font_weight']; user_profile['font_style'] = json_data['font_style']; user_profile['data_theme'] = json_data['data_theme']; //write the user.json file fs.writeFileSync(user_file_path, JSON.stringify(users), 'utf8'); //send the user profile to the client res.writeHead(200, {'Content-Type': 'application/json'}); res.end(JSON.stringify(user_profile)); } else if(path == '/api/user/update/data_theme'){ //get json data from the request let json_data = JSON.parse(buffer); //get the user profile let user_profile = users[user_id]; //update the user profile user_profile['data_theme'] = json_data['data_theme']; //write the user.json file fs.writeFileSync(user_file_path, JSON.stringify(users), 'utf8'); //send the user profile to the client res.writeHead(200, {'Content-Type': 'application/json'}); res.end(JSON.stringify(user_profile)); } else if(path == '/api/post/add') { //get json data from the request let json_data = JSON.parse(buffer); //get the post title let post_title = json_data['post_title']; let post_background_color = json_data['post_background_color']; let post_text_color = json_data['post_text_color']; //get the post content let post_content = json_data['post_content']; //get the post url let post_url = json_data['post_url']; //check if post url is valid if(!isValidHttpsUrl(post_url)){ post_url=''; } //store the post in the json database let posts = JSON.parse(fs.readFileSync(post_file_path, 'utf8')); //add the post to the json file posts.push({'post_title':post_title, 'post_content':post_content, 'post_url':post_url, 'post_id': posts.length, 'post_background_color': post_background_color, 'post_text_color': post_text_color, }); //write the post.json file fs.writeFileSync(post_file_path, JSON.stringify(posts), 'utf8'); //respond with success message json res.writeHead(200, {'Content-Type': 'application/json'}); res.end(JSON.stringify({'success':true,'message':'post added'})); } else if(path == '/api/post/update') { //get json data from the request let json_data = JSON.parse(buffer); //get the post title let post_title = json_data['post_title']; let post_background_color = json_data['post_background_color']; let post_text_color = json_data['post_text_color']; //get the post content let post_content = json_data['post_content']; //get the post url let post_url = json_data['post_url']; //check if post url is valid if(!isValidHttpsUrl(post_url)){ post_url=''; } //get the post id let post_id = json_data['post_id']; //update the post in the json database let posts = JSON.parse(fs.readFileSync(post_file_path, 'utf8')); //loop through the posts to find the post id, alternatively we can keep all indexes as is and reference by the id, never deleting let index_id = -1; for(let i = 0; i < posts.length; i++){ //if the post id is found if(posts[i]['post_id'] == post_id){ index_id = i; break; } } if(index_id != -1){ //update the post posts[index_id]['post_title'] = post_title; posts[index_id]['post_content'] = post_content; posts[index_id]['post_url'] = post_url; posts[index_id]['post_background_color'] = post_background_color; posts[index_id]['post_text_color'] = post_text_color; //write the post.json file fs.writeFileSync(post_file_path, JSON.stringify(posts), 'utf8'); //respond with success message json res.writeHead(200, {'Content-Type': 'application/json'}); res.end(JSON.stringify({'success':true,'message':'post updated'})); }else{ //respond with error message json res.writeHead(200, {'Content-Type': 'application/json'}); res.end(JSON.stringify({'success':false,'message':'post not found'})); } } else if(path.match(/^\/[A-Za-z0-9_-]+?$/)){ //get the post title, watch out if tag title is api let tag_title = path.split('/')[1]; if(tag_title == 'api'){ //redirect to home page if api is the tag title res.writeHead(301, { "Location": "/" }); res.end('api'); return; } //check if the tag title exists let tags = JSON.parse(fs.readFileSync(tag_file_path, 'utf8')); //loop through the posts to find the post id let tag_id = -1; for(let i = 0; i < tags.length; i++){ //if the tag title is not found if(tags[i]['tag_title'] == tag_title){ tag_id = tags[i]['tag_id']; break; } } //if the tag id is not found if(tag_id == -1){ //redirect to home page res.writeHead(301, { "Location": "/" }); res.end('post not found'); return; } //show the first 20 posts with the tag date desc let posts = JSON.parse(fs.readFileSync(tag_file_path, 'utf8')); let post_json = []; for(let i = posts.length - 1; i >= 0; i--){ //if the tag id is found in the post , add it to the post_json if(posts[i]['post_tag_id'] == tag_id){ post_json.push(posts[i]); } //if the post_json is 20 then break if(post_json.length == 20){ break; } } res.writeHead(200, {'Content-Type': 'text/json'}); res.end(JSON.stringify(post_json)); } else if(path.match(/^\/[A-Za-z0-9_-]+?\/[A-Za-z0-9_-]+?$/)){ //get the tag title and post title /tag_title/post_title let tag_title = path.split('/')[1]; if(tag_title == 'api'){ //redirect to home page res.writeHead(301, { "Location": "/" }); res.end('api'); return; } //check if the tag title exists let tags = JSON.parse(fs.readFileSync(tag_file_path, 'utf8')); //loop through the tags to find the tag id let tag_id = -1; for(let i = 0; i < tags.length; i++){ //if the tag title is not found if(tags[i]['tag_title'] == tag_title){ tag_id = tags[i]['tag_id']; break; } } //if the tag id is not found if(tag_id == -1){ //redirect to home page res.writeHead(301, { "Location": "/" }); res.end('tag not found'); return; } //get the post_title let post_title = path.split('/')[2]; let post_id = -1; //loop through the posts to find the post id let posts = JSON.parse(fs.readFileSync(post_file_path, 'utf8')); for(let i = 0; i < posts.length; i++){ //if the post title is not found if(posts[i]['post_title'] == post_title){ post_id = posts[i]['post_id']; break; } } //if the post id is not found if(post_id == -1){ //redirect to home page res.writeHead(301, { "Location": "/" }); res.end('post not found'); return; } else { //get the last 20 comments with the post id let comments = JSON.parse(fs.readFileSync(comment_file_path, 'utf8')); let comment_json = []; for(let i = 0; i < comments.length; i++){ //if the post id is found in the comment , add it to the comment_json if(comments[i]['comment_post_id'] == post_id){ comment_json.push(comments[i]); } //if the comment_json is 20 then break if(comment_json.length == 20){ break; } } res.writeHead(200, {'Content-Type': 'text/json'}); res.end(JSON.stringify(comment_json)); } } else { //if the path is empty then redirect to the home page res.writeHead(200, {"content-type":"text/html"}); //get file content as the template fs.readFile('index.html', function(err, data) { if (err) { console.log(err); }else{ res.writeHead(200, {"content-type":"text/html"}); res.end(data); } }); } }); //end of req.on end function }).listen(port, hostname);