First Commit. Duplicate bunext-sqlite preset

This commit is contained in:
2026-10-18 16:47:11 +01:00
commit 81e4a8aecc
718 changed files with 41695 additions and 0 deletions
@@ -0,0 +1,28 @@
import checkUserAccess from "@/src/functions/backend/auth/check-user-access";
import type { AdminCrudAPIParams } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
export default async function ({
user,
user_types,
}: AdminCrudAPIParams): Promise<APIResponseObject> {
if (!user?.id) {
return {
success: false,
msg: `No user passed`,
};
}
const is_super_admin = checkUserAccess({ user_types });
if (!is_super_admin.success) {
return {
success: false,
msg: `Unauthorized`,
};
}
return {
success: true,
};
}
@@ -0,0 +1,28 @@
import _ from "lodash";
import defaultChecks from "../default-checks";
import type { AdminCrudAPIParams } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import checkUserAccess from "@/src/functions/backend/auth/check-user-access";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const { table, user, user_types, body, id, query } = params;
const default_check = await defaultChecks(params);
const is_super_admin = checkUserAccess({ user_types }).success;
if (!default_check.success) {
return default_check;
}
if (!is_super_admin) {
return {
success: false,
};
}
return {
success: true,
};
}
@@ -0,0 +1,28 @@
import _ from "lodash";
import checks from "./checks";
import type { AdminCrudAPIParams } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import BunSQLite from "@moduletrace/bun-sqlite";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const { table, body, id, query } = params;
const check = await checks(params);
if (!check?.success) {
return {
success: false,
msg: `Delete Unauthorized`,
};
}
const res = await BunSQLite.delete({
table,
query: _.merge(query?.sql_query, body?.sql_query),
targetId: id,
});
return res;
}
@@ -0,0 +1,20 @@
import _ from "lodash";
import defaultChecks from "../default-checks";
import type { AdminCrudAPIParams } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const { table, user, user_types, body, id, query } = params;
const default_check = await defaultChecks(params);
if (!default_check.success) {
return default_check;
}
return {
success: true,
};
}
@@ -0,0 +1,31 @@
import _ from "lodash";
import checks from "./checks";
import type { AdminCrudAPIParams } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import BunSQLite from "@moduletrace/bun-sqlite";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const { table, body, id, query } = params;
const check = await checks(params);
if (!check?.success) {
return {
success: false,
msg: `Select Unauthorized`,
};
}
let final_sql_query = _.merge(query?.sql_query, body?.sql_query);
const res = await BunSQLite.select({
table,
query: final_sql_query,
targetId: id,
count: query?.count || body?.count,
});
return res;
}
@@ -0,0 +1,18 @@
import type { AdminCrudAPIParams } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import _ from "lodash";
import defaultChecks from "../default-checks";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const default_check = await defaultChecks(params);
if (!default_check.success) {
return default_check;
}
return {
success: true,
};
}
@@ -0,0 +1,37 @@
import _ from "lodash";
import checks from "./checks";
import type { AdminCrudAPIParams } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import BunSQLite from "@moduletrace/bun-sqlite";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const { table, body } = params;
const check = await checks(params);
if (!check?.success) {
return {
success: false,
msg: `Insert Unauthorized`,
};
}
if (!body?.insert_data) {
return {
success: false,
msg: `No data to insert`,
};
}
let final_insert_data = [...body.insert_data];
const res = await BunSQLite.insert({
table,
data: final_insert_data,
update_on_duplicate: body.update_on_duplicate,
});
return res;
}
@@ -0,0 +1,30 @@
import _ from "lodash";
import defaultChecks from "../default-checks";
import type { AdminCrudAPIParams } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import checkUserAccess from "@/src/functions/backend/auth/check-user-access";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const { user_types } = params;
const default_check = await defaultChecks(params);
if (!default_check.success) {
return default_check;
}
const is_admin = checkUserAccess({
user_types,
includes: ["admin"],
});
if (!is_admin.success) {
return is_admin;
}
return {
success: true,
};
}
@@ -0,0 +1,36 @@
import _ from "lodash";
import checks from "./checks";
import type { AdminCrudAPIParams } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import BunSQLite from "@moduletrace/bun-sqlite";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const { table, body, id, query } = params;
const check = await checks(params);
if (!check?.success) {
return {
success: false,
msg: `Update Unauthorized`,
};
}
if (!body?.update_data) {
return {
success: false,
msg: `No data to update`,
};
}
const res = await BunSQLite.update({
table,
data: body.update_data,
query: _.merge(query?.sql_query, body?.sql_query),
targetId: id,
});
return res;
}
@@ -0,0 +1,29 @@
import type { AdminCrudAPIParams } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import {
gatedCrudDelete,
gatedCrudGet,
gatedCrudPost,
gatedCrudPut,
} from "../../(utils)/gated-crud";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
switch (params.req.method) {
case "GET":
return await gatedCrudGet(params);
case "POST":
return await gatedCrudPost(params);
case "PUT":
return await gatedCrudPut(params);
case "DELETE":
return await gatedCrudDelete(params);
default:
return {
success: false,
msg: `Unhandled`,
};
}
}
@@ -0,0 +1,29 @@
import type { AdminCrudAPIParams } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import {
gatedCrudDelete,
gatedCrudGet,
gatedCrudPost,
gatedCrudPut,
} from "../../(utils)/gated-crud";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
switch (params.req.method) {
case "GET":
return await gatedCrudGet(params);
case "POST":
return await gatedCrudPost(params);
case "PUT":
return await gatedCrudPut(params);
case "DELETE":
return await gatedCrudDelete(params);
default:
return {
success: false,
msg: `Unhandled`,
};
}
}
@@ -0,0 +1,29 @@
import type { AdminCrudAPIParams } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import {
gatedCrudDelete,
gatedCrudGet,
gatedCrudPost,
gatedCrudPut,
} from "../../(utils)/gated-crud";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
switch (params.req.method) {
case "GET":
return await gatedCrudGet(params);
case "POST":
return await gatedCrudPost(params);
case "PUT":
return await gatedCrudPut(params);
case "DELETE":
return await gatedCrudDelete(params);
default:
return {
success: false,
msg: `Unhandled`,
};
}
}
@@ -0,0 +1,29 @@
import type { AdminCrudAPIParams } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import {
gatedCrudDelete,
gatedCrudGet,
gatedCrudPost,
gatedCrudPut,
} from "../../(utils)/gated-crud";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
switch (params.req.method) {
case "GET":
return await gatedCrudGet(params);
case "POST":
return await gatedCrudPost(params);
case "PUT":
return await gatedCrudPut(params);
case "DELETE":
return await gatedCrudDelete(params);
default:
return {
success: false,
msg: `Unhandled`,
};
}
}
@@ -0,0 +1,107 @@
import type { BUN_SQLITE_BSQLITE_INVOICES } from "@/db/types/db";
import { SiteData } from "@/src/data/site-data";
import checkUserAccess from "@/src/functions/backend/auth/check-user-access";
import grabNextDocumentSequence from "@/src/functions/backend/db/documents/grab-next-document-sequence";
import type { AdminCrudAPIParams } from "@/src/types";
import BunSQLite from "@moduletrace/bun-sqlite";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import {
gatedCrudDelete,
gatedCrudGet,
gatedCrudPut,
} from "../../(utils)/gated-crud";
function invoiceNumberPrefix(): string {
const now = new Date();
return `${SiteData["SiteSlug"].toUpperCase()}-${now.getFullYear()}${String(
now.getMonth() + 1,
).padStart(2, "0")}`;
}
async function buildInvoiceRows({
rows,
}: {
rows: BUN_SQLITE_BSQLITE_INVOICES[];
}): Promise<BUN_SQLITE_BSQLITE_INVOICES[]> {
const prefix = invoiceNumberPrefix();
let next_sequence: number | undefined;
const final_rows: BUN_SQLITE_BSQLITE_INVOICES[] = [];
for (const row of rows) {
const invoice_row = { ...row } as BUN_SQLITE_BSQLITE_INVOICES;
if (!invoice_row.invoice_number) {
next_sequence ??= await grabNextDocumentSequence({
table: "invoices",
column: "invoice_number",
prefix,
});
invoice_row.invoice_number = `${prefix}-${String(next_sequence).padStart(3, "0")}`;
next_sequence += 1;
}
final_rows.push(invoice_row);
}
return final_rows;
}
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
switch (params.req.method) {
case "GET":
return await gatedCrudGet(params);
case "POST": {
const { table, body, user_types } = params;
const access = checkUserAccess({
user_types,
includes: ["admin"],
});
if (!access.success) {
return access;
}
if (!body?.insert_data) {
return {
success: false,
msg: `No data to insert`,
};
}
for (let attempt = 0; attempt < 5; attempt++) {
const final_insert_data = await buildInvoiceRows({
rows: body.insert_data as BUN_SQLITE_BSQLITE_INVOICES[],
});
try {
return await BunSQLite.insert({
table,
data: final_insert_data,
update_on_duplicate: body.update_on_duplicate,
});
} catch (error: any) {
if (attempt >= 4) {
throw error;
}
}
}
throw new Error(`Couldn't create invoice`);
}
case "PUT":
return await gatedCrudPut(params);
case "DELETE":
return await gatedCrudDelete(params);
default:
return {
success: false,
msg: `Unhandled`,
};
}
}
@@ -0,0 +1,29 @@
import type { AdminCrudAPIParams } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import {
gatedCrudDelete,
gatedCrudGet,
gatedCrudPost,
gatedCrudPut,
} from "../../(utils)/gated-crud";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
switch (params.req.method) {
case "GET":
return await gatedCrudGet(params);
case "POST":
return await gatedCrudPost(params);
case "PUT":
return await gatedCrudPut(params);
case "DELETE":
return await gatedCrudDelete(params);
default:
return {
success: false,
msg: `Unhandled`,
};
}
}
@@ -0,0 +1,80 @@
import _ from "lodash";
import type { AdminCrudAPIParams, TableType } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import checkUserAccess from "@/src/functions/backend/auth/check-user-access";
import type { ServerQueryParam } from "@moduletrace/bun-sqlite";
import type {
BUN_SQLITE_BSQLITE_ALL_TYPEDEFS,
BUN_SQLITE_BSQLITE_MEDIA,
} from "@/db/types/db";
import BunSQLite from "@moduletrace/bun-sqlite";
import { rmSync } from "fs";
import path from "path";
import grabDirNames from "@/src/utils/grab-dir-names";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const { user, user_types, body, id } = params;
const can_delete_media = checkUserAccess({
user_types,
includes: ["admin"],
});
if (!can_delete_media.success) {
return {
success: false,
msg: `Can't delete media`,
};
}
let query = _.merge<
ServerQueryParam<BUN_SQLITE_BSQLITE_ALL_TYPEDEFS>,
ServerQueryParam<BUN_SQLITE_BSQLITE_ALL_TYPEDEFS>
>(body?.sql_query || {}, {});
const media_to_delete = await BunSQLite.select<
BUN_SQLITE_BSQLITE_MEDIA,
TableType
>({
table: "media",
query,
targetId: id,
});
if (!media_to_delete.payload) {
return {
success: false,
msg: `No Media to Delete.`,
};
}
const { DATA_DIR } = grabDirNames();
for (let i = 0; i < media_to_delete.payload.length; i++) {
const media = media_to_delete.payload[i];
if (!media?.id) continue;
if (media.media_write_path) {
rmSync(path.join(DATA_DIR, media.media_write_path), {
force: true,
});
}
if (media.media_thumbnail_write_path) {
rmSync(path.join(DATA_DIR, media.media_thumbnail_write_path), {
force: true,
});
}
await BunSQLite.delete<BUN_SQLITE_BSQLITE_MEDIA, TableType>({
table: "media",
targetId: media.id,
});
}
return {
success: true,
};
}
@@ -0,0 +1,34 @@
import checkUserAccess from "@/src/functions/backend/auth/check-user-access";
import grabMedia from "@/src/functions/backend/db/media/grab-media";
import type { AdminCrudAPIParams } from "@/src/types";
import BunSQLite from "@moduletrace/bun-sqlite";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import _ from "lodash";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const { table, user_types, body, id, query, user } = params;
let final_sql_query = _.merge(query?.sql_query, body?.sql_query) || {};
delete final_sql_query.join;
const can_user_see_all_media = checkUserAccess({
user_types,
includes: ["admin"],
});
if (!can_user_see_all_media.success) {
final_sql_query.query = {
...final_sql_query.query,
user_id: {
value: user.id,
},
};
}
const GET = await grabMedia({ query, user });
return GET;
}
@@ -0,0 +1,29 @@
import type { AdminCrudAPIParams } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import _ from "lodash";
import get from "./get";
import post from "./post";
import put from "./put";
import del from "./del";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const { req, query } = params;
switch (req.method) {
case "GET":
return await get(params);
case "POST":
return await post(params);
case "PUT":
return await put(params);
case "DELETE":
return await del(params);
default:
return {
success: false,
};
}
}
@@ -0,0 +1,36 @@
import type { BUN_SQLITE_BSQLITE_MEDIA } from "@/db/types/db";
import checkUserAccess from "@/src/functions/backend/auth/check-user-access";
import type { AdminCrudAPIParams } from "@/src/types";
import BunSQLite from "@moduletrace/bun-sqlite";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import _ from "lodash";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const { table, body, user_types, user } = params;
let final_insert_data: BUN_SQLITE_BSQLITE_MEDIA[] = [
...(body?.insert_data || []),
].map((d) => ({ ...d, user_id: user.id }));
const is_user_allowed_to_post_any = checkUserAccess({
includes: ["admin"],
user_types,
});
if (!is_user_allowed_to_post_any.success) {
final_insert_data = final_insert_data.map((fid) => ({
...fid,
user_id: user.id,
}));
}
const POST = await BunSQLite.insert({
table,
data: final_insert_data,
update_on_duplicate: body?.update_on_duplicate,
});
return POST;
}
@@ -0,0 +1,40 @@
import checkUserAccess from "@/src/functions/backend/auth/check-user-access";
import type { AdminCrudAPIParams } from "@/src/types";
import BunSQLite from "@moduletrace/bun-sqlite";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import _ from "lodash";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const { table, body, user_types, id, query, user } = params;
if (!body?.update_data) {
return {
success: false,
msg: `No Update Data`,
};
}
const is_user_allowed_to_put = checkUserAccess({
includes: ["admin"],
user_types,
});
let final_sql_query = _.merge(query?.sql_query, body?.sql_query) || {};
let targetId = id;
if (!is_user_allowed_to_put.success) {
final_sql_query = {};
targetId = user.id;
}
const PUT = await BunSQLite.update({
table,
data: body?.update_data,
targetId,
query: final_sql_query,
});
return PUT;
}
@@ -0,0 +1,29 @@
import type { AdminCrudAPIParams } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import {
gatedCrudDelete,
gatedCrudGet,
gatedCrudPost,
gatedCrudPut,
} from "../../(utils)/gated-crud";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
switch (params.req.method) {
case "GET":
return await gatedCrudGet(params);
case "POST":
return await gatedCrudPost(params);
case "PUT":
return await gatedCrudPut(params);
case "DELETE":
return await gatedCrudDelete(params);
default:
return {
success: false,
msg: `Unhandled`,
};
}
}
@@ -0,0 +1,107 @@
import type { BUN_SQLITE_BSQLITE_QUOTES } from "@/db/types/db";
import { SiteData } from "@/src/data/site-data";
import checkUserAccess from "@/src/functions/backend/auth/check-user-access";
import grabNextDocumentSequence from "@/src/functions/backend/db/documents/grab-next-document-sequence";
import type { AdminCrudAPIParams } from "@/src/types";
import BunSQLite from "@moduletrace/bun-sqlite";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import {
gatedCrudDelete,
gatedCrudGet,
gatedCrudPut,
} from "../../(utils)/gated-crud";
function quoteNumberPrefix(): string {
const now = new Date();
return `${SiteData["SiteSlug"].toUpperCase()}Q-${now.getFullYear()}${String(
now.getMonth() + 1,
).padStart(2, "0")}`;
}
async function buildQuoteRows({
rows,
}: {
rows: BUN_SQLITE_BSQLITE_QUOTES[];
}): Promise<BUN_SQLITE_BSQLITE_QUOTES[]> {
const prefix = quoteNumberPrefix();
let next_sequence: number | undefined;
const final_rows: BUN_SQLITE_BSQLITE_QUOTES[] = [];
for (const row of rows) {
const quote_row = { ...row } as BUN_SQLITE_BSQLITE_QUOTES;
if (!quote_row.quote_number) {
next_sequence ??= await grabNextDocumentSequence({
table: "quotes",
column: "quote_number",
prefix,
});
quote_row.quote_number = `${prefix}-${String(next_sequence).padStart(3, "0")}`;
next_sequence += 1;
}
final_rows.push(quote_row);
}
return final_rows;
}
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
switch (params.req.method) {
case "GET":
return await gatedCrudGet(params);
case "POST": {
const { table, body, user_types } = params;
const access = checkUserAccess({
user_types,
includes: ["admin"],
});
if (!access.success) {
return access;
}
if (!body?.insert_data) {
return {
success: false,
msg: `No data to insert`,
};
}
for (let attempt = 0; attempt < 5; attempt++) {
const final_insert_data = await buildQuoteRows({
rows: body.insert_data as BUN_SQLITE_BSQLITE_QUOTES[],
});
try {
return await BunSQLite.insert({
table,
data: final_insert_data,
update_on_duplicate: body.update_on_duplicate,
});
} catch (error: any) {
if (attempt >= 4) {
throw error;
}
}
}
throw new Error(`Couldn't create quote`);
}
case "PUT":
return await gatedCrudPut(params);
case "DELETE":
return await gatedCrudDelete(params);
default:
return {
success: false,
msg: `Unhandled`,
};
}
}
@@ -0,0 +1,87 @@
import type { BUN_SQLITE_BSQLITE_ALL_TYPEDEFS } from "@/db/types/db";
import {
RolesAdminCantUpdate,
SuperAdminOnlyEditableUserTypes,
UneditableUserTypes,
} from "@/src/dict/user-types-dict";
import checkUserAccess from "@/src/functions/backend/auth/check-user-access";
import type { AdminCrudAPIParams, UserType } from "@/src/types";
import BunSQLite from "@moduletrace/bun-sqlite";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import _ from "lodash";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const { table, user_types, body, id, query } = params;
let final_sql_query = _.merge(query?.sql_query, body?.sql_query) || {};
const records_to_delete =
await BunSQLite.select<BUN_SQLITE_BSQLITE_ALL_TYPEDEFS>({
table,
query: final_sql_query,
targetId: id,
});
if (!records_to_delete.payload?.[0]) {
return {
success: false,
msg: `No Records to Delete`,
};
}
const is_user_super_admin = checkUserAccess({
user_types,
});
const admin_includes: UserType[] = ["admin"];
const can_delete_user_type = checkUserAccess({
user_types,
includes: admin_includes,
});
const is_user_admin = Boolean(
user_types.find(
(ut) => ut.user_type && admin_includes.includes(ut.user_type as any),
),
);
if (!can_delete_user_type.success) {
return can_delete_user_type;
}
const ignored_user_types = [...UneditableUserTypes] as UserType[];
if (!is_user_super_admin.success) {
ignored_user_types.push(...SuperAdminOnlyEditableUserTypes);
}
const user_types_to_delete = records_to_delete.payload.filter(
(rec) => !ignored_user_types.includes(rec.user_type as any),
);
for (let i = 0; i < user_types_to_delete.length; i++) {
const user_type_to_delete = user_types_to_delete[i];
if (!user_type_to_delete?.id) continue;
if (
is_user_admin &&
user_type_to_delete.user_type &&
RolesAdminCantUpdate.includes(user_type_to_delete.user_type as any)
) {
continue;
}
await BunSQLite.delete({
table,
targetId: user_type_to_delete.id,
});
}
return {
success: true,
};
}
@@ -0,0 +1,38 @@
import checkUserAccess from "@/src/functions/backend/auth/check-user-access";
import type { AdminCrudAPIParams } from "@/src/types";
import BunSQLite from "@moduletrace/bun-sqlite";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import _ from "lodash";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const { table, user_types, body, id, query, user } = params;
let final_sql_query = _.merge(query?.sql_query, body?.sql_query) || {};
delete final_sql_query.join;
const can_user_see_all_user_types = checkUserAccess({
user_types,
includes: ["admin"],
});
if (!can_user_see_all_user_types.success) {
final_sql_query.query = {
...final_sql_query.query,
user_id: {
value: user.id,
},
};
}
const GET = await BunSQLite.select({
table,
query: final_sql_query,
targetId: id,
count: query?.count || body?.count,
});
return GET;
}
@@ -0,0 +1,29 @@
import type { AdminCrudAPIParams } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import _ from "lodash";
import get from "./get";
import post from "./post";
import put from "./put";
import del from "./del";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const { req, query } = params;
switch (req.method) {
case "GET":
return await get(params);
case "POST":
return await post(params);
case "PUT":
return await put(params);
case "DELETE":
return await del(params);
default:
return {
success: false,
};
}
}
@@ -0,0 +1,61 @@
import {
RolesAdminCantUpdate,
SuperAdminOnlyEditableUserTypes,
UneditableUserTypes,
} from "@/src/dict/user-types-dict";
import checkUserAccess from "@/src/functions/backend/auth/check-user-access";
import type { AdminCrudAPIParams, UserType } from "@/src/types";
import BunSQLite from "@moduletrace/bun-sqlite";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import _ from "lodash";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const { table, body, user_types } = params;
const is_user_super_admin = checkUserAccess({
user_types,
});
const ignored_user_types = [...UneditableUserTypes] as UserType[];
if (!is_user_super_admin.success) {
ignored_user_types.push(...SuperAdminOnlyEditableUserTypes);
}
let final_insert_data = [...(body?.insert_data || [])].filter(
(i) => !ignored_user_types.includes(i.user_type as any),
);
const admin_includes: UserType[] = ["admin"];
const is_user_allowed_to_post = checkUserAccess({
includes: admin_includes,
user_types,
});
if (!is_user_allowed_to_post.success) {
return is_user_allowed_to_post;
}
const is_user_admin = Boolean(
user_types.find(
(ut) => ut.user_type && admin_includes.includes(ut.user_type as any),
),
);
if (is_user_admin) {
final_insert_data = final_insert_data.filter(
(d) => d.user_type && !RolesAdminCantUpdate.includes(d.user_type as any),
);
}
const POST = await BunSQLite.insert({
table,
data: final_insert_data,
update_on_duplicate: body?.update_on_duplicate,
});
return POST;
}
@@ -0,0 +1,14 @@
import type { AdminCrudAPIParams } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import _ from "lodash";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const { table, body, user_types, id, query } = params;
return {
success: false,
msg: `Can't update user_type. Can only delete and insert.`,
};
}
@@ -0,0 +1,11 @@
/**
* The only `users` columns writable through the CRUD API. Anything else
* (`id`, `archived`, timestamps) is stripped from insert/update payloads.
*/
export const UserUpdatableFields = [
"first_name",
"last_name",
"email",
"username",
"bio",
] as const;
@@ -0,0 +1,52 @@
import type { BUN_SQLITE_BSQLITE_ALL_TYPEDEFS } from "@/db/types/db";
import checkUserAccess from "@/src/functions/backend/auth/check-user-access";
import checkAllowedJoins from "@/src/functions/backend/db/check-allowed-joins";
import grabUsers from "@/src/functions/backend/db/users/grab-users";
import type { AdminCrudAPIParams, ApiReqParams, TableType } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import _ from "lodash";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const { req, table, user_types, body, id, query, user } = params;
let final_sql_query = _.merge(query?.sql_query, body?.sql_query) || {};
let final_query =
_.merge<
ApiReqParams<BUN_SQLITE_BSQLITE_ALL_TYPEDEFS>,
ApiReqParams<BUN_SQLITE_BSQLITE_ALL_TYPEDEFS>
>(query || {}, { sql_query: final_sql_query }) || {};
const allowed_joins: TableType[] = ["user_types", "media"];
const check_allowed_joins = checkAllowedJoins({
joins: allowed_joins,
query: final_sql_query,
});
if (!check_allowed_joins.success) {
return check_allowed_joins;
}
const can_user_see_all_users = checkUserAccess({
user_types,
includes: ["admin"],
});
if (!can_user_see_all_users.success) {
final_sql_query.query = {
...final_sql_query.query,
user_id: {
value: user.id,
},
};
}
const GET = await grabUsers({
query: final_query,
user_id: id,
});
return GET;
}
@@ -0,0 +1,31 @@
import type { AdminCrudAPIParams } from "@/src/types";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import _ from "lodash";
import get from "./get";
import post from "./post";
import put from "./put";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const { req } = params;
switch (req.method) {
case "GET":
return await get(params);
case "POST":
return await post(params);
case "PUT":
return await put(params);
case "DELETE":
return {
success: false,
msg: `Can't delete any user`,
};
default:
return {
success: false,
};
}
}
@@ -0,0 +1,43 @@
import checkUserAccess from "@/src/functions/backend/auth/check-user-access";
import sanitizeUserBio from "@/src/functions/backend/db/users/sanitize-user-bio";
import type { AdminCrudAPIParams } from "@/src/types";
import BunSQLite from "@moduletrace/bun-sqlite";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import _ from "lodash";
import { UserUpdatableFields } from "./(data)/user-updatable-fields";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const { table, body, user_types } = params;
const is_user_allowed_to_post = checkUserAccess({
includes: ["admin"],
user_types,
});
if (!is_user_allowed_to_post.success) {
return is_user_allowed_to_post;
}
const final_insert_data = (body?.insert_data || []).map((row) =>
sanitizeUserBio({
user: _.pick(row, UserUpdatableFields),
}),
);
if (!final_insert_data[0]) {
return {
success: false,
msg: `No data to insert`,
};
}
const POST = await BunSQLite.insert({
table,
data: final_insert_data,
update_on_duplicate: body?.update_on_duplicate,
});
return POST;
}
@@ -0,0 +1,63 @@
import type { AdminCrudAPIParams } from "@/src/types";
import BunSQLite from "@moduletrace/bun-sqlite";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import _ from "lodash";
import canUpdateUser from "@/src/utils/rbac/can-update-user";
import grabUserTypes from "@/src/functions/backend/db/users/grab-user-types";
import sanitizeUserBio from "@/src/functions/backend/db/users/sanitize-user-bio";
import { UserUpdatableFields } from "./(data)/user-updatable-fields";
export default async function (
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const { table, body, user_types, id, user } = params;
if (!body?.update_data) {
return {
success: false,
msg: `No Update Data`,
};
}
const update_data = sanitizeUserBio({
user: _.pick(body.update_data, UserUpdatableFields),
});
const update_fields = Object.keys(update_data);
if (!update_fields[0]) {
return {
success: false,
msg: `No updatable fields passed`,
};
}
const target_user_id = id || user.id;
const target_user_types =
String(target_user_id) === String(user.id)
? user_types
: await grabUserTypes({ user_id: target_user_id });
const can_update = canUpdateUser({
actor_user_types: user_types,
actor_user_id: user.id,
target_user_id,
target_user_types,
update_fields,
});
if (!can_update) {
return {
success: false,
msg: `Unauthorized`,
};
}
const PUT = await BunSQLite.update({
table,
data: update_data,
targetId: target_user_id,
});
return PUT;
}
@@ -0,0 +1,100 @@
import checkUserAccess from "@/src/functions/backend/auth/check-user-access";
import type { AdminCrudAPIParams } from "@/src/types";
import BunSQLite from "@moduletrace/bun-sqlite";
import type { APIResponseObject } from "@moduletrace/bunext/types";
import _ from "lodash";
/**
* # Shared admin-gated CRUD actions for app tables.
* Gates every action on `admin` (or super_admin) then performs the
* standard table operation. Keeps table folders thin.
*/
export async function gatedCrudGet(
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const { table, body, id, query, user_types } = params;
const access = checkUserAccess({ user_types, includes: ["admin"] });
if (!access.success) {
return access;
}
const final_sql_query = _.merge(query?.sql_query, body?.sql_query);
return await BunSQLite.select({
table,
query: final_sql_query,
targetId: id,
count: query?.count || body?.count,
});
}
export async function gatedCrudPost(
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const { table, body, user_types } = params;
const access = checkUserAccess({ user_types, includes: ["admin"] });
if (!access.success) {
return access;
}
if (!body?.insert_data) {
return {
success: false,
msg: `No data to insert`,
};
}
return await BunSQLite.insert({
table,
data: [...body.insert_data],
update_on_duplicate: body.update_on_duplicate,
});
}
export async function gatedCrudPut(
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const { table, body, id, query, user_types } = params;
const access = checkUserAccess({ user_types, includes: ["admin"] });
if (!access.success) {
return access;
}
if (!body?.update_data) {
return {
success: false,
msg: `No data to update`,
};
}
return await BunSQLite.update({
table,
data: body.update_data,
query: _.merge(query?.sql_query, body?.sql_query),
targetId: id,
});
}
export async function gatedCrudDelete(
params: AdminCrudAPIParams,
): Promise<APIResponseObject> {
const { table, body, id, query, user_types } = params;
const access = checkUserAccess({ user_types, includes: ["admin"] });
if (!access.success) {
return access;
}
return await BunSQLite.delete({
table,
query: _.merge(query?.sql_query, body?.sql_query),
targetId: id,
});
}
+146
View File
@@ -0,0 +1,146 @@
import _ from "lodash";
import users from "./(tables)/users";
import userTypes from "./(tables)/user_types";
import media from "./(tables)/media";
import letters from "./(tables)/letters";
import emails from "./(tables)/emails";
import emailAttachments from "./(tables)/email_attachments";
import invoices from "./(tables)/invoices";
import invoiceItems from "./(tables)/invoice_items";
import quotes from "./(tables)/quotes";
import quoteItems from "./(tables)/quote_items";
import invoiceQuotes from "./(tables)/invoice_quotes";
import get from "./(functions)/get";
import put from "./(functions)/put";
import del from "./(functions)/delete";
import post from "./(functions)/post";
import type {
APIResponseObject,
BunextAPIRouteHandler,
} from "@moduletrace/bunext/types";
import type { AdminCrudAPIParams, ApiReqParams, TableType } from "@/src/types";
import userAuth from "@/src/functions/backend/auth/user-auth";
import checkUserAccess from "@/src/functions/backend/auth/check-user-access";
import isReadOnlyUser from "@/src/utils/rbac/is-read-only-user";
import { PrivateTables } from "@/src/dict/private-tables";
import safeErrorMessage from "@/src/utils/safe-error-message";
export const handler: BunextAPIRouteHandler<APIResponseObject> = async (
params,
) => {
const req = params.req;
const body = params.body as ApiReqParams;
const query = params.query as ApiReqParams;
const { user, user_types } = await userAuth({ req });
if (!user?.logged_in_status) {
return {
success: false,
msg: `Unauthorized`,
logoutUser: true,
};
}
if (!user_types?.[0]) {
return {
success: false,
msg: `User types not found`,
logoutUser: true,
};
}
try {
const [table, id] = params.query.paths.split("/") as [
TableType,
string | number,
];
if (PrivateTables.includes(table)) {
return {
success: false,
msg: `Unauthorized`,
};
}
if (isReadOnlyUser({ user_types }) && !req.method.match(/get/)) {
const is_self_user_profile_update =
req.method == "PUT" &&
table == "users" &&
(!id || String(id) === String(user.id));
if (!is_self_user_profile_update) {
throw new Error(`This user can only read records.`);
}
}
const crud_params: AdminCrudAPIParams = {
table,
id,
user: user || undefined,
user_types: user_types || [],
body,
query,
req,
};
switch (table) {
case "users":
return await users(crud_params);
case "user_types":
return await userTypes(crud_params);
case "media":
return await media(crud_params);
case "letters":
return await letters(crud_params);
case "emails":
return await emails(crud_params);
case "email_attachments":
return await emailAttachments(crud_params);
case "invoices":
return await invoices(crud_params);
case "invoice_items":
return await invoiceItems(crud_params);
case "quotes":
return await quotes(crud_params);
case "quote_items":
return await quoteItems(crud_params);
case "invoice_quotes":
return await invoiceQuotes(crud_params);
default:
break;
}
const is_super_admin = checkUserAccess({ user_types });
if (!is_super_admin.success) {
return {
success: false,
msg: `Unauthorized`,
};
}
switch (req.method) {
case "GET":
return await get(crud_params);
case "POST":
return await post(crud_params);
case "PUT":
return await put(crud_params);
case "DELETE":
return await del(crud_params);
default:
return {
success: false,
msg: `Unhandled`,
};
}
} catch (error: unknown) {
return {
success: false,
msg: safeErrorMessage({ error }),
};
}
};