400 lines
16 KiB
JavaScript
400 lines
16 KiB
JavaScript
import Fastify from "fastify";
|
|
import cors from "@fastify/cors";
|
|
import sequelizePlugin from "./plugins/sequelize.js";
|
|
import dotenv from "dotenv";
|
|
|
|
import { executeQueryAsync, retrieveResultsAsync } from "./services/athena.js";
|
|
dotenv.config();
|
|
import * as dashboard from "./queries/dashboard.js";
|
|
import * as queries from "./queries/queries.js";
|
|
import * as dashboardQueries from "./queries/dashboard.js";
|
|
|
|
const server = Fastify({ logger: true });
|
|
|
|
server.register(sequelizePlugin);
|
|
|
|
await server.register(cors, {
|
|
origin: "*",
|
|
});
|
|
// await server.register(cors, {
|
|
// origin: ["http://localhost:3000"],
|
|
// });
|
|
server.get("/", async (request, reply) => {
|
|
const [results, metadata] = await server.sequelize.query('SELECT 1 + 2 AS result');
|
|
console.log(results);
|
|
console.log(metadata);
|
|
return { hello: "world" };
|
|
});
|
|
|
|
server.get("/accounts", async (request, reply) => {
|
|
const queryExecutionId = await executeQueryAsync(queries.accountsQuery);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/accounts/:accountId/regions", async (request, reply) => {
|
|
const query = queries.regionsQuery.replace('%accountId%', request.params.accountId);
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/accounts/:accountId/regions/:regionCode/products", async (request, reply) => {
|
|
let regionCode = request.params.regionCode;
|
|
if (!regionCode || regionCode === 'global') {
|
|
regionCode = '';
|
|
}
|
|
const query = queries.productsByRegionQuery
|
|
.replace('%accountId%', request.params.accountId)
|
|
.replace('%regionCode%', regionCode);
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/accounts/:accountId/regions/:regionCode/products/:productCode", async (request, reply) => {
|
|
let regionCode = request.params.regionCode;
|
|
if (!regionCode || regionCode === 'global') {
|
|
regionCode = '';
|
|
}
|
|
const query = queries.productByRegionQuery
|
|
.replace('%accountId%', request.params.accountId)
|
|
.replace('%regionCode%', regionCode)
|
|
.replace('%productCode%', request.params.productCode);
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/accounts/:accountId/regions/:regionCode/products/:productCode/usage", async (request, reply) => {
|
|
let regionCode = request.params.regionCode;
|
|
if (!regionCode || regionCode === 'global') {
|
|
regionCode = '';
|
|
}
|
|
const query = queries.productUsageByRegionQuery
|
|
.replace('%accountId%', request.params.accountId)
|
|
.replace('%regionCode%', regionCode)
|
|
.replace('%productCode%', request.params.productCode);
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/accounts/:accountId/regions/:regionCode/products/:productCode/usage/:year", async (request, reply) => {
|
|
let regionCode = request.params.regionCode;
|
|
if (!regionCode || regionCode === 'global') {
|
|
regionCode = '';
|
|
}
|
|
const query = queries.productUsageByRegionYearQuery
|
|
.replace('%accountId%', request.params.accountId)
|
|
.replace('%regionCode%', regionCode)
|
|
.replace('%productCode%', request.params.productCode)
|
|
.replace('%year%', request.params.year);
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/accounts/:accountId/regions/:regionCode/products/:productCode/usage/:year/:month", async (request, reply) => {
|
|
let regionCode = request.params.regionCode;
|
|
if (!regionCode || regionCode === 'global') {
|
|
regionCode = '';
|
|
}
|
|
const query = queries.productUsageByRegionYearMonthQuery
|
|
.replace('%accountId%', request.params.accountId)
|
|
.replace('%regionCode%', regionCode)
|
|
.replace('%productCode%', request.params.productCode)
|
|
.replace('%year%', request.params.year)
|
|
.replace('%month%', request.params.month);
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/products", async (request, reply) => {
|
|
const query = queries.productQuery;
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/products/:productCode", async (request, reply) => {
|
|
const query = queries.productByCodeQuery
|
|
.replace('%productCode%', request.params.productCode);
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/products/:productCode/usage", async (request, reply) => {
|
|
const query = queries.productByCodeUsageQuery
|
|
.replace('%productCode%', request.params.productCode);
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/products/:productCode/usage/:year", async (request, reply) => {
|
|
const query = queries.productByCodeUsageYearQuery
|
|
.replace('%productCode%', request.params.productCode)
|
|
.replace('%year%', request.params.year);
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/products/:productCode/usage/:year/:month", async (request, reply) => {
|
|
const query = queries.productByCodeUsageYearMonthQuery
|
|
.replace('%productCode%', request.params.productCode)
|
|
.replace('%year%', request.params.year)
|
|
.replace('%month%', request.params.month);
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/invoices", async (request, reply) => {
|
|
const query = queries.invoices;
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/invoices/:invoiceId", async (request, reply) => {
|
|
const query = queries.invoiceById
|
|
.replace('%invoiceId%', request.params.invoiceId);
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/invoices/:invoiceId/products", async (request, reply) => {
|
|
const query = queries.invoiceByIdProducts
|
|
.replace('%invoiceId%', request.params.invoiceId);
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/invoices/:invoiceId/products/:productCode", async (request, reply) => {
|
|
const query = queries.invoiceByProductCode
|
|
.replace('%invoiceId%', request.params.invoiceId)
|
|
.replace('%productCode%', request.params.productCode);
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/invoices/:invoiceId/products/:productCode/usage", async (request, reply) => {
|
|
const query = queries.invoiceByProductCodeUsage
|
|
.replace('%invoiceId%', request.params.invoiceId)
|
|
.replace('%productCode%', request.params.productCode);
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/invoices/:invoiceId/accounts", async (request, reply) => {
|
|
const query = queries.invoiceByIdAccounts
|
|
.replace('%invoiceId%', request.params.invoiceId);
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/invoices/:invoiceId/accounts/:accountId", async (request, reply) => {
|
|
const query = queries.invoiceByIdAccount
|
|
.replace('%invoiceId%', request.params.invoiceId)
|
|
.replace('%accountId%', request.params.accountId);
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/invoices/:invoiceId/accounts/:accountId/products/:productCode", async (request, reply) => {
|
|
const query = queries.invoiceByIdAccountProducts
|
|
.replace('%invoiceId%', request.params.invoiceId)
|
|
.replace('%accountId%', request.params.accountId)
|
|
.replace('%productCode%', request.params.productCode);
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/invoices/:invoiceId/accounts/:accountId/products/:productCode/usage", async (request, reply) => {
|
|
const query = queries.invoiceByIdAccountProductsUsage
|
|
.replace('%invoiceId%', request.params.invoiceId)
|
|
.replace('%accountId%', request.params.accountId)
|
|
.replace('%productCode%', request.params.productCode);
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/accounts/:accountId/regions/:regionCode/products/:productCode/resources/:resourceId/usage", async (request, reply) => {
|
|
let regionCode = request.params.regionCode;
|
|
if (!regionCode || regionCode === 'global') {
|
|
regionCode = 'global';
|
|
}
|
|
|
|
const query = dashboard.productResourceUsageQuery
|
|
.replace('%accountId%', request.params.accountId)
|
|
.replace('%regionCode%', regionCode)
|
|
.replace('%productCode%', request.params.productCode)
|
|
.replace('%resourceId%', request.params.resourceId);
|
|
|
|
try {
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results ?? [];
|
|
} catch (err) {
|
|
request.log.error(err);
|
|
return reply.status(500).send({
|
|
error: 'Failed to fetch resource usage',
|
|
details: err.message,
|
|
});
|
|
}
|
|
});
|
|
|
|
server.get("/products/usage", async (request, reply) => {
|
|
const query = dashboard.allProductUsageQuery;
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/accounts/cost-usage-summary", async (request, reply) => {
|
|
const query = dashboard.accountCostUsageSummaryQuery;
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
|
|
|
|
server.get("/dashboard/summary", async (request, reply) => {
|
|
const [currentQueryId, previousQueryId] = await Promise.all([
|
|
executeQueryAsync(dashboardQueries.currentMonthSummary),
|
|
executeQueryAsync(dashboardQueries.previousMonthSummary)
|
|
]);
|
|
const [currentResults, previousResults] = await Promise.all([
|
|
retrieveResultsAsync(currentQueryId),
|
|
retrieveResultsAsync(previousQueryId)
|
|
]);
|
|
return {
|
|
current: currentResults[0] || {},
|
|
previous: previousResults[0] || {}
|
|
};
|
|
});
|
|
|
|
server.get("/dashboard/services", async (request, reply) => {
|
|
const queryExecutionId = await executeQueryAsync(dashboardQueries.serviceBreakdown);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/dashboard/trends", async (request, reply) => {
|
|
const queryExecutionId = await executeQueryAsync(dashboardQueries.dailyTrends);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/dashboard/accounts", async (request, reply) => {
|
|
const queryExecutionId = await executeQueryAsync(dashboardQueries.topAccounts);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/dashboard/today", async (request, reply) => {
|
|
const queryExecutionId = await executeQueryAsync(dashboardQueries.todaySpending);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results[0] || {};
|
|
});
|
|
|
|
server.get("/dashboard/weekly", async (request, reply) => {
|
|
const queryExecutionId = await executeQueryAsync(dashboardQueries.weeklyComparison);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
server.get("/dashboard/services/:serviceCode/accounts", async (request, reply) => {
|
|
const { days = 30 } = request.query;
|
|
const query = dashboardQueries.serviceAccountBreakdown
|
|
.replace('%serviceCode%', request.params.serviceCode)
|
|
.replace('%days%', days);
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return {
|
|
serviceCode: request.params.serviceCode,
|
|
accounts: results,
|
|
totalCost: results.reduce((sum, account) => sum + parseFloat(account.totalCost || 0), 0).toFixed(2)
|
|
};
|
|
});
|
|
|
|
server.get("/dashboard/services/:serviceCode/trends", async (request, reply) => {
|
|
const { days = 30 } = request.query;
|
|
const query = dashboardQueries.serviceTrends
|
|
.replace('%serviceCode%', request.params.serviceCode)
|
|
.replace('%days%', days);
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return {
|
|
serviceCode: request.params.serviceCode,
|
|
trends: results,
|
|
totalCost: results.reduce((sum, day) => sum + parseFloat(day.dailyCost || 0), 0).toFixed(2)
|
|
};
|
|
});
|
|
|
|
// Account drill-down routes
|
|
server.get("/dashboard/accounts/:accountId/services", async (request, reply) => {
|
|
const { days = 30 } = request.query;
|
|
const query = dashboardQueries.accountServiceBreakdown
|
|
.replace('%accountId%', request.params.accountId)
|
|
.replace('%days%', days);
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return {
|
|
accountId: request.params.accountId,
|
|
services: results,
|
|
totalCost: results.reduce((sum, service) => sum + parseFloat(service.totalCost || 0), 0).toFixed(2)
|
|
};
|
|
});
|
|
|
|
server.get("/dashboard/accounts/:accountId/trends", async (request, reply) => {
|
|
const { days = 30 } = request.query;
|
|
const query = dashboardQueries.accountTrends
|
|
.replace('%accountId%', request.params.accountId)
|
|
.replace('%days%', days);
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return {
|
|
accountId: request.params.accountId,
|
|
trends: results,
|
|
totalCost: results.reduce((sum, day) => sum + parseFloat(day.dailyCost || 0), 0).toFixed(2)
|
|
};
|
|
});
|
|
|
|
server.get("/dashboard/usage-per-month", async (request, reply) => {
|
|
const { startDate, endDate } = request.query;
|
|
const query = dashboardQueries.usageCostPerMonthWithDateRange(startDate, endDate);
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
server.get("/dashboard/yearly-spend", async (request, reply) => {
|
|
const query = dashboardQueries.yearlySpendSummary;
|
|
const queryExecutionId = await executeQueryAsync(query);
|
|
const results = await retrieveResultsAsync(queryExecutionId);
|
|
return results;
|
|
});
|
|
|
|
|
|
try {
|
|
await server.listen({ port: 3000 })
|
|
} catch (err) {
|
|
server.log.error(err);
|
|
process.exit(1);
|
|
}
|
|
|
|
|