summaryrefslogtreecommitdiff
path: root/lib/client/src/posts.ts
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2023-07-12 01:28:23 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2023-07-12 01:28:23 -0400
commitf64955c69d91e578e580b409ba31ac4b3477da96 (patch)
tree16f01392ddf1abfea13d7d1ede3bfb0459fe8f0d /lib/client/src/posts.ts
Initial commit
Diffstat (limited to 'lib/client/src/posts.ts')
-rw-r--r--lib/client/src/posts.ts169
1 files changed, 169 insertions, 0 deletions
diff --git a/lib/client/src/posts.ts b/lib/client/src/posts.ts
new file mode 100644
index 0000000..f673b21
--- /dev/null
+++ b/lib/client/src/posts.ts
@@ -0,0 +1,169 @@
+// Copyright (C) 2023 Vaughn Nugent
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+import { defaultTo, startsWith } from "lodash";
+import { ChannelApi, createScopedChannelApi } from "./channels";
+import { CMNextApi, CMNextAutoConfig, CMNextIndex, PostMeta } from "./types";
+
+export interface PostApi extends CMNextApi<PostMeta> {
+ /**
+ * Gets the content for a post by its Id
+ * @param postId The id of the post to fetch content for
+ * @returns A promise that resolves to the post content as a string
+ */
+ getPostContent: (post: string | PostMeta, extension?:string) => Promise<string>;
+ /**
+ * Gets the index file path for the channel
+ */
+ getIndexFilePath(): Promise<string>
+}
+
+/**
+ * A post api created from an automatic configuration
+ */
+export interface AutoPostApi extends PostApi, CMNextAutoConfig {
+ /**
+ * The channel api pass-thru
+ */
+ readonly channelApi: ChannelApi;
+}
+
+/**
+ * A post api created from a manual configuration
+ */
+export interface ManualPostApi extends PostApi, PostApiManualConfig {
+}
+
+export interface PostApiManualConfig {
+ /**
+ * The root directory path of the desired channel
+ */
+ readonly channelRootDir: string;
+ /**
+ * The relative path to the channel's post index file
+ */
+ readonly postIndexPath: string;
+ /**
+ * The relative path to the channel's content directory
+ */
+ readonly contentDir: string;
+}
+
+
+/**
+ * Creates a post api around the channel index file and the desired channel id
+ * to get posts from. This method requires an additional fetch to get the channel
+ * information before it can fetch posts, so it will be slower, but its
+ * discovery is automatic.
+ * @param channelUrl The url to the channel index file
+ * @param channelId The id of the channel to get posts from
+ * @returns A post api that can be used to get posts from the channel
+ */
+export const createAutoPostApi = ({ cmsChannelIndexPath, channelId }: CMNextAutoConfig): AutoPostApi => {
+ //Use scoped channel api
+ const channelApi = createScopedChannelApi(cmsChannelIndexPath, channelId);
+
+ const getIndex = async (): Promise<CMNextIndex<PostMeta>> => {
+ //Await the selected channel index
+ const indexUrl = await channelApi.getPostIndexPath();
+
+ if (!indexUrl){
+ //Return empty index
+ return { date: 0, records: [], version: "0.0.0" }
+ }
+
+ //Fetch the index file
+ const res = await fetch(indexUrl)
+ return await res.json()
+ }
+
+ const getPostContent = async (post: PostMeta | string, extension = '.html'): Promise<string> => {
+ //Get the selected channel
+ const contentDir = await channelApi.getContentDir();
+ const baseDir = await channelApi.getBaseDir();
+
+ if (!contentDir || !baseDir){
+ //Return empty content
+ return ""
+ }
+
+ const itemId = defaultTo(post.id, post)
+
+ //Fetch the content as text because it is html
+ const res = await fetch(`${baseDir}${contentDir}/${itemId}${extension}`)
+ return await res.text()
+ }
+
+ const getIndexFilePath = async () : Promise<string> => {
+ const indexUrl = await channelApi.getPostIndexPath();
+ return indexUrl || ""
+ }
+
+ return{
+ channelApi,
+ getPostContent,
+ channelId,
+ getIndexFilePath,
+ getIndex,
+ cmsChannelIndexPath,
+ }
+
+}
+
+/**
+ * Creates a post api around known channel information to avoid additional fetch
+ * requests to get the channel information. This method is faster, but requires
+ * the channel information to be known and remain constant.
+ * @param baseUrl The base url of the desired channel
+ * @param indexFilePath The path to the index file within the channel
+ * @param contentDir The path to the content directory within the channel
+ * @returns A post api that can be used to get posts from the channel
+ */
+export const createManualPostApi = ({ channelRootDir, contentDir, postIndexPath }: PostApiManualConfig): ManualPostApi => {
+
+ //Make sure inedx file has a leading slash
+ postIndexPath = startsWith(postIndexPath, '/') ? postIndexPath : `/${postIndexPath}`
+ contentDir = startsWith(contentDir, '/') ? contentDir : `/${contentDir}`
+
+ const getItemPath = (path: string) => `${channelRootDir}${path}`
+
+ const getIndex = async (): Promise<CMNextIndex<PostMeta>> => {
+ //Fetch the index file
+ const res = await fetch(getItemPath(postIndexPath))
+ return await res.json()
+ }
+
+ const getPostContent = async (post: PostMeta | string, extension = '.html'): Promise<string> => {
+ //Get the content url
+ const contentUrl = `${getItemPath(contentDir)}/${defaultTo(post.id, post)}${extension}`
+
+ //Fetch the content as text because it is html
+ const res = await fetch(contentUrl)
+ return await res.text()
+ }
+
+ const getIndexFilePath = async () : Promise<string> => {
+ return getItemPath(postIndexPath)
+ }
+
+ return{
+ getIndex,
+ channelRootDir,
+ contentDir,
+ postIndexPath,
+ getPostContent,
+ getIndexFilePath
+ }
+} \ No newline at end of file