youtuber-blog/src/routes/dashboard.js

302 lines
10 KiB
JavaScript
Raw Normal View History

2024-05-12 20:05:06 +00:00
/** @typedef {import("fastify").FastifyInstance} FastifyInstance */
import { desc, eq, getTableColumns, sql } from "drizzle-orm";
import { db } from "../db/index.js";
import { articles, articles as articlesTable, signups as signupsTable, sites, users } from "../db/schemas.js";
import { authMiddleware, authMiddlewareFn } from "../modules/middleware.js";
2024-05-29 19:35:18 +00:00
import { jsonToCsv, getAccessToken, getVideoCaptions, getCaptionText, parseTextFromCaptions, createBlogFromCaptions, createArticleSlug, getVideoById } from "../utils/index.js";
2024-05-12 20:05:06 +00:00
/**
*
* @param {FastifyInstance} fastify
* @param {unknown} _
* @param {() => void} done
*/
export const dashboardRoutes = (fastify, _, done) => {
fastify.register(authMiddleware);
fastify.get("/", async (req, response) => {
try {
const [{ site_id, user_id }] = await db.select({
site_id: sites.id,
user_id: sites.user_id
}).from(sites).where(eq(sites.user_id, req.session.user_id));
const recentArticles = await db.select({
title: articlesTable.title,
views: articlesTable.views,
created_at: articlesTable.created_at
}).from(articlesTable)
.where(eq(articlesTable.site_id, site_id))
.orderBy(articlesTable.created_at).limit(5);
const recentSignups = await db.select({
email: signupsTable.email,
created_at: signupsTable.created_at
}).from(signupsTable)
.where(eq(signupsTable.site_id, site_id))
.orderBy(desc(signupsTable.created_at)).limit(8);
2024-05-29 19:35:18 +00:00
const [{ totalArticles }] = await db.select({ totalArticles: sql`count(*)` }).from(articlesTable).where(eq(articlesTable.site_id, site_id));
2024-05-30 10:24:10 +00:00
const [{ totalViews }] = await db.select({ totalViews: sql`COALESCE(sum(${articlesTable.views}), 0)` }).from(articlesTable).where(eq(articlesTable.site_id, site_id));
2024-05-29 19:35:18 +00:00
const [{ totalEmails }] = await db.select({ totalEmails: sql`count(*)` }).from(signupsTable).where(eq(signupsTable.site_id, site_id));
2024-05-12 20:05:06 +00:00
response.send({
success: true,
recentArticles,
2024-05-29 19:35:18 +00:00
recentSignups,
totalArticles,
totalEmails,
totalViews
2024-05-12 20:05:06 +00:00
});
return;
} catch (e) {
console.log(e);
response.status(500).send({
success: false,
message: "Failed to get dashboard data",
log: e.message
});
return;
}
});
fastify.get("/email-export", async (req, response) => {
});
fastify.get("/signups", async (req, reply) => {
const site = await db.select().from(sites).where(eq(sites.user_id, req.session.user_id));
if (site[0]?.user_id != req.session.user_id) {
reply.status(403).send({ success: false });
return;
}
const signups = await db.select().from(signupsTable).where(eq(signupsTable.site_id, site[0].id)).orderBy(desc(signupsTable.created_at));
reply.send({
success: true,
signups
});
});
fastify.get("/signups/export", async (req, reply) => {
try {
const [{ site_id }] = await db.select({
site_id: sites.id
}).from(sites).where(eq(sites.user_id, req.session.user_id));
const signups = await db.select({
email: signupsTable.email,
source: sql`COALESCE(source, 'Home')`,
created_at: signupsTable.created_at
}).from(signupsTable).where(eq(signupsTable.site_id, site_id)).orderBy(desc(signupsTable.created_at));
let result = jsonToCsv(signups);
reply.send({
success: true,
data: result
});
return;
} catch (e) {
console.log(e);
}
});
fastify.put("/article", {
schema: {
body: {
type: "object",
properties: {
id: {
type: "string"
},
is_public: {
type: "boolean"
},
content: {
type: "string"
},
title: {
type: "string"
},
},
required: ["id"]
}
}
}, async (req, reply) => {
const [article] = await db.select(getTableColumns(articles)).from(articles).leftJoin(sites, eq(sites.id, articles.site_id)).where(eq(sites.user_id, req.session.user_id));
if (!article) {
reply.status(404).send({
success: false,
message: "This article does not exist."
});
return;
}
if (Object.keys(req.body).length > 1) {
await db.update(articles).set(JSON.parse(JSON.stringify({ ...req.body, id: undefined }))).where(eq(articles.id, req.body.id));
}
reply.send({
success: true
});
return;
});
fastify.post("/create", {
schema: {
body: {
type: "object",
required: ["video_id"],
properties: {
video_id: {
type: "string",
},
length: {
type: "number",
},
format: {
type: "string"
},
2024-05-29 19:35:18 +00:00
faq: {
type: "boolean"
},
2024-05-12 20:05:06 +00:00
}
},
}
}, async (req, reply) => {
try {
const [{ tokens }] = await db.select({
tokens: users.tokens
}).from(users).where(eq(users.id, req.session.user_id));
if (tokens < 1) {
reply.send({
success: false,
code: "insufficient_tokens",
message: "Insufficient tokens"
});
return;
}
const access_token = await getAccessToken(fastify, req);
const captions = await getVideoCaptions(access_token, req.body.video_id);
const preferred_caption_id = captions.find(x => x.snippet.language == 'en').id;
reply.send({
success: true
});
const caption_body = await getCaptionText(access_token, preferred_caption_id);
const caption_text = parseTextFromCaptions(caption_body).substring(28);
2024-05-29 19:35:18 +00:00
const video_data = await getVideoById(access_token, req.body.video_id);
const blog_content = await createBlogFromCaptions(caption_text, { title: video_data.title, description: video_data.description }, req.body);
2024-05-12 20:05:06 +00:00
const blog_content_json = JSON.parse(blog_content);
// TODO: once I add multiple sites per user, this should come from the client
const site = await db.select().from(sites).where(eq(sites.user_id, req.session.user_id));
await db.insert(articlesTable).values({
site_id: site[0].id,
title: blog_content_json.title,
2024-05-30 13:21:55 +00:00
content: blog_content_json.content,
meta_title: blog_content_json.meta_title,
meta_desc: blog_content_json.meta_desc,
excerp: blog_content_json.excerp,
2024-05-12 20:05:06 +00:00
source_video_id: req.body.video_id,
seo_slug: createArticleSlug(blog_content_json.title),
is_public: false
}).returning({ id: articlesTable.id });
await db.update(users).set({
tokens: tokens - 1
2024-05-29 19:35:18 +00:00
}).where(eq(users.id, req.session.user_id));
2024-05-12 20:05:06 +00:00
} catch (e) {
console.log(e);
}
});
fastify.put("/website", {
schema: {
body: {
type: "object",
properties: {
id: {
type: "string"
},
name: {
type: "string"
},
primary_color_hex: {
type: "string",
maxLength: 6,
minLength: 6
},
secondary_color_hex: {
type: "string",
maxLength: 6,
minLength: 6
},
text_color_hex: {
type: "string",
maxLength: 6,
minLength: 6
2024-05-29 19:35:18 +00:00
},
use_freebie: {
type: "boolean"
},
freebie_name: {
type: "string"
},
freebie_url: {
type: "string",
format: "uri"
},
freebie_image_url: {
type: "string",
format: "uri"
},
freebie_text: {
type: "string",
},
title: {
type: "string",
},
subtitle: {
type: "string",
2024-05-12 20:05:06 +00:00
}
},
required: ["id"]
}
},
preValidation: authMiddlewareFn
}, async (req, reply) => {
2024-05-29 19:35:18 +00:00
try {
const [site] = await db.select().from(sites).where(eq(sites.id, req.body.id));
2024-05-12 20:05:06 +00:00
2024-05-29 19:35:18 +00:00
if (site.user_id !== req.session.user_id) {
reply.status(403).send({
success: false
});
return;
}
2024-05-12 20:05:06 +00:00
2024-05-29 19:35:18 +00:00
await db.update(sites).set(req.body);
2024-05-12 20:05:06 +00:00
2024-05-29 19:35:18 +00:00
return {
success: true
}
} catch (e) {
console.log(e);
2024-05-12 20:05:06 +00:00
}
});
done();
};