studiocms
Version:
Astro Native CMS for AstroDB. Built from the ground up by the Astro community.
421 lines (420 loc) • 16.8 kB
JavaScript
import { eq } from "astro:db";
import { Effect, genLogger } from "../../../effect.js";
import { AstroDB, SDKCore_Generators, SDKCore_Users } from "../effect/index.js";
import { SDKCoreError, StudioCMS_SDK_Error } from "../errors.js";
import {
tsDiffTracking,
tsPageContent,
tsPageData,
tsPageDataCategories,
tsPageDataTags,
tsPageFolderStructure,
tsPermissions
} from "../tables.js";
import {
_ClearUnknownError,
_clearLibSQLError,
CacheContext,
isCacheEnabled,
pageDataReturn
} from "../utils.js";
import { SDKCore_CLEAR } from "./clear.js";
import { SDKCore_GET } from "./get.js";
import { SDKCore_UPDATE } from "./update.js";
class SDKCore_POST extends Effect.Service()(
"studiocms/sdk/SDKCore/modules/post",
{
dependencies: [
AstroDB.Default,
SDKCore_CLEAR.Default,
SDKCore_Users.Default,
SDKCore_UPDATE.Default,
SDKCore_Generators.Default,
SDKCore_GET.Default
],
effect: genLogger("studiocms/sdk/SDKCore/modules/post/effect")(function* () {
const [dbService, CLEAR, UPDATE, GET, { generateRandomIDNumber }, { pages }] = yield* Effect.all([
AstroDB,
SDKCore_CLEAR,
SDKCore_UPDATE,
SDKCore_GET,
SDKCore_Generators,
CacheContext
]);
const POST = {
databaseEntry: {
/**
* Insert a new page into the database.
*
* @param pageData - The data to insert into the page data table.
* @param pageContent - The data to insert into the page content table.
* @returns A promise that resolves to the inserted page data and page content.
* @throws {StudioCMS_SDK_Error} If an error occurs while inserting the page.
*/
pages: (pageData, pageContent) => Effect.gen(function* () {
const newContentID = pageData.id || crypto.randomUUID().toString();
const {
id = newContentID,
title,
slug,
description,
authorId = null,
package: packageName = "studiocms",
contentLang = "default",
heroImage = "",
showOnNav = false,
showAuthor = false,
showContributors = false,
categories = [],
tags = [],
contributorIds = [],
draft = false,
parentFolder = null
} = pageData;
const stringified = {
categories: categories || [],
tags: tags || [],
contributorIds: contributorIds || []
};
const contentData = {
id: crypto.randomUUID().toString(),
contentId: newContentID,
contentLang,
content: pageContent.content || ""
};
const NOW = /* @__PURE__ */ new Date();
const newPageData = yield* dbService.execute(
(db) => db.insert(tsPageData).values({
id,
title,
slug,
description,
authorId,
contentLang,
heroImage,
showAuthor,
showContributors,
showOnNav,
draft,
parentFolder,
package: packageName,
publishedAt: NOW,
updatedAt: NOW,
...stringified
}).returning({ id: tsPageData.id })
);
const newPageContent = yield* dbService.execute(
(db) => db.insert(tsPageContent).values(contentData).returning({ id: tsPageContent.id })
);
return {
pageData: newPageData,
pageContent: newPageContent
};
}).pipe(
Effect.catchTags({
LibSQLClientError: (cause) => _clearLibSQLError("POST.databaseEntry.pages", cause)
})
),
/**
* Inserts new page content into the database.
*
* @param pageContent - The data to insert into the page content table.
* @returns A promise that resolves to the inserted page content.
* @throws {StudioCMS_SDK_Error} If an error occurs while inserting the page content.
*/
pageContent: dbService.makeQuery(
(ex, pageContent) => ex(
(db) => db.insert(tsPageContent).values({
id: pageContent.id || crypto.randomUUID().toString(),
contentId: pageContent.contentId,
contentLang: pageContent.contentLang || "default",
content: pageContent.content || ""
}).returning({ id: tsPageContent.id })
).pipe(
Effect.catchTags({
LibSQLClientError: (cause) => _clearLibSQLError("POST.databaseEntry.pageContent", cause)
})
)
),
/**
* Inserts a new tag into the database.
*
* @param tag - The data to insert into the page data tags table.
* @returns A promise that resolves to the inserted tag.
* @throws {StudioCMS_SDK_Error} If an error occurs while inserting the tag.
*/
tags: (tag) => Effect.gen(function* () {
const id = tag.id || (yield* generateRandomIDNumber(9));
return yield* dbService.execute(
(db) => db.insert(tsPageDataTags).values({
id,
name: tag.name,
description: tag.description,
slug: tag.slug,
meta: JSON.stringify(tag.meta)
}).returning({ id: tsPageDataTags.id })
);
}).pipe(
Effect.catchTags({
LibSQLClientError: (cause) => _clearLibSQLError("POST.databaseEntry.tags", cause)
})
),
/**
* Inserts a new category into the database.
*
* @param category - The data to insert into the page data categories table.
* @returns A promise that resolves to the inserted category.
* @throws {StudioCMS_SDK_Error} If an error occurs while inserting the category.
*/
categories: (category) => Effect.gen(function* () {
const id = category.id || (yield* generateRandomIDNumber(9));
return yield* dbService.execute(
(db) => db.insert(tsPageDataCategories).values({
id,
name: category.name,
description: category.description,
slug: category.slug,
meta: JSON.stringify(category.meta)
}).returning({ id: tsPageDataCategories.id })
);
}).pipe(
Effect.catchTags({
LibSQLClientError: (cause) => _clearLibSQLError("POST.databaseEntry.tags", cause)
})
),
/**
* Inserts a new permission into the database.
*
* @param userId - The ID of the user to assign the rank to.
* @param rank - The rank to assign to the user.
* @returns A promise that resolves to the inserted permission.
* @throws {StudioCMS_SDK_Error} If an error occurs while inserting the permission.
*/
permissions: (userId, rank) => Effect.gen(function* () {
const userExists = yield* dbService.execute(
(db) => db.select().from(tsPermissions).where(eq(tsPermissions.user, userId)).get()
);
if (userExists) {
return yield* _clearLibSQLError(
"POST.databaseEntry.permissions",
"User already is already assigned a rank, please update the existing rank instead."
);
}
return yield* dbService.execute(
(db) => db.insert(tsPermissions).values({
user: userId,
rank
}).returning({ user: tsPermissions.user, rank: tsPermissions.rank })
);
}).pipe(
Effect.catchTags({
LibSQLClientError: (cause) => _clearLibSQLError("POST.databaseEntry.permissions", cause)
})
),
/**
* Inserts a new diff tracking entry into the database.
*
* @param diff - The data to insert into the diff tracking table.
* @returns A promise that resolves to the inserted diff tracking entry.
* @throws {StudioCMS_SDK_Error} If an error occurs while inserting the diff tracking entry.
*/
diffTracking: dbService.makeQuery(
(ex, diff) => ex(
(db) => db.insert(tsDiffTracking).values({
id: diff.id || crypto.randomUUID().toString(),
userId: diff.userId,
pageId: diff.pageId,
diff: diff.diff || "",
timestamp: diff.timestamp || /* @__PURE__ */ new Date(),
pageContentStart: diff.pageContentStart,
pageMetaData: JSON.stringify(diff.pageMetaData || {})
}).returning()
).pipe(
Effect.catchTags({
LibSQLClientError: (cause) => _clearLibSQLError("POST.databaseEntry.diffTracking", cause)
})
)
),
/**
* Inserts a new folder into the database.
*
* @param folder - The data to insert into the page folder structure table.
* @returns A promise that resolves to the inserted folder.
* @throws {StudioCMS_SDK_Error} If an error occurs while inserting the folder.
*/
folder: dbService.makeQuery(
(ex, folder) => ex(
(db) => db.insert(tsPageFolderStructure).values({
id: folder.id || crypto.randomUUID().toString(),
name: folder.name,
parent: folder.parent || null
}).returning().get()
).pipe(
Effect.catchTags({
LibSQLClientError: (cause) => _clearLibSQLError("POST.databaseEntry.folder", cause)
})
)
)
},
databaseEntries: {
/**
* Inserts multiple tags into the database.
*
* @param data - The data to insert into the page data tags table.
* @returns A promise that resolves to the inserted tags.
* @throws {StudioCMS_SDK_Error} If an error occurs while inserting the tags.
*/
tags: (data) => Effect.gen(function* () {
const entries = [];
for (const item of data) {
const id = item.id || (yield* generateRandomIDNumber(9));
entries.push({
id,
name: item.name,
slug: item.slug,
description: item.description,
meta: JSON.stringify(item.meta)
});
}
return yield* dbService.execute(
(db) => db.insert(tsPageDataTags).values(entries).returning({ id: tsPageDataTags.id })
);
}).pipe(
Effect.catchTags({
LibSQLClientError: (cause) => _clearLibSQLError("POST.databaseEntries.tags", cause)
})
),
/**
* Inserts multiple categories into the database.
*
* @param data - The data to insert into the page data categories table.
* @returns A promise that resolves to the inserted categories.
* @throws {StudioCMS_SDK_Error} If an error occurs while inserting the categories.
*/
categories: (data) => Effect.gen(function* () {
const entries = [];
for (const item of data) {
const id = item.id || (yield* generateRandomIDNumber(9));
entries.push({
id,
name: item.name,
slug: item.slug,
description: item.description,
meta: JSON.stringify(item.meta),
parent: null
});
}
return yield* dbService.execute(
(db) => db.insert(tsPageDataCategories).values(entries).returning({ id: tsPageDataCategories.id })
);
}).pipe(
Effect.catchTags({
LibSQLClientError: (cause) => _clearLibSQLError("POST.databaseEntries.categories", cause)
})
),
/**
* Inserts multiple permissions into the database.
*
* @param data - The data to insert into the permissions table.
* @returns A promise that resolves to the inserted permissions.
* @throws {StudioCMS_SDK_Error} If an error occurs while inserting the permissions.
*/
permissions: (data) => Effect.gen(function* () {
const currentPermittedUsers = yield* dbService.execute(
(db) => db.select().from(tsPermissions)
);
for (const permission of data) {
const userAlreadyExists = currentPermittedUsers.find(
(user) => user.user === permission.user
);
if (userAlreadyExists) {
return yield* Effect.fail(
new SDKCoreError({
type: "LibSQLDatabaseError",
cause: new StudioCMS_SDK_Error(
`User with ID ${permission.user} already has a rank assigned. Please update the existing rank instead.`
)
})
);
}
}
return yield* dbService.execute(
(db) => db.insert(tsPermissions).values(
data.map((permission) => {
return {
user: permission.user,
rank: permission.rank
};
})
).returning({ user: tsPermissions.user, rank: tsPermissions.rank })
);
}).pipe(
Effect.catchTags({
LibSQLClientError: (cause) => _clearLibSQLError("POST.databaseEntries.permissions", cause)
})
),
/**
* Inserts multiple pages into the database.
*
* @param pages - The data to insert into the page data and page content tables.
* @returns A promise that resolves to the inserted pages.
* @throws {StudioCMS_SDK_Error} If an error occurs while inserting the pages.
*/
pages: (pages2) => Effect.gen(function* () {
for (const { pageData, pageContent } of pages2) {
yield* POST.databaseEntry.pages(pageData, pageContent);
}
})
},
/**
* Inserts a new folder into the database and updates the cache.
* @param data - The data to insert into the page folder structure table.
* @returns A promise that resolves to the inserted folder.
* @throws {StudioCMS_SDK_Error} If an error occurs while inserting the folder.
*/
folder: (data) => Effect.gen(function* () {
const newEntry = yield* POST.databaseEntry.folder(data);
yield* CLEAR.folderList();
yield* CLEAR.folderTree();
yield* UPDATE.folderList;
yield* UPDATE.folderTree;
return newEntry;
}),
/**
* Inserts a new page into the database and updates the cache.
* @param data - The data to insert into the page data and page content tables.
* @returns A promise that resolves to the inserted page data.
* @throws {StudioCMS_SDK_Error} If an error occurs while inserting the page.
*/
page: (data) => Effect.gen(function* () {
const status = yield* isCacheEnabled;
const newPage = yield* POST.databaseEntry.pages(data.pageData, data.pageContent);
const pageId = newPage.pageData[0]?.id;
if (!pageId) {
return void 0;
}
const fetchedPageData = yield* GET.page.byId(pageId);
if (!fetchedPageData) {
return void 0;
}
const { data: toReturn } = fetchedPageData;
if (status) {
pages.set(toReturn.id, pageDataReturn(toReturn));
yield* CLEAR.folderList();
yield* CLEAR.folderTree();
}
return pageDataReturn(toReturn);
}).pipe(
Effect.catchTags({
UnknownException: (cause) => _ClearUnknownError("POST.page", cause)
})
)
};
return POST;
})
}
) {
}
export {
SDKCore_POST
};