From 94d2479dc9f9305f38471fd5c39337ddd69b6138 Mon Sep 17 00:00:00 2001 From: Thomas Hintz Date: Thu, 30 Mar 2023 16:17:58 -0700 Subject: [PATCH] update --- src/app/(main)/reactors/account/layout.jsx | 4 +- src/db.js | 11 ++ src/pages/api/create-account.js | 4 +- src/pages/api/feed/[guid].rss.js | 111 ------------------- src/pages/api/feed/[uuid]/index.js | 118 +++++++++++++++++++++ src/pages/api/sign-out.js | 10 ++ 6 files changed, 145 insertions(+), 113 deletions(-) delete mode 100644 src/pages/api/feed/[guid].rss.js create mode 100644 src/pages/api/feed/[uuid]/index.js create mode 100644 src/pages/api/sign-out.js diff --git a/src/app/(main)/reactors/account/layout.jsx b/src/app/(main)/reactors/account/layout.jsx index 71b1ff8..eca4c28 100644 --- a/src/app/(main)/reactors/account/layout.jsx +++ b/src/app/(main)/reactors/account/layout.jsx @@ -28,7 +28,9 @@ export default async function Layout({ children }) { signed in as: {userId} diff --git a/src/db.js b/src/db.js index 8d24d13..9f05f77 100644 --- a/src/db.js +++ b/src/db.js @@ -53,6 +53,17 @@ id integer primary key autoincrement, last_build_date text )`] }, + { + key: 6, + name: 'create table subscriptions', + sql: [`create table subscriptions ( +id integer primary key autoincrement, +user_id integer not null, +uuid text not null, +started_date text, +foreign key (user_id) references users (id) +)`] + } ]; const checkForMigrationsSql = `select key from migrations where run='True' order by key`; diff --git a/src/pages/api/create-account.js b/src/pages/api/create-account.js index 034a021..aa35ea4 100644 --- a/src/pages/api/create-account.js +++ b/src/pages/api/create-account.js @@ -3,7 +3,7 @@ const stripe = new Stripe('sk_test_51MVz87Ke2JFOuDSNa2PVPrs3BBq9vJQwwDITC3sOB521 import db from '@/db'; -import { scrypt, randomBytes, timingSafeEqual } from 'crypto'; +import { scrypt, randomBytes, timingSafeEqual, randomUUID } from 'crypto'; import { promisify } from 'util'; const scryptPromise = promisify(scrypt); @@ -45,6 +45,8 @@ export default async function handler(req, res) { const salt = genSalt(); const hashRes = await hash(salt, password); await db.run('insert into users (email, salt, password_hash) values (?, ?, ?);', email, salt, hashRes); + const { id: userId } = await db.get('select id from users where email=?', email); + await db.run('insert into subscriptions (uuid, user_id) values (?, ?);', randomUUID(), userId); console.log('done inserting user'); res.redirect('/reactors') } else { diff --git a/src/pages/api/feed/[guid].rss.js b/src/pages/api/feed/[guid].rss.js deleted file mode 100644 index 942125e..0000000 --- a/src/pages/api/feed/[guid].rss.js +++ /dev/null @@ -1,111 +0,0 @@ -import path from 'path'; -import fs from 'fs'; - -import db from '@/db'; - -import { Podcast } from 'podcast'; -import mp3Duration from 'mp3-duration'; - -import { getEpisodes } from '@/data/episodes'; - -async function syncEpisodes() { - const episodes = await getEpisodes(); - let newEpisodes = false; - - const dbUpdates = episodes.map(async ({ title, published, description, content, slug, audio: { src, length }, num, id, youtube }) => { - const filename = `0${num}-mixed.mp3`; // TODO auto-add the 0 prefix - const filepath = path.join(process.cwd(), 'public', 'files', 'episodes', filename); - if (fs.existsSync(filepath)) { - const existsInDb = await db.get('select id from episodes where number=?', num); - if (!existsInDb) { - newEpisodes = true; - console.log('adding to db'); - const duration = Math.round(await mp3Duration(filepath)); - await db.run('insert into episodes (number, content, summary, slug, season, episode, filename, duration, title, episode_type, buzzsprout_id, buzzsprout_url, pub_date, youtube_url) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);', - num, - content, - description, - slug, - 1, - num, - filename, - duration, - title, - 'full', - id, - src, - published, - youtube); - console.log('added to db', num); - } - } - }) - // if (newEpisodes) { - // TODO upsert: "insert into feed (last_build_date) VALUES(datetime('now'),datetime('now', 'localtime'));" - // } - await Promise.all(dbUpdates); - return newEpisodes; -}; - -export default async function handler(req, res) { - if (req.method === 'GET') { - // await syncEpisodes(); - const feed = new Podcast({ - title: 'The React Show (Reactors)', - description: "Premium subscription to The React Show. Discussions about React, JavaScript, and web development by React experts with a focus on diving deep into learning React and discussing what it's like to work within the React industry.", - feedUrl: 'https://www.thereactshow.com/api/feed.rss', - siteUrl: 'https://www.thereactshow.com', - imageUrl: 'https://storage.buzzsprout.com/variants/d1tds1rufs5340fyq9mpyzo491qp/5cfec01b44f3e29fae1fb88ade93fc4aecd05b192fbfbc2c2f1daa412b7c1921.jpg', - author: 'The React Show', - copyright: '© 2023 Owl Creek', - language: 'en', - categories: ['Technology','Education','Business'], - pubDate: 'May 20, 2012 04:00:00 GMT', - ttl: 60, - itunesAuthor: 'The React Show', - itunesOwner: { name: 'The React Show' }, - itunesExplicit: false, - itunesCategory: [{ - text: 'Technology' - }, - { - text: 'Education' - }, - { - text: 'Business' - }], - itunesImage: 'https://storage.buzzsprout.com/variants/d1tds1rufs5340fyq9mpyzo491qp/5cfec01b44f3e29fae1fb88ade93fc4aecd05b192fbfbc2c2f1daa412b7c1921.jpg' - }); - - const dbEpisodes = await db.all('select * from episodes order by number desc;'); - dbEpisodes.forEach(({ title, pub_date, summary: description, content, slug, duration, filename, number, episode_type }) => { - const filepath = path.join(process.cwd(), 'public', 'files', 'episodes', filename); - if (fs.existsSync(filepath)) { - feed.addItem({ - title, - description: content, - content, - url: `https://www.thereactshow.com/podcast/${slug}`, - date: pub_date, - itunesTitle: title, - itunesExplicit: false, - itunesSummary: description, - itunesDuration: duration, - itunesAuthor: 'The React Show', - itunesSeason: 1, - itunesEpisode: number, - itunesEpisodeType: episode_type, - enclosure : { - url: `https://www.thereactshow.com/files/episodes/${filename}`, - file: filepath - }, - }); - } - }) - - const xml = feed.buildXml(); - - res.setHeader('Content-Type', 'text/xml; charset=utf-8'); - res.send(xml); - } -}; diff --git a/src/pages/api/feed/[uuid]/index.js b/src/pages/api/feed/[uuid]/index.js new file mode 100644 index 0000000..ff804d9 --- /dev/null +++ b/src/pages/api/feed/[uuid]/index.js @@ -0,0 +1,118 @@ +import path from 'path'; +import fs from 'fs'; + +import db from '@/db'; + +import { Podcast } from 'podcast'; +import mp3Duration from 'mp3-duration'; + +import { getEpisodes } from '@/data/episodes'; + +async function syncEpisodes() { + const episodes = await getEpisodes(); + let newEpisodes = false; + + const dbUpdates = episodes.map(async ({ title, published, description, content, slug, audio: { src, length }, num, id, youtube }) => { + const filename = `0${num}-mixed.mp3`; // TODO auto-add the 0 prefix + const filepath = path.join(process.cwd(), 'public', 'files', 'episodes', filename); + if (fs.existsSync(filepath)) { + const existsInDb = await db.get('select id from episodes where number=?', num); + if (!existsInDb) { + newEpisodes = true; + console.log('adding to db'); + const duration = Math.round(await mp3Duration(filepath)); + await db.run('insert into episodes (number, content, summary, slug, season, episode, filename, duration, title, episode_type, buzzsprout_id, buzzsprout_url, pub_date, youtube_url) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);', + num, + content, + description, + slug, + 1, + num, + filename, + duration, + title, + 'full', + id, + src, + published, + youtube); + console.log('added to db', num); + } + } + }) + // if (newEpisodes) { + // TODO upsert: "insert into feed (last_build_date) VALUES(datetime('now'),datetime('now', 'localtime'));" + // } + await Promise.all(dbUpdates); + return newEpisodes; +}; + +export default async function handler(req, res) { + if (req.method === 'GET') { + // await syncEpisodes(); + const { uuid: uuidRaw } = req.query; + const uuid = uuidRaw.split('.rss')[0]; + const subExists = await db.get('select id from subscriptions where uuid=?', uuid); + if (subExists) { + const feed = new Podcast({ + title: 'The React Show (Reactors)', + description: "Premium subscription to The React Show. Discussions about React, JavaScript, and web development by React experts with a focus on diving deep into learning React and discussing what it's like to work within the React industry.", + feedUrl: `https://www.thereactshow.com/api/${uuidRaw}`, + siteUrl: 'https://www.thereactshow.com', + imageUrl: 'https://storage.buzzsprout.com/variants/d1tds1rufs5340fyq9mpyzo491qp/5cfec01b44f3e29fae1fb88ade93fc4aecd05b192fbfbc2c2f1daa412b7c1921.jpg', + author: 'The React Show', + copyright: '© 2023 Owl Creek', + language: 'en', + categories: ['Technology','Education','Business'], + pubDate: 'May 20, 2012 04:00:00 GMT', + ttl: 60, + itunesAuthor: 'The React Show', + itunesOwner: { name: 'The React Show' }, + itunesExplicit: false, + itunesCategory: [{ + text: 'Technology' + }, + { + text: 'Education' + }, + { + text: 'Business' + }], + itunesImage: 'https://storage.buzzsprout.com/variants/d1tds1rufs5340fyq9mpyzo491qp/5cfec01b44f3e29fae1fb88ade93fc4aecd05b192fbfbc2c2f1daa412b7c1921.jpg' + }); + + const dbEpisodes = await db.all('select * from episodes order by number desc;'); + dbEpisodes.forEach(({ title, pub_date, summary: description, content, slug, duration, filename, number, episode_type }) => { + const filepath = path.join(process.cwd(), 'public', 'files', 'episodes', filename); + if (fs.existsSync(filepath)) { + feed.addItem({ + title, + description: content, + content, + url: `https://www.thereactshow.com/podcast/${slug}`, + date: pub_date, + itunesTitle: title, + itunesExplicit: false, + itunesSummary: description, + itunesDuration: duration, + itunesAuthor: 'The React Show', + itunesSeason: 1, + itunesEpisode: number, + itunesEpisodeType: episode_type, + enclosure : { + url: `https://www.thereactshow.com/files/episodes/${filename}`, + file: filepath + }, + }); + } + }) + + const xml = feed.buildXml(); + + res.setHeader('Content-Type', 'text/xml; charset=utf-8'); + res.send(xml); + } else { + return res.status(404).send('Not found'); + } + } +}; diff --git a/src/pages/api/sign-out.js b/src/pages/api/sign-out.js new file mode 100644 index 0000000..614b407 --- /dev/null +++ b/src/pages/api/sign-out.js @@ -0,0 +1,10 @@ +import { deleteCookie } from 'cookies-next'; + +export default async function handler(req, res) { + if (req.method === 'POST') { + deleteCookie('session', { req, res, httpOnly: true, sameSite: true, secure: process.env.NODE_ENV === 'production' }); + res.redirect('/reactors'); + } else { + // Handle any other HTTP method + } +}