diff --git a/README.md b/README.md index 1ace566..bb08cba 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,8 @@ I'm still looking into how rss aggregators generally handle auth for feeds and o } ``` +> For the most up to date definition, see [the actual typescript type](./src/lib/types.ts#L16). + ## :heavy_plus_sign: Add an article :lock: **Requires Authentication** @@ -125,12 +127,12 @@ You can add an article by providing the article's url in the body of a `POST` re ### Responses -| Status | StatusText | Body | Content-Type | -| ------ | ------------------------------------- | ----------- | ------------------ | -| 200 | `article added successfully` | `undefined` | `application/json` | -| 400 | `url is required` | `undefined` | `undefined` | -| 400 | `unable to extract metadata at {url}` | `undefined` | `undefined` | -| 401 | `not authorized` | `undefined` | `undefined` | +| Status | Body | Content-Type | +| ------ | ------------------------------------- | ------------ | +| 200 | `article added successfully` | `text/plain` | +| 400 | `url is required` | `text/plain` | +| 400 | `unable to extract metadata at {url}` | `text/plain` | +| 401 | `not authorized` | `text/plain` | ## :page_facing_up: Get a single `JSON` article @@ -142,11 +144,11 @@ You can get a single `JSON` object representing an article by making a `GET` req ### Responses -| Status | StatusText | Body | Content-Type | -| ------ | ----------------------------------------- | ----------- | ------------------ | -| 200 | `undefined` | `Article` | `application/json` | -| 401 | `not authorized` | `undefined` | `undefined` | -| 404 | `there is no article with an id of ":id"` | `undefined` | `undefined` | +| Status | Body | Content-Type | +| ------ | ----------------------------------------- | ------------------ | +| 200 | [Article](./src/lib/types.ts#L16) | `application/json` | +| 401 | `not authorized` | `text/plain` | +| 404 | `there is no article with an id of ":id"` | `text/plain` | ## :clipboard: Get a `JSON` array of all articles @@ -158,10 +160,10 @@ You can get a `JSON` array of articles by making a `GET` request to the `/articl ### Responses -| Status | StatusText | Body | Content-Type | -| ------ | ---------------- | ----------- | ------------------ | -| 200 | `undefined` | `Article[]` | `application/json` | -| 401 | `not authorized` | `undefined` | `undefined` | +| Status | Body | Content-Type | +| ------ | ----------------------------------- | ------------------ | +| 200 | [Article[]](./src/lib/types.ts#L16) | `application/json` | +| 401 | `not authorized` | `text/plain` | ## :memo: Update an article @@ -191,11 +193,11 @@ You can update an article based on it's id by making a `PATCH` request to the `/ ### Responses -| Status | StatusText | Body | Content-Type | -| ------ | ------------------------------------ | ----------- | ------------ | -| 200 | `article :id deleted successfully` | `undefined` | `undefined` | -| 401 | `not authorized` | `undefined` | `undefined` | -| 404 | `there is no article with id of :id` | `undefined` | `undefined` | +| Status | Body | Content-Type | +| ------ | ------------------------------------ | ------------ | +| 200 | `article :id deleted successfully` | `text/plain` | +| 401 | `not authorized` | `text/plain` | +| 404 | `there is no article with id of :id` | `text/plain` | ## :wastebasket: Delete an article @@ -207,11 +209,11 @@ You can delete an article based on it's id by making a `DELETE` request to the ` ### Responses -| Status | StatusText | Body | Content-Type | -| ------ | ------------------------------------ | ----------- | ------------ | -| 200 | `article :id deleted successfully` | `undefined` | `undefined` | -| 401 | `not authorized` | `undefined` | `undefined` | -| 404 | `there is no article with id of :id` | `undefined` | `undefined` | +| Status | Body | Content-Type | +| ------ | ------------------------------------ | ------------ | +| 200 | `article :id deleted successfully` | `text/plain` | +| 401 | `not authorized` | `text/plain` | +| 404 | `there is no article with id of :id` | `text/plain` | ## :wastebasket: Remove all articles @@ -221,10 +223,10 @@ You can delete an article based on it's id by making a `DELETE` request to the ` ### Responses -| Status | StatusText | Body | Content-Type | -| ------ | --------------------------------- | ----------- | ------------ | -| 200 | `x articles cleared successfully` | `undefined` | `undefined` | -| 401 | `not authorized` | `undefined` | `undefined` | +| Status | Body | Content-Type | +| ------ | --------------------------------- | ------------ | +| 200 | `x articles cleared successfully` | `text/plain` | +| 401 | `not authorized` | `text/plain` | ## :wastebasket: Purge old articles @@ -253,11 +255,11 @@ Examples ### Responses -| Status | StatusText | Body | Content-Type | -| ------ | -------------------------------------------------------------- | ----------- | ------------ | -| 200 | `x articles purged successfully` | `undefined` | `undefined` | -| 400 | `invalid format, use the formula ""` | `undefined` | `undefined` | -| 401 | `not authorized` | `undefined` | `undefined` | +| Status | Body | Content-Type | +| ------ | -------------------------------------------------------------- | ------------ | +| 200 | `x articles purged successfully` | `text/plain` | +| 400 | `invalid format, use the formula ""` | `text/plain` | +| 401 | `not authorized` | `text/plain` | ## :inbox_tray: Generate RSS2 feed from articles @@ -271,9 +273,9 @@ Examples ### Responses -| Status | StatusText | Body | Content-Type | -| ------ | ----------- | -------- | --------------------- | -| 200 | `undefined` | RSS Feed | `application/rss+xml` | +| Status | Body | Content-Type | +| ------ | ------------------------------------------------------- | --------------------- | +| 200 | [RSS2 Feed](https://www.rssboard.org/rss-specification) | `application/rss+xml` | ## :inbox_tray: Generate Atom feed from articles @@ -281,9 +283,9 @@ Examples ### Responses -| Status | StatusText | Body | Content-Type | -| ------ | ----------- | --------- | ---------------------- | -| 200 | `undefined` | Atom Feed | `application/atom+xml` | +| Status | Body | Content-Type | +| ------ | --------------------------------------------------------- | ---------------------- | +| 200 | [Atom Feed](https://validator.w3.org/feed/docs/atom.html) | `application/atom+xml` | ## :inbox_tray: Generate JSON feed from articles @@ -291,9 +293,9 @@ Examples ### Responses -| Status | StatusText | Body | Content-Type | -| ------ | ----------- | --------- | ------------------ | -| 200 | `undefined` | JSON Feed | `application/json` | +| Status | Body | Content-Type | +| ------ | -------------------------------------- | ------------------ | +| 200 | [JSON Feed](https://www.jsonfeed.org/) | `application/json` | ## :heart: Health @@ -303,6 +305,6 @@ A simple `GET` route to see if the server is up and ready to handle incoming req ### Responses -| Status | StatusText | Body | Content-Type | -| ------ | ---------- | ----------- | ------------ | -| 200 | `OK` | `undefined` | `undefined` | +| Status | Body | Content-Type | +| ------ | ---- | ------------ | +| 200 | `OK` | `text/plain` | diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 96cfa1e..1883666 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -1,6 +1,6 @@ import { env } from '$env/dynamic/private'; import { logger } from '$lib/utils'; -import type { Handle } from '@sveltejs/kit'; +import { text, type Handle } from '@sveltejs/kit'; export const handle: Handle = async ({ event, resolve }) => { if (env.PASSWORD && !env.AUTH_SECRET) { @@ -8,11 +8,10 @@ export const handle: Handle = async ({ event, resolve }) => { 'You must set $AUTH_SECRET since you set $PASSWORD ($AUTH_SECRET is used to sign JWTs!)' ); - return new Response(undefined, { - status: 500, - statusText: - 'You must set $AUTH_SECRET since you set $PASSWORD ($AUTH_SECRET is used to sign JWTs!)' - }); + return text( + 'You must set $AUTH_SECRET since you set $PASSWORD ($AUTH_SECRET is used to sign JWTs!)', + { status: 500, headers: { 'Content-Type': 'text/plain' } } + ); } return await resolve(event); diff --git a/src/routes/articles/+server.ts b/src/routes/articles/+server.ts index 7757b67..0246e03 100644 --- a/src/routes/articles/+server.ts +++ b/src/routes/articles/+server.ts @@ -1,16 +1,11 @@ import { isAuthorized } from '$lib/auth'; import { getArticles } from '$lib/db'; -import { type RequestHandler } from '@sveltejs/kit'; +import { json, text, type RequestHandler } from '@sveltejs/kit'; export const GET: RequestHandler = async ({ request, cookies }) => { if (!(await isAuthorized({ request, cookies }))) { - return new Response(undefined, { status: 401, statusText: 'not authorized' }); + return text('not authorized', { status: 401, headers: { 'Content-Type': 'text/plain' } }); } - return new Response(JSON.stringify(await getArticles()), { - headers: { - 'Content-Type': 'application/json' - }, - status: 200 - }); + return json(await getArticles(), { status: 200 }); }; diff --git a/src/routes/articles/[articleId]/+server.ts b/src/routes/articles/[articleId]/+server.ts index 0bb5de4..7a975d7 100644 --- a/src/routes/articles/[articleId]/+server.ts +++ b/src/routes/articles/[articleId]/+server.ts @@ -1,25 +1,22 @@ import { isAuthorized } from '$lib/auth'; import { getArticle } from '$lib/db'; -import { type RequestHandler } from '@sveltejs/kit'; +import { json, text, type RequestHandler } from '@sveltejs/kit'; export const GET: RequestHandler = async ({ params, request, cookies }) => { const { articleId } = params; if (!(await isAuthorized({ request, cookies }))) { - return new Response(undefined, { status: 401, statusText: 'not authorized' }); + return text('not authorized', { status: 401, headers: { 'Content-Type': 'text/plain' } }); } - const article = getArticle(Number(articleId)); + const article = await getArticle(Number(articleId)); if (!article) { - return new Response(undefined, { + return text(`there is no article with an id of "${articleId}"`, { status: 404, - statusText: `there is no article with an id of "${articleId}"` + headers: { 'Content-Type': 'text/plain' } }); } - return new Response(JSON.stringify(article), { - headers: { 'Content-Type': 'application/json' }, - status: 200 - }); + return json(article, { status: 200 }); }; diff --git a/src/routes/articles/[articleId]/delete/+server.ts b/src/routes/articles/[articleId]/delete/+server.ts index bc46fbc..c64a7a8 100644 --- a/src/routes/articles/[articleId]/delete/+server.ts +++ b/src/routes/articles/[articleId]/delete/+server.ts @@ -1,25 +1,25 @@ import { isAuthorized } from '$lib/auth'; import { deleteArticle } from '$lib/db'; -import { type RequestHandler } from '@sveltejs/kit'; +import { text, type RequestHandler } from '@sveltejs/kit'; export const DELETE: RequestHandler = async ({ request, params, cookies }) => { const { articleId } = params; if (!(await isAuthorized({ request, cookies }))) { - return new Response(undefined, { status: 401, statusText: 'not authorized' }); + return text('not authorized', { status: 401, headers: { 'Content-Type': 'text/plain' } }); } const [{ numDeletedRows }] = await deleteArticle(Number(articleId)); if (numDeletedRows < 1) { - return new Response(undefined, { + return text(`there is no article with id of ${articleId}`, { status: 404, - statusText: `there is no article with id of ${articleId}` + headers: { 'Content-Type': 'text/plain' } }); } - return new Response(undefined, { + return text(`article ${articleId} deleted successfully`, { status: 200, - statusText: `article ${articleId} deleted successfully` + headers: { 'Content-Type': 'text/plain' } }); }; diff --git a/src/routes/articles/[articleId]/update/+server.ts b/src/routes/articles/[articleId]/update/+server.ts index 6e4fc3f..09c9d51 100644 --- a/src/routes/articles/[articleId]/update/+server.ts +++ b/src/routes/articles/[articleId]/update/+server.ts @@ -1,28 +1,23 @@ import { isAuthorized } from '$lib/auth'; import { updateArticle } from '$lib/db'; -import { type RequestHandler } from '@sveltejs/kit'; +import { json, text, type RequestHandler } from '@sveltejs/kit'; export const PATCH: RequestHandler = async ({ request, params, cookies }) => { const { articleId } = params; const { article } = await request.json(); if (!(await isAuthorized({ request, cookies }))) { - return new Response(undefined, { status: 401, statusText: 'not authorized' }); + return text('not authorized', { status: 401, headers: { 'Content-Type': 'text/plain' } }); } - const updatedArticle = updateArticle(Number(articleId), article); + const updatedArticle = await updateArticle(Number(articleId), article); if (!updatedArticle) { - return new Response(undefined, { + return text(`there is no article with id of ${articleId}`, { status: 404, - statusText: `there is no article with id of ${articleId}` + headers: { 'Content-Type': 'text/plain' } }); } - return new Response(JSON.stringify(updatedArticle), { - headers: { - 'Content-Type': 'application/json' - }, - status: 200 - }); + return json(updatedArticle, { status: 200 }); }; diff --git a/src/routes/articles/add/+server.ts b/src/routes/articles/add/+server.ts index e3b1446..a7c8032 100644 --- a/src/routes/articles/add/+server.ts +++ b/src/routes/articles/add/+server.ts @@ -1,15 +1,19 @@ import { isAuthorized } from '$lib/auth'; import { addArticle } from '$lib/db'; import { extract } from '@extractus/article-extractor'; -import { type RequestHandler } from '@sveltejs/kit'; +import { text, type RequestHandler } from '@sveltejs/kit'; export const POST: RequestHandler = async ({ request, cookies }) => { + if (!request.body) { + return text('url is required', { status: 400, headers: { 'Content-Type': 'text/plain' } }); + } + const { url } = await request.json(); - if (!isAuthorized({ request, cookies })) { - return new Response(undefined, { status: 401, statusText: 'not authorized' }); - } else if (!url) { - return new Response(undefined, { status: 400, statusText: 'url is required' }); + if (!(await isAuthorized({ request, cookies }))) { + return text('not authorized', { status: 401, headers: { 'Content-Type': 'text/plain' } }); + } else if (!request.body || !url) { + return text('url is required', { status: 400, headers: { 'Content-Type': 'text/plain' } }); } const article = await extract(url); @@ -27,14 +31,14 @@ export const POST: RequestHandler = async ({ request, cookies }) => { ttr: article.ttr }); - return new Response(undefined, { + return text('article added successfully', { status: 200, - statusText: 'article added successfully' + headers: { 'Content-Type': 'text/plain' } }); } - return new Response(undefined, { + return text(`unable to extract metadata at "${url}"`, { status: 400, - statusText: `unable to extract metadata at "${url}"` + headers: { 'Content-Type': 'text/plain' } }); }; diff --git a/src/routes/articles/clear/+server.ts b/src/routes/articles/clear/+server.ts index f71396f..4c8bf9f 100644 --- a/src/routes/articles/clear/+server.ts +++ b/src/routes/articles/clear/+server.ts @@ -1,16 +1,16 @@ import { isAuthorized } from '$lib/auth'; import { deleteAllArticles } from '$lib/db'; -import { type RequestHandler } from '@sveltejs/kit'; +import { text, type RequestHandler } from '@sveltejs/kit'; export const DELETE: RequestHandler = async ({ request, cookies }) => { if (!(await isAuthorized({ request, cookies }))) { - return new Response(undefined, { status: 401, statusText: 'not authorized' }); + return text('not authorized', { status: 401, headers: { 'Content-Type': 'text/plain' } }); } const [{ numDeletedRows }] = await deleteAllArticles(); - return new Response(undefined, { + return text(`${numDeletedRows} articles cleared successfully`, { status: 200, - statusText: `${numDeletedRows} articles cleared successfully` + headers: { 'Content-Type': 'text/plain' } }); }; diff --git a/src/routes/articles/purge/+server.ts b/src/routes/articles/purge/+server.ts index 64c4961..4a2aa7d 100644 --- a/src/routes/articles/purge/+server.ts +++ b/src/routes/articles/purge/+server.ts @@ -1,32 +1,32 @@ import { isAuthorized } from '$lib/auth'; import { purgeArticles } from '$lib/db'; -import { type RequestHandler } from '@sveltejs/kit'; +import { text, type RequestHandler } from '@sveltejs/kit'; export const DELETE: RequestHandler = async ({ request, cookies, url }) => { const older_than = url.searchParams.get('older_than'); if (!older_than) { - return new Response(undefined, { + return text('missing required "older_than" url query parameter', { status: 400, - statusText: 'missing required "older_than" url query parameter' + headers: { 'Content-Type': 'text/plain' } }); } const [matchesFormat, amount, period] = older_than.match(/^([0-9]+)([hdmy])$/) ?? []; if (!(await isAuthorized({ request, cookies }))) { - return new Response(undefined, { status: 401, statusText: 'Not authorized' }); + return text('not authorized', { status: 401, headers: { 'Content-Type': 'text/plain' } }); } else if (!matchesFormat) { - return new Response(undefined, { + return text('invalid format, use the formula ""', { status: 400, - statusText: 'invalid format, use the formula ""' + headers: { 'Content-Type': 'text/plain' } }); } const [{ numDeletedRows }] = await purgeArticles(period as 'h' | 'd' | 'm' | 'y', Number(amount)); - return new Response(undefined, { + return text(`${numDeletedRows} articles purged successfully`, { status: 200, - statusText: `${numDeletedRows} articles purged successfully` + headers: { 'Content-Type': 'text/plain' } }); }; diff --git a/src/routes/atom/+server.ts b/src/routes/atom/+server.ts index 7cfe57d..f9fc7e5 100644 --- a/src/routes/atom/+server.ts +++ b/src/routes/atom/+server.ts @@ -1,12 +1,9 @@ import { getArticles } from '$lib/db'; import { generateFeed } from '$lib/feed'; -import { type RequestHandler } from '@sveltejs/kit'; +import { text, type RequestHandler } from '@sveltejs/kit'; -export const GET: RequestHandler = async () => { - return new Response(generateFeed(await getArticles()).atom1(), { - headers: { - 'Content-Type': 'application/atom+xml' - }, - status: 200 +export const GET: RequestHandler = async () => + text(generateFeed(await getArticles()).atom1(), { + status: 200, + headers: { 'Content-Type': 'application/atom+xml' } }); -}; diff --git a/src/routes/feed.xml/+server.ts b/src/routes/feed.xml/+server.ts index 4fe0d89..049fec6 100644 --- a/src/routes/feed.xml/+server.ts +++ b/src/routes/feed.xml/+server.ts @@ -1,12 +1,9 @@ import { getArticles } from '$lib/db'; import { generateFeed } from '$lib/feed'; -import { type RequestHandler } from '@sveltejs/kit'; +import { text, type RequestHandler } from '@sveltejs/kit'; -export const GET: RequestHandler = async () => { - return new Response(generateFeed(await getArticles()).rss2(), { - headers: { - 'Content-Type': 'application/rss+xml' - }, - status: 200 +export const GET: RequestHandler = async () => + text(generateFeed(await getArticles()).rss2(), { + status: 200, + headers: { 'Content-Type': 'application/rss+xml' } }); -}; diff --git a/src/routes/feed/+server.ts b/src/routes/feed/+server.ts index 4fe0d89..049fec6 100644 --- a/src/routes/feed/+server.ts +++ b/src/routes/feed/+server.ts @@ -1,12 +1,9 @@ import { getArticles } from '$lib/db'; import { generateFeed } from '$lib/feed'; -import { type RequestHandler } from '@sveltejs/kit'; +import { text, type RequestHandler } from '@sveltejs/kit'; -export const GET: RequestHandler = async () => { - return new Response(generateFeed(await getArticles()).rss2(), { - headers: { - 'Content-Type': 'application/rss+xml' - }, - status: 200 +export const GET: RequestHandler = async () => + text(generateFeed(await getArticles()).rss2(), { + status: 200, + headers: { 'Content-Type': 'application/rss+xml' } }); -}; diff --git a/src/routes/health/+server.ts b/src/routes/health/+server.ts index f27b445..4bfbc3c 100644 --- a/src/routes/health/+server.ts +++ b/src/routes/health/+server.ts @@ -1,8 +1,4 @@ -import { type RequestHandler } from '@sveltejs/kit'; +import { text, type RequestHandler } from '@sveltejs/kit'; -export const GET: RequestHandler = async () => { - return new Response(undefined, { - status: 200, - statusText: 'OK' - }); -}; +export const GET: RequestHandler = async () => + text('OK', { status: 200, headers: { 'Content-Type': 'text/plain' } }); diff --git a/src/routes/json/+server.ts b/src/routes/json/+server.ts index 029501a..15cc411 100644 --- a/src/routes/json/+server.ts +++ b/src/routes/json/+server.ts @@ -1,12 +1,9 @@ import { getArticles } from '$lib/db'; import { generateFeed } from '$lib/feed'; -import { type RequestHandler } from '@sveltejs/kit'; +import { json, type RequestHandler } from '@sveltejs/kit'; export const GET: RequestHandler = async () => { - return new Response(generateFeed(await getArticles()).json1(), { - headers: { - 'Content-Type': 'application/json' - }, + return json(generateFeed(await getArticles()).json1(), { status: 200 }); }; diff --git a/src/routes/rss.xml/+server.ts b/src/routes/rss.xml/+server.ts index 4fe0d89..049fec6 100644 --- a/src/routes/rss.xml/+server.ts +++ b/src/routes/rss.xml/+server.ts @@ -1,12 +1,9 @@ import { getArticles } from '$lib/db'; import { generateFeed } from '$lib/feed'; -import { type RequestHandler } from '@sveltejs/kit'; +import { text, type RequestHandler } from '@sveltejs/kit'; -export const GET: RequestHandler = async () => { - return new Response(generateFeed(await getArticles()).rss2(), { - headers: { - 'Content-Type': 'application/rss+xml' - }, - status: 200 +export const GET: RequestHandler = async () => + text(generateFeed(await getArticles()).rss2(), { + status: 200, + headers: { 'Content-Type': 'application/rss+xml' } }); -}; diff --git a/src/routes/rss/+server.ts b/src/routes/rss/+server.ts index 4fe0d89..049fec6 100644 --- a/src/routes/rss/+server.ts +++ b/src/routes/rss/+server.ts @@ -1,12 +1,9 @@ import { getArticles } from '$lib/db'; import { generateFeed } from '$lib/feed'; -import { type RequestHandler } from '@sveltejs/kit'; +import { text, type RequestHandler } from '@sveltejs/kit'; -export const GET: RequestHandler = async () => { - return new Response(generateFeed(await getArticles()).rss2(), { - headers: { - 'Content-Type': 'application/rss+xml' - }, - status: 200 +export const GET: RequestHandler = async () => + text(generateFeed(await getArticles()).rss2(), { + status: 200, + headers: { 'Content-Type': 'application/rss+xml' } }); -};