clean redundant api endpoints

This commit is contained in:
MuslemRahimi 2024-11-18 22:02:10 +01:00
parent 4823333017
commit 82e574955e
27 changed files with 2 additions and 1841 deletions

View File

@ -1,24 +0,0 @@
// Declare a route
module.exports = function (fastify, opts, done) {
const pb = opts.pb;
fastify.post('/all-strategies', async (request, reply) => {
const data = request.body;
const userId = data?.userId;
let output;
try {
output = await pb.collection("stockscreener").getFullList({
filter: `user="${userId}"`,
});
}
catch(e) {
output = [];
}
reply.send({ items: output })
});
done();
};

View File

@ -7,16 +7,10 @@ const cors = require("@fastify/cors");
require("dotenv").config({ path: "../app/.env" }); require("dotenv").config({ path: "../app/.env" });
const fmpAPIKey = process.env.FMP_API_KEY; const fmpAPIKey = process.env.FMP_API_KEY;
//const mixpanelAPIKey = process.env.MIXPANEL_API_KEY; //const mixpanelAPIKey = process.env.MIXPANEL_API_KEY;
const twitchAPIKey = process.env.TWITCH_API_KEY;
const twitchSecretKey = process.env.TWITCH_SECRET_KEY;
//const Mixpanel = require('mixpanel'); //const Mixpanel = require('mixpanel');
//const UAParser = require('ua-parser-js'); //const UAParser = require('ua-parser-js');
const got = require("got"); //Only version npm i got@11.8.3 works with ESM
const cheerio = require("cheerio");
const sharp = require("sharp");
const axios = require("axios");
const fs = require("fs"); const fs = require("fs");
const path = require("path"); const path = require("path");
//const pino = require('pino'); //const pino = require('pino');
@ -60,37 +54,9 @@ const corsMiddleware = (request, reply, done) => {
}; };
fastify.addHook("onRequest", corsMiddleware); fastify.addHook("onRequest", corsMiddleware);
//fastify.register(require('./mixpanel/server'), { mixpanel, UAParser });
fastify.register(require("./get-post/server"), { pb });
fastify.register(require("./get-one-post/server"), { pb });
fastify.register(require("./get-portfolio-data/server"), { pb });
fastify.register(require("./create-portfolio/server"), { pb, serialize });
fastify.register(require("./buy-stock/server"), { pb });
fastify.register(require("./sell-stock/server"), { pb });
fastify.register(require("./create-post-link/server"), { got, cheerio, sharp });
fastify.register(require("./create-post-image/server"), { sharp });
fastify.register(require("./delete-comment/server"), { pb });
fastify.register(require("./delete-post/server"), { pb });
fastify.register(require("./leaderboard/server"), { pb });
fastify.register(require("./edit-name-watchlist/server"), { pb });
fastify.register(require("./create-strategy/server"), { pb });
fastify.register(require("./delete-strategy/server"), { pb });
fastify.register(require("./all-strategies/server"), { pb });
fastify.register(require("./save-strategy/server"), { pb });
fastify.register(require("./get-strategy/server"), { pb });
fastify.register(require("./get-twitch-status/server"), {
axios,
twitchAPIKey,
twitchSecretKey,
});
fastify.register(require("./get-portfolio/server"), { pb });
fastify.register(require("./create-price-alert/server"), { pb });
fastify.register(require("./get-price-alert/server"), { pb, fs, path }); fastify.register(require("./get-price-alert/server"), { pb, fs, path });
fastify.register(require("./delete-price-alert/server"), { pb });
fastify.register(require("./upvote/server"), { pb });
fastify.register(require("./downvote/server"), { pb });
fastify.register(require("./upvote-comment/server"), { pb });
fastify.register(require("./downvote-comment/server"), { pb });
//fastify.register(require('./create-comment/server'), { pb }); //fastify.register(require('./create-comment/server'), { pb });

View File

@ -1,140 +0,0 @@
// Declare a route
function processHoldingsList(holdings) {
const stockGroups = {};
for (const stock of holdings) {
if (stock.symbol in stockGroups) {
stockGroups[stock.symbol].totalCost += stock.boughtPrice * stock.numberOfShares;
stockGroups[stock.symbol].totalShares += stock.numberOfShares;
} else {
stockGroups[stock.symbol] = {
totalCost: stock.boughtPrice * stock.numberOfShares,
totalShares: stock.numberOfShares,
name: stock.name,
assetType: stock.assetType,
currentPrice: stock.currentPrice,
dailyChange: stock.dailyChange,
sinceBoughtChange: stock.sinceBoughtChange,
};
}
// Update dailyChange automatically
stockGroups[stock.symbol].dailyChange = stock.dailyChange;
stockGroups[stock.symbol].sinceBoughtChange = stock.sinceBoughtChange;
stockGroups[stock.symbol].currentPrice = stock.currentPrice;
}
const updatedHoldings = [];
for (const symbol in stockGroups) {
const { totalCost, totalShares, name, assetType, currentPrice, dailyChange, sinceBoughtChange } = stockGroups[symbol];
const finalBoughtPrice = totalCost / totalShares;
const updatedStock = {
symbol,
name,
assetType,
boughtPrice: finalBoughtPrice,
currentPrice,
dailyChange,
sinceBoughtChange: Number(((currentPrice/finalBoughtPrice -1) * 100)?.toFixed(2)),
numberOfShares: totalShares,
};
updatedHoldings.push(updatedStock);
}
return updatedHoldings;
}
module.exports = function (fastify, opts, done) {
const pb = opts.pb;
fastify.post('/buy-stock', async (request, reply) => {
const holidays = ['2024-01-01', '2024-01-15','2024-02-19','2024-03-29','2024-05-27','2024-06-19','2024-07-04','2024-09-02','2024-11-28','2024-12-25'];
const currentDate = new Date().toISOString().split('T')[0];
// Get the current time in the ET time zone
const etTimeZone = 'America/New_York';
const currentTime = new Date().toLocaleString('en-US', { timeZone: etTimeZone });
// Determine if the NYSE is currently open or closed
const currentHour = new Date(currentTime).getHours();
const isWeekend = new Date(currentTime).getDay() === 6 || new Date(currentTime).getDay() === 0;
const isBeforeMarketOpen = currentHour < 9 || (currentHour === 9 && new Date(currentTime).getMinutes() < 30);
const isAfterMarketClose = currentHour >= 16;
const isStockMarketOpen = !(isWeekend || isBeforeMarketOpen || isAfterMarketClose || holidays?.includes(currentDate));
let output;
if (isStockMarketOpen === true) {
const data = request.body;
const currentDate = new Date();
const year = currentDate.getFullYear();
const month = String(currentDate.getMonth() + 1).padStart(2, '0'); // Month is zero-based
const day = '01';
const formattedDate = `${year}-${month}-${day}`; // Output: "yyyy-mm-01"
const userId = data?.userId;
let newHolding = {'symbol': data['symbol'],
'name': data['name'],
'assetType': data['assetType'],
'numberOfShares': data['numberOfShares'],
'boughtPrice': data['boughtPrice'],
'currentPrice': data['boughtPrice'],
'dailyChange': 0,
'sinceBoughtChange': 0 }
let currentPortfolio = await pb.collection("portfolios").getList(1, 500, {
filter: `user="${userId}" && created >="${formattedDate}" `,
});
currentPortfolio = currentPortfolio?.items[0];
let holdings = currentPortfolio?.holdings || [];
let tradingHistory = currentPortfolio?.tradingHistory || [];
let availableCash = currentPortfolio?.availableCash - data['estimatedTotal'];
holdings.push(newHolding)
const updatedHoldings = processHoldingsList(holdings)
tradingHistory.push({'symbol': data['symbol'],
'name': data['name'],
'assetType': data['assetType'],
'numberOfShares': data['numberOfShares'],
'price': Number(data['boughtPrice']),
'type': 'buy',
'date': new Date()});
try {
await pb.collection("portfolios").update(currentPortfolio?.id, {
"holdings": updatedHoldings,
"availableCash": availableCash,
"tradingHistory": tradingHistory,
})
output = 'success';
} catch (err) {
output = 'failure'
}
}
else {
output = 'marketClosed'
}
reply.send({ items: output })
})
done();
};

View File

@ -1,35 +0,0 @@
// Declare a route
module.exports = function (fastify, opts, done) {
const pb = opts.pb;
const serialize = opts.serialize;
fastify.post('/create-portfolio', async (request, reply) => {
const data = request.body;
const formData = {'user': data?.userId, 'accountValue': 100000,
'availableCash': 100000, 'overallReturn': 0, 'rank': 0,
'holdings': JSON.stringify([]), 'tradingHistory': '[]',
'metrics': JSON.stringify({'alpha': 'n/a',
'beta': 'n/a',
'maxDrawdown': 0
})
}
let output = 'failure';
try {
await pb.collection('portfolios').create(serialize(formData));
output = 'success';
}
catch(e) {
console.log(e)
}
reply.send({ message: output })
});
done();
};

View File

@ -1,39 +0,0 @@
// Declare a route
module.exports = function (fastify, opts, done) {
const sharp = opts.sharp;
fastify.post('/create-post-image', async (request, reply) => {
try {
const data = request.body;
let imageBufferArray = data?.imageBufferArray;
if (imageBufferArray) {
// Resize and optimize the image
const optimizedImageBuffer = await sharp(imageBufferArray)
.resize({
width: 800,
height: 1000,
fit: sharp.fit.inside,
withoutEnlargement: true,
})
.jpeg({ quality: 80 })
.toBuffer();
// Send the optimized image in the response
reply.send({
image: optimizedImageBuffer,
});
} else {
reply.status(400).send({ error: 'Image data is missing.' });
}
} catch (error) {
console.error('Error processing image:', error);
reply.status(500).send({ error: 'Internal Server Error' });
}
});
done();
};

View File

@ -1,77 +0,0 @@
// Declare a route
module.exports = function (fastify, opts, done) {
const got = opts.got;
const cheerio = opts.cheerio;
const sharp = opts.sharp;
fastify.post('/create-post-link', async (request, reply) => {
const data = request.body;
const url = data?.link;
let description;
let imageBuffer;
try {
const response = await got(url, {
headers: {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3',
},
responseType: 'buffer',
});
const $ = cheerio.load(response.body);
description = $('head meta[property="og:description"]').attr('content');
let image = $('head meta[property="og:image"]').attr('content');
if (!image) {
let largestSize = 0;
let largestImage = '';
$('img').each(async function () {
if ($(this).attr('src') && $(this).attr('src').match(/\.(webp|jpg|jpeg|png|gif)$/)) {
try {
imageBuffer = await got($(this).attr('src'), {
headers: {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3',
},
responseType: 'buffer',
}).then((response) => response.body);
const metadata = await sharp(imageBuffer).metadata();
const imageSize = metadata.width * metadata.height;
if (imageSize > largestSize) {
largestSize = imageSize;
largestImage = $(this).attr('src');
}
} catch (error) {
console.error('Error getting image:', error);
}
}
});
image = largestImage;
}
imageBuffer = await got(image, {
headers: {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3',
},
responseType: 'buffer',
}).then((response) => response.body);
} catch (e) {
console.error(e);
}
// Check if imageBlob is not null before sending it in the response
reply.send({
description: description,
image: imageBuffer,
})
});
done();
};

View File

@ -1,41 +0,0 @@
// Declare a route
module.exports = function (fastify, opts, done) {
const pb = opts.pb;
fastify.post('/create-price-alert', async (request, reply) => {
const data = request.body;
let output;
let newAlert = {
'user': data['userId'],
'symbol': data['symbol']?.toUpperCase(),
'name': data['name'],
'assetType': data['assetType']?.toLowerCase(),
'targetPrice': Number(data['targetPrice']),
'condition': data['condition']?.toLowerCase(),
'priceWhenCreated': Number(data['priceWhenCreated']),
'triggered': false,
}
try {
await pb.collection("priceAlert")?.create(newAlert)
output = 'success';
} catch (err) {
output = 'failure'
}
reply.send({ items: output })
})
done();
};

View File

@ -1,21 +0,0 @@
// Declare a route
module.exports = function (fastify, opts, done) {
const pb = opts.pb;
fastify.post('/create-strategy', async (request, reply) => {
const data = request.body;
let output;
try {
output = await pb.collection("stockscreener").create(data)
}
catch(e) {
output = {};
}
reply.send({ items: output })
});
done();
};

View File

@ -1,48 +0,0 @@
// Declare a route
module.exports = function (fastify, opts, done) {
const pb = opts.pb;
fastify.post('/delete-comment', async (request, reply) => {
const data = request.body;
let output;
const userId = data?.userId;
const commentUserId = data?.commentUser;
const commentId = data.commentId
//Each delete gives the user -1 Karma points
let checkModerator = await pb.collection('moderators').getList(1, 50)
//OP and moderators have the right to delete comments
if (commentUserId === userId || checkModerator.items.some((item) => item.user === userId))
{
try {
await pb.collection('comments').delete(commentId);
await pb.collection("users").update(commentUserId, {
"karma-": 1,
})
output = 'success';
} catch (err) {
output = 'failure'
console.log(err)
}
}
else {
output = 'failure';
}
reply.send({ message: output })
});
done();
};

View File

@ -1,40 +0,0 @@
// Declare a route
module.exports = function (fastify, opts, done) {
const pb = opts.pb;
fastify.post('/delete-post', async (request, reply) => {
const data = request.body;
const postId = data?.postId;
const userId = data?.userId;
const moderator = 'db5s41oatgoeh0q' //moderators can always delete post
let output;
try {
if(moderator === userId)
{
await pb.collection('posts').delete(postId);
output = 'success';
}
else {
const res = await pb.collection('posts').getOne(postId);
if (res?.user === userId)
{
await pb.collection('posts').delete(postId);
output = 'success';
}
else {
output = 'failure';
}
}
}
catch(e) {
console.log(e)
output = 'failure';
}
reply.send({ items: output })
});
done();
};

View File

@ -1,26 +0,0 @@
// Declare a route
module.exports = function (fastify, opts, done) {
const pb = opts.pb;
fastify.post('/delete-price-alert', async (request, reply) => {
const data = request.body;
const priceAlertIdList = data?.priceAlertIdList;
let output;
try {
for (const item of priceAlertIdList) {
await pb.collection("priceAlert")?.delete(item)
}
output = 'success';
}
catch(e) {
//console.log(e)
output = 'failure';
}
reply.send({ items: output })
});
done();
};

View File

@ -1,22 +0,0 @@
// Declare a route
module.exports = function (fastify, opts, done) {
const pb = opts.pb;
fastify.post('/delete-strategy', async (request, reply) => {
const data = request.body;
let output;
try {
await pb.collection("stockscreener").delete(data['strategyId'])
output = 'success';
}
catch(e) {
output = 'failure';
}
reply.send({ items: output })
});
done();
};

View File

@ -1,187 +0,0 @@
// Declare a route
module.exports = function (fastify, opts, done) {
const pb = opts.pb;
fastify.post('/downvote-comment', async (request, reply) => {
const data = request.body;
const commentId = data?.commentId;
const userId = data?.userId;
let output = 'failure';
console.log(data)
try {
let doesUserExist = await pb.collection("alreadyVoted").getList(1, 50, {
filter: `user="${userId}"` && `comment="${commentId}"`,
})
doesUserExist = doesUserExist?.items?.find(item => item?.user === userId && item?.comment === commentId);
//console.log('Does it exist yet: ', doesUserExist)
const votedId = doesUserExist?.id;
let currentVote = doesUserExist?.type;
const opPost = await pb.collection('comments').getOne(commentId)
console.log('currentVote: ', currentVote)
/*
console.log('commentId: ', commentId)
console.log('votedId: ', votedId)
console.log('currentVote: ', currentVote)
console.log('user: ', user);
*/
//If user has no history with this post create it
if( !currentVote || votedId === 'undefined')
{
console.log('created')
let formDataAlreadyVoted = new FormData();
formDataAlreadyVoted.append('post', commentId);
formDataAlreadyVoted.append('user', userId);
formDataAlreadyVoted.append('notifyType', 'downvote');
await pb.collection('alreadyVoted').create(formDataAlreadyVoted);
}
if (currentVote === 'upvote')
{
await pb.collection("comments").update(commentId, {
"downvote+": 1,
});
await pb.collection("comments").update(commentId, {
"upvote-": 1,
});
await pb.collection("alreadyVoted").update(votedId, {
"type": 'downvote',
});
//if user is the opPost then it should only subtract -1 once
let opPost = await pb.collection('comment').getOne(commentId)
if (opPost.user === userId)
{
//Punishment: Find user of post and subtract -1 karma points
await pb.collection("users").update(opPost.user, {
"karma-": 2,
})
}
else
{
//Punishment: Find user of post and subtract -1 karma points
await pb.collection("users").update(opPost.user, {
"karma-": 2,
})
//Punishment: User who downvotes post also loose -1 karma points
await pb.collection("users").update(userId, {
"karma-": 2,
})
}
}
else if (currentVote === 'neutral' || !currentVote)
{
if (opPost.user === userId)
{
//Punishment: Find user of post and subtract -1 karma points
await pb.collection("users").update(opPost.user, {
"karma-": 1,
})
}
else
{
//Punishment: Find user of post and subtract -1 karma points
await pb.collection("users").update(opPost.user, {
"karma-": 1,
})
//Punishment: User who downvotes post also loose -1 karma points
await pb.collection("users").update(userId, {
"karma-": 1,
})
}
await pb.collection("comments").update(commentId, {
"downvote+": 1,
});
await pb.collection("alreadyVoted").update(votedId, {
"type": 'downvote',
});
}
else
{
await pb.collection("comments").update(commentId, {
"downvote-": 1,
});
await pb.collection("alreadyVoted").update(votedId, {
"type": 'neutral',
});
let opPost = await pb.collection('comment').getOne(commentId)
//if user is the opPost then it should only add +1 once
if (opPost.user === userId)
{
//Reset Punishment: Find user of post and add +1 karma points back
let opPost = await pb.collection('comment').getOne(commentId)
await pb.collection("users").update(opPost.user, {
"karma+": 1,
})
}
else
{
//Reset Punishment: Find user of post and add +1 karma points back
let opPost = await pb.collection('comment').getOne(commentId)
await pb.collection("users").update(opPost.user, {
"karma+": 1,
})
//Reset Punishment: User who removes downvote gets back +1 karma points
await pb.collection("users").update(userId, {
"karma+": 1,
})
}
}
}
catch(e) {
console.log(e)
}
reply.send({ items: output })
});
done();
};

View File

@ -1,186 +0,0 @@
// Declare a route
module.exports = function (fastify, opts, done) {
const pb = opts.pb;
fastify.post('/downvote', async (request, reply) => {
const data = request.body;
const postId = data?.postId;
const userId = data?.userId;
let output = 'failure';
try {
let doesUserExist = await pb.collection("alreadyVoted").getList(1, 50, {
filter: `user="${userId}"` && `post="${postId}"`,
})
doesUserExist = doesUserExist.items.find(item => item.user === userId && item.post === postId);
//console.log('Does it exist yet: ', doesUserExist)
const votedId = doesUserExist?.id;
let currentVote = doesUserExist?.type;
const opPost = await pb.collection('posts').getOne(postId)
//console.log('currentVote: ', currentVote)
/*
console.log('postId: ', postId)
console.log('votedId: ', votedId)
console.log('currentVote: ', currentVote)
console.log('user: ', user);
*/
//If user has no history with this post create it
if( !currentVote || votedId === 'undefined')
{
let formDataAlreadyVoted = new FormData();
formDataAlreadyVoted.append('post', postId);
formDataAlreadyVoted.append('user', userId);
formDataAlreadyVoted.append('type', 'downvote');
await pb.collection('alreadyVoted').create(formDataAlreadyVoted);
}
if (currentVote === 'upvote')
{
await pb.collection("posts").update(postId, {
"downvote+": 1,
});
await pb.collection("posts").update(postId, {
"upvote-": 1,
});
await pb.collection("alreadyVoted").update(votedId, {
"type": 'downvote',
});
//if user is the opPost then it should only subtract -1 once
let opPost = await pb.collection('posts').getOne(postId)
if (opPost.user === userId)
{
//Punishment: Find user of post and subtract -1 karma points
await pb.collection("users").update(opPost.user, {
"karma-": 2,
})
}
else
{
//Punishment: Find user of post and subtract -1 karma points
await pb.collection("users").update(opPost.user, {
"karma-": 2,
})
//Punishment: User who downvotes post also loose -1 karma points
await pb.collection("users").update(userId, {
"karma-": 2,
})
}
}
else if (currentVote === 'neutral' || !currentVote)
{
if (opPost.user === userId)
{
//Punishment: Find user of post and subtract -1 karma points
await pb.collection("users").update(opPost.user, {
"karma-": 1,
})
}
else
{
//Punishment: Find user of post and subtract -1 karma points
await pb.collection("users").update(opPost.user, {
"karma-": 1,
})
//Punishment: User who downvotes post also loose -1 karma points
await pb.collection("users").update(userId, {
"karma-": 1,
})
}
await pb.collection("posts").update(postId, {
"downvote+": 1,
});
await pb.collection("alreadyVoted").update(votedId, {
"type": 'downvote',
});
}
else
{
await pb.collection("posts").update(postId, {
"downvote-": 1,
});
await pb.collection("alreadyVoted").update(votedId, {
"type": 'neutral',
});
let opPost = await pb.collection('posts').getOne(postId)
//if user is the opPost then it should only add +1 once
if (opPost.user === userId)
{
//Reset Punishment: Find user of post and add +1 karma points back
let opPost = await pb.collection('posts').getOne(postId)
await pb.collection("users").update(opPost.user, {
"karma+": 1,
})
}
else
{
//Reset Punishment: Find user of post and add +1 karma points back
let opPost = await pb.collection('posts').getOne(postId)
await pb.collection("users").update(opPost.user, {
"karma+": 1,
})
//Reset Punishment: User who removes downvote gets back +1 karma points
await pb.collection("users").update(userId, {
"karma+": 1,
})
}
}
}
catch(e) {
console.log(e)
}
reply.send({ items: output })
});
done();
};

View File

@ -1,29 +0,0 @@
// Declare a route
module.exports = function (fastify, opts, done) {
const pb = opts.pb;
fastify.post('/edit-name-watchlist', async (request, reply) => {
const data = request.body;
const watchListId = data?.watchListId;
const newTitle = data?.title;
let output;
try {
await pb.collection("watchlist").update(watchListId, {
'title': newTitle
})
output = 'success';
}
catch(e) {
//console.log(e)
output = 'failure';
}
reply.send({ items: output })
});
done();
};

View File

@ -1,22 +0,0 @@
// Declare a route
module.exports = function (fastify, opts, done) {
const pb = opts.pb;
fastify.post('/get-one-post', async (request, reply) => {
const data = request.body;
const postId = data?.postId;
const output = await pb.collection('posts').getOne(postId, {
expand: 'user,alreadyVoted(post)',
fields: "*,expand.user,expand.alreadyVoted(post).user,expand.alreadyVoted(post).type"
});
reply.send({ items: output })
})
done();
};

View File

@ -1,30 +0,0 @@
// Declare a route
module.exports = function (fastify, opts, done) {
const pb = opts.pb;
fastify.post('/get-portfolio-data', async (request, reply) => {
const data = request.body;
const userId = data?.userId
const currentDate = new Date();
const year = currentDate.getFullYear();
const month = String(currentDate.getMonth() + 1).padStart(2, '0'); // Month is zero-based
const day = '01';
const formattedDate = `${year}-${month}-${day}`; // Output: "yyyy-mm-01"
//Get Portfolio of user for current month
const output = await pb.collection("portfolios").getList(1, 500, {
filter: `user="${userId}" && created >="${formattedDate}" `,
});
reply.send({ items: output.items })
});
done();
};

View File

@ -1,36 +0,0 @@
// Declare a route
module.exports = function (fastify, opts, done) {
const pb = opts.pb;
fastify.post('/get-portfolio', async (request, reply) => {
const data = request.body;
const userId = data?.userId;
// Get the current date
const currentMonth = new Date();
const nextMonth = new Date(currentMonth);
nextMonth.setDate(currentMonth.getDate() + 31); // Add a number of days to ensure next month
// Set the day to 1 to get the beginning of the current month
const beginningOfMonth = new Date(currentMonth);
beginningOfMonth.setDate(1);
const beginningOfNextMonth = new Date(nextMonth);
beginningOfNextMonth.setDate(1);
// Format it as a string if needed
const startDate = beginningOfMonth.toISOString().split('T')[0];
const endDate = beginningOfNextMonth.toISOString().split('T')[0];
//console.log('Start Date:', startDate);
//console.log('End Date:', endDate);
const output = await pb.collection("portfolios").getFullList(query_params = {"filter": `user="${userId}" && created >= "${startDate}" && created < "${endDate}"`})
reply.send({ items: output })
})
done();
};

View File

@ -1,171 +0,0 @@
// Optimized postHotness function
function postHotness(upvotes, created) {
let s = 0;
for (let i = 1; i <= upvotes; i++) {
if (i <= 3) {
s += 1;
} else if (i <= 6) {
s += 3;
} else if (i <= 10) {
s += 3;
} else if (i <= 20) {
s += 4;
} else if (i <= 40) {
s += 5;
} else {
s += 6;
}
}
const order = Math.log10(Math.max(Math.abs(s), 1));
let sign = 0;
if (s > 0) {
sign = 1;
} else if (s < 0) {
sign = -1;
}
const interval = 45000; // or 69000
const createdDate = new Date(created);
const seconds = createdDate.getTime() / 1000;
const hotness = order + (sign * seconds) / interval;
return Math.round(hotness * 10000000);
}
module.exports = function (fastify, opts, done) {
const pb = opts.pb;
fastify.post("/get-post", async (request, reply) => {
const data = request.body;
let filter;
const sort = data?.sortingPosts === "hot" ? "-upvote" : "-created";
let pinnedPost;
let posts;
try {
if (data?.seenPostId.length !== 0) {
filter = data?.seenPostId?.map((id) => `id!="${id}"`).join("&&");
//applies only for profile and user directory
if (data?.userId) {
filter += `&& user="${data?.userId}" && pinned=false`;
}
if (data?.filterTicker) {
filter += `&& tagline="${data?.filterTicker}" && pinned=false`;
}
if (data?.sortingPosts === "hot") {
//community page code space
// In case of sort === 'hot' show the most recent post up to 7 week by ranking them with the function postHotness
let endDate = new Date();
// Get the date one week earlier
let startDate = new Date();
startDate.setDate(endDate.getDate() - 180);
endDate.setDate(endDate.getDate() + 1);
// Format the dates as needed (e.g., "YYYY-MM-DD")
let endDateStr = endDate.toISOString().split("T")[0];
let startDateStr = startDate.toISOString().split("T")[0];
filter += `&& created >= "${startDateStr}" && created <= "${endDateStr}" && pinned = false`;
}
posts = (
await pb.collection("posts").getList(data?.startPage, 10, {
sort: "-created",
filter: filter,
expand: "user,comments(post),alreadyVoted(post)",
fields:
"*,expand.user,expand.comments(post), expand.alreadyVoted(post).user,expand.alreadyVoted(post).type",
})
)?.items;
if (data?.sortingPosts === "hot") {
// Add hotness property to each post
posts?.forEach((post) => {
post.hotness = postHotness(post?.upvote, post?.created);
});
posts?.sort((a, b) => b?.hotness - a?.hotness);
}
} else {
if (data?.userId) {
posts = (
await pb.collection("posts").getList(data?.startPage, 10, {
sort: sort,
filter: `user="${data?.userId}" && pinned=false`,
expand: `user,comments(post),alreadyVoted(post)`,
fields:
"*,expand.user,expand.comments(post), expand.alreadyVoted(post).user,expand.alreadyVoted(post).type",
})
)?.items;
} else if (data?.filterTicker) {
posts = await pb.collection("posts").getList(data?.startPage, 10, {
sort: sort,
filter: `tagline="${data?.filterTicker}" && pinned=false`,
expand: `user,comments(post),alreadyVoted(post)`,
fields:
"*,expand.user,expand.comments(post), expand.alreadyVoted(post).user,expand.alreadyVoted(post).type",
});
} else {
//community page code space
// In case of sort === 'hot' show the most recent post up to 7 week by ranking them with the function postHotness
if (data?.sortingPosts === "hot") {
let endDate = new Date();
// Get the date one week earlier
let startDate = new Date();
startDate.setDate(endDate.getDate() - 30);
endDate.setDate(endDate.getDate() + 1);
// Format the dates as needed (e.g., "YYYY-MM-DD")
let endDateStr = endDate.toISOString().split("T")[0];
let startDateStr = startDate.toISOString().split("T")[0];
filter = `created >= "${startDateStr}" && created <= "${endDateStr}" && pinned = false`;
} else {
filter = `pinned=false`;
}
posts = await pb.collection("posts").getList(1, 10, {
sort: "-created",
filter: filter,
expand: "user, comments(post), alreadyVoted(post)",
fields:
"*,expand.user,expand.comments(post), expand.alreadyVoted(post).user,expand.alreadyVoted(post).type",
});
posts = posts.items;
// Add hotness property to each post
if (data?.sortingPosts === "hot") {
posts?.forEach((post) => {
post.hotness = postHotness(post?.upvote, post?.created);
});
posts?.sort((a, b) => b?.hotness - a?.hotness);
}
pinnedPost = await pb.collection("posts").getFullList({
filter: `pinned=true`,
sort: "-created",
expand: `user,comments(post),alreadyVoted(post)`,
fields:
"*,expand.user,expand.comments(post), expand.alreadyVoted(post).user,expand.alreadyVoted(post).type",
});
for (let i = pinnedPost?.length - 1; i >= 0; i--) {
posts?.unshift(pinnedPost[i]);
}
}
}
} catch (e) {
//console.log(e)
posts = [];
}
reply.send({ items: posts });
});
done();
};

View File

@ -1,21 +0,0 @@
// Declare a route
module.exports = function (fastify, opts, done) {
const pb = opts.pb;
fastify.post('/get-strategy', async (request, reply) => {
const data = request.body;
let output;
try {
output = await pb.collection("stockscreener").getOne(data['strategyId'])
}
catch(e) {
output = {};
}
reply.send({ items: output })
});
done();
};

View File

@ -1,49 +0,0 @@
module.exports = function (fastify, opts, done) {
const axios = opts.axios;
const twitchAPIKey = opts.twitchAPIKey;
const twitchSecretKey = opts.twitchSecretKey;
fastify.get('/get-twitch-status', async (request, reply) => {
let twitchStatus = false;
const client_id = twitchAPIKey;
const client_secret = twitchSecretKey;
const streamer_name = 'stocknear';
try {
// Obtain an access token from Twitch
const tokenResponse = await axios.post('https://id.twitch.tv/oauth2/token', null, {
params: {
client_id,
client_secret,
grant_type: 'client_credentials',
},
});
const { access_token } = tokenResponse.data;
// Check if the stream is online
const streamResponse = await axios.get(
`https://api.twitch.tv/helix/streams?user_login=${streamer_name}`,
{
headers: {
'Client-ID': client_id,
Authorization: `Bearer ${access_token}`,
},
}
);
const streamData = streamResponse.data;
twitchStatus = streamData.data.length === 1;
} catch (e) {
console.error(e);
}
reply.send({ items: twitchStatus });
});
done();
};

View File

@ -1,31 +0,0 @@
// Declare a route
module.exports = function (fastify, opts, done) {
const pb = opts.pb;
fastify.post('/leaderboard', async (request, reply) => {
const data = request.body;
const startDate = data?.startDate;
const endDate = data?.endDate;
let output;
try {
output = await pb.collection("portfolios").getFullList({
filter: `created >= "${startDate}" && created < "${endDate}"`,
expand: 'user'
})
}
catch(e) {
//console.log(e)
output = []
}
reply.send({ items: output })
});
done();
};

View File

@ -1,72 +0,0 @@
// Declare a route
module.exports = function (fastify, opts, done) {
const mixpanel = opts.mixpanel;
const UAParser = opts.UAParser;
fastify.post('/mixpanel', async (request, reply) => {
const data = request.body;
const { browser, cpu, device, os } = UAParser(data.userAgent)
let options = {
path: data.path,
browser: browser.name,
browser_version: browser.version,
device: device.vendor,
cpu: cpu.architecture,
os: os.name,
}
if (data.type === 'trackPageError')
{
options.status = data.status;
options.message = data.message;
mixpanel.track('Error status', options);
console.log('Send error page data to mixpanel')
}
else if (data.type === 'trackPageVisit')
{
mixpanel.track('Page Visit', options);
}
else if (data.type === 'trackPageDuration')
{
options.time_spent = data.time;
mixpanel.track('Page Duration', options);
}
else if (data.type === 'trackAsset')
{
const options = {
symbol: data.symbol,
assetType: data.assetType,
}
mixpanel.track('asset', options);
}
else if (data.type === 'trackButton')
{
const options = {
name: data.name,
}
mixpanel.track('buttonClick', options);
}
reply.send({ message: 'success' })
})
done();
};

View File

@ -1,23 +0,0 @@
// Declare a route
module.exports = function (fastify, opts, done) {
const pb = opts.pb;
fastify.post('/save-strategy', async (request, reply) => {
const data = request.body;
let output;
try {
output = await pb.collection("stockscreener").update(data['strategyId'], {
'rules': data['rules']
})
}
catch(e) {
output = {};
}
reply.send({ items: output })
});
done();
};

View File

@ -1,157 +0,0 @@
function processHoldingsList(holdings, soldTicker) {
const stockGroups = {};
for (const stock of holdings) {
if (stock.symbol in stockGroups) {
stockGroups[stock.symbol].totalCost -= stock.boughtPrice * stock.numberOfShares;
stockGroups[stock.symbol].totalShares -= stock.numberOfShares;
} else {
stockGroups[stock.symbol] = {
totalCost: stock.boughtPrice * stock.numberOfShares,
totalShares: stock.numberOfShares,
name: stock.name,
assetType: stock.assetType,
currentPrice: stock.currentPrice,
dailyChange: stock.dailyChange,
sinceBoughtChange: stock.sinceBoughtChange,
};
}
if (stock.symbol === soldTicker) {
// Only update dailyChange and sinceBoughtChange for the sold ticker
stockGroups[stock.symbol].dailyChange = stock.dailyChange;
stockGroups[stock.symbol].sinceBoughtChange = stock.sinceBoughtChange;
stockGroups[stock.symbol].currentPrice = stock.currentPrice;
}
}
const updatedHoldings = [];
for (const symbol in stockGroups) {
const { totalCost, totalShares, name, assetType, currentPrice, dailyChange, sinceBoughtChange } = stockGroups[symbol];
let finalBoughtPrice;
if (totalShares !== 0) {
finalBoughtPrice = totalCost / totalShares;
} else {
// If totalShares is 0, set finalBoughtPrice to 0
finalBoughtPrice = 0;
}
const updatedStock = {
symbol,
name,
assetType,
boughtPrice: finalBoughtPrice,
currentPrice,
dailyChange,
sinceBoughtChange: Number(((currentPrice / finalBoughtPrice - 1) * 100)?.toFixed(2)),
numberOfShares: totalShares,
};
updatedHoldings.push(updatedStock);
}
return updatedHoldings;
}
// Declare a route
module.exports = function (fastify, opts, done) {
const pb = opts.pb;
fastify.post('/sell-stock', async (request, reply) => {
const holidays = ['2024-01-01', '2024-01-15','2024-02-19','2024-03-29','2024-05-27','2024-06-19','2024-07-04','2024-09-02','2024-11-28','2024-12-25'];
const currentDate = new Date().toISOString().split('T')[0];
// Get the current time in the ET time zone
const etTimeZone = 'America/New_York';
const currentTime = new Date().toLocaleString('en-US', { timeZone: etTimeZone });
// Determine if the NYSE is currently open or closed
const currentHour = new Date(currentTime).getHours();
const isWeekend = new Date(currentTime).getDay() === 6 || new Date(currentTime).getDay() === 0;
const isBeforeMarketOpen = currentHour < 9 || (currentHour === 9 && new Date(currentTime).getMinutes() < 30);
const isAfterMarketClose = currentHour >= 16;
const isStockMarketOpen = !(isWeekend || isBeforeMarketOpen || isAfterMarketClose || holidays?.includes(currentDate));
let output;
if (isStockMarketOpen === true) {
const data = request.body;
const currentDate = new Date();
const year = currentDate.getFullYear();
const month = String(currentDate.getMonth() + 1).padStart(2, '0'); // Month is zero-based
const day = '01';
const formattedDate = `${year}-${month}-${day}`; // Output: "yyyy-mm-01"
const { userId, symbol, name, numberOfShares, soldPrice } = data;
let currentPortfolio = await pb.collection("portfolios").getList(1, 500, {
filter: `user="${userId}" && created >="${formattedDate}" `,
});
currentPortfolio = currentPortfolio?.items[0];
let holdings = currentPortfolio?.holdings || [];
let tradingHistory = currentPortfolio?.tradingHistory || [];
let availableCash = currentPortfolio?.availableCash || 0;
// Find the stock in the holdings list
const stockIndex = holdings.findIndex((stock) => stock.symbol === symbol);
if(stockIndex !== -1)
{
const soldValue = numberOfShares * soldPrice;
// Reduce the number of shares from the existing holding
holdings[stockIndex].numberOfShares -= numberOfShares;
if (holdings[stockIndex].numberOfShares <= 0) {
// If all shares are sold, remove the stock from the holdings list
holdings.splice(stockIndex, 1);
}
// Add the sold value to the available cash
availableCash += soldValue;
// Recalculate the updated holdings list
const updatedHoldings = processHoldingsList(holdings);
tradingHistory.push({'symbol': symbol,
'name': name,
'assetType': data['assetType'],
'numberOfShares': numberOfShares,
'price': Number(soldPrice),
'type': 'sell',
'date': new Date()});
try {
await pb.collection("portfolios").update(currentPortfolio?.id, {
"holdings": updatedHoldings,
"availableCash": availableCash,
"tradingHistory": tradingHistory,
})
output = 'success';
} catch (err) {
output = 'failure';
}
}
}
else {
output = 'marketClosed'
}
reply.send({ message: output })
})
done();
};

View File

@ -1,141 +0,0 @@
// Declare a route
module.exports = function (fastify, opts, done) {
const pb = opts.pb;
fastify.post('/upvote-comment', async (request, reply) => {
const data = request.body;
const commentId = data?.commentId;
const postId = data?.postId;
const userId = data?.userId;
console.log(data)
let output = 'failure';
try {
let doesUserExist = await pb.collection("alreadyVoted").getList(1, 50, {
filter: `user="${userId}"` && `comment="${commentId}"`,
})
doesUserExist = doesUserExist?.items?.find(item => item?.user === userId && item?.comment === commentId);
const votedId = doesUserExist?.id;
let currentVote = doesUserExist?.type;
//console.log('currentVote: ', currentVote)
const opPost = await pb.collection('comments').getOne(commentId)
//If user has no history with this post create it
if( !currentVote || votedId === 'undefined')
{
let formDataAlreadyVoted = new FormData();
formDataAlreadyVoted.append('comment', commentId);
formDataAlreadyVoted.append('user', userId);
formDataAlreadyVoted.append('type', 'upvote');
await pb.collection('alreadyVoted').create(formDataAlreadyVoted);
//create new record for notifications collections
if (userId !== opPost.user)
{
let formDataNotifications = new FormData();
formDataNotifications.append('opUser', opPost.user);
formDataNotifications.append('user', userId)
formDataNotifications.append('comment', commentId);
formDataNotifications.append('post', postId);
formDataNotifications.append('notifyType', 'vote');
await pb.collection('notifications').create(formDataNotifications);
}
}
if (currentVote === 'downvote')
{
console.log('downvote')
await pb.collection("comments").update(commentId, {
"upvote+": 1,
});
await pb.collection("comments").update(commentId, {
"downvote-": 1,
});
await pb.collection("alreadyVoted").update(votedId, {
"type": 'upvote',
});
//Reward: Find user of post and add +1 karma points
let opPost = await pb.collection('comments').getOne(commentId)
await pb.collection("users").update(opPost.user, {
"karma+": 2,
})
}
else if (currentVote === 'neutral' || !currentVote)
{
//Reward: User of post gets +1 karma points
await pb.collection("users").update(opPost.user, {
"karma+": 1,
})
await pb.collection("comments").update(commentId, {
"upvote+": 1,
});
await pb.collection("alreadyVoted").update(votedId, {
"type": 'upvote',
});
}
else
{
await pb.collection("comments").update(commentId, {
"upvote-": 1,
});
await pb.collection("alreadyVoted").update(votedId, {
"type": 'neutral',
});
//Reset Reward: Find user of post and subtract -1 karma points
await pb.collection("users").update(opPost.user, {
"karma-": 1,
})
}
}
catch(e) {
console.log(e)
}
reply.send({ items: output })
});
done();
};

View File

@ -1,137 +0,0 @@
// Declare a route
module.exports = function (fastify, opts, done) {
const pb = opts.pb;
fastify.post('/upvote', async (request, reply) => {
const data = request.body;
const postId = data?.postId;
const userId = data?.userId;
let output = 'failure';
try {
let doesUserExist = await pb.collection("alreadyVoted").getList(1, 50, {
filter: `user="${userId}"` && `post="${postId}"`,
})
doesUserExist = doesUserExist?.items?.find(item => item?.user === userId && item?.post === postId);
const votedId = doesUserExist?.id;
let currentVote = doesUserExist?.type;
//console.log('currentVote: ', currentVote)
const opPost = await pb.collection('posts').getOne(postId)
//If user has no history with this post create it
if( !currentVote || votedId === 'undefined')
{
let formDataAlreadyVoted = new FormData();
formDataAlreadyVoted.append('post', postId);
formDataAlreadyVoted.append('user', userId);
formDataAlreadyVoted.append('type', 'upvote');
await pb.collection('alreadyVoted').create(formDataAlreadyVoted);
//create new record for notifications collections
if (userId !== opPost.user)
{
let formDataNotifications = new FormData();
formDataNotifications.append('opUser', opPost.user);
formDataNotifications.append('user', userId)
formDataNotifications.append('post', postId);
formDataNotifications.append('notifyType', 'vote');
await pb.collection('notifications').create(formDataNotifications);
}
}
if (currentVote === 'downvote')
{
console.log('downvote')
await pb.collection("posts").update(postId, {
"upvote+": 1,
});
await pb.collection("posts").update(postId, {
"downvote-": 1,
});
await pb.collection("alreadyVoted").update(votedId, {
"type": 'upvote',
});
//Reward: Find user of post and add +1 karma points
let opPost = await pb.collection('posts').getOne(postId)
await pb.collection("users").update(opPost.user, {
"karma+": 2,
})
}
else if (currentVote === 'neutral' || !currentVote)
{
//Reward: User of post gets +1 karma points
await pb.collection("users").update(opPost.user, {
"karma+": 1,
})
await pb.collection("posts").update(postId, {
"upvote+": 1,
});
await pb.collection("alreadyVoted").update(votedId, {
"type": 'upvote',
});
}
else
{
await pb.collection("posts").update(postId, {
"upvote-": 1,
});
await pb.collection("alreadyVoted").update(votedId, {
"type": 'neutral',
});
//Reset Reward: Find user of post and subtract -1 karma points
await pb.collection("users").update(opPost.user, {
"karma-": 1,
})
}
}
catch(e) {
console.log(e)
}
reply.send({ items: output })
});
done();
};