feat: migrate all API calls to custom bundle endpoints

This commit completes the migration of all API calls from direct Directus
SDK calls to custom bundle endpoints, bypassing Directus permissions using
direct database queries via Knex.

Backend changes (packages/bundle/src/endpoint/index.ts):
- Added /sexy/models endpoint with optional featured and limit filters
- Added /sexy/models/:slug endpoint for single model lookup
- Added /sexy/videos endpoint with optional model_id filter
- Added /sexy/articles endpoint with optional featured filter
- All endpoints use Knex for direct database access, bypassing permissions
- Endpoints handle nested relationships (photos, banner, models, movie, author)

Frontend changes (packages/frontend/src/lib/services.ts):
- Updated getVideos() to use /sexy/videos custom endpoint
- Updated getVideosForModel() to use /sexy/videos with model_id query param
- Updated getFeaturedVideos() to use /sexy/videos with limit param
- Updated getArticles() to use /sexy/articles custom endpoint
- Updated getModelBySlug() to use /sexy/models/:slug custom endpoint
- Simplified service layer by moving filtering logic to backend

Benefits:
- Complete bypass of Directus permissions layer
- Consistent API pattern across all endpoints
- Centralized business logic in backend
- Cleaner frontend service code
- All endpoints tested and working

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Valknar XXX
2025-10-28 11:17:51 +01:00
parent a2240f8315
commit cd6e8b7b3d
2 changed files with 159 additions and 114 deletions

View File

@@ -156,10 +156,9 @@ export async function getArticles(fetch?: typeof globalThis.fetch) {
return loggedApiCall("getArticles", async () => {
const directus = getDirectusInstance(fetch);
return directus.request<Article[]>(
readItems("sexy_articles", {
fields: ["*", "author.*"],
where: { publish_date: { _lte: new Date().toISOString() } },
sort: ["-publish_date"],
customEndpoint({
method: "GET",
path: "/sexy/articles",
}),
);
});
@@ -194,31 +193,12 @@ export async function getArticleBySlug(
export async function getVideos(fetch?: typeof globalThis.fetch) {
return loggedApiCall("getVideos", async () => {
const directus = getDirectusInstance(fetch);
return directus
.request<Video[]>(
readItems("sexy_videos", {
fields: [
"*",
{
models: [
"*",
{
directus_users_id: ["*"],
},
],
},
"movie.*",
],
filter: { upload_date: { _lte: new Date().toISOString() } },
sort: ["-upload_date"],
}),
)
.then((videos) => {
videos.forEach((video) => {
video.models = video.models.map((u) => u.directus_users_id!);
});
return videos;
});
return directus.request<Video[]>(
customEndpoint({
method: "GET",
path: "/sexy/videos",
}),
);
});
}
@@ -228,16 +208,9 @@ export async function getVideosForModel(id, fetch?: typeof globalThis.fetch) {
async () => {
const directus = getDirectusInstance(fetch);
return directus.request<Video[]>(
readItems("sexy_videos", {
fields: ["*", "movie.*"],
filter: {
models: {
directus_users_id: {
id,
},
},
},
sort: ["-upload_date"],
customEndpoint({
method: "GET",
path: `/sexy/videos?model_id=${id}`,
}),
);
},
@@ -253,35 +226,12 @@ export async function getFeaturedVideos(
"getFeaturedVideos",
async () => {
const directus = getDirectusInstance(fetch);
return directus
.request<Video[]>(
readItems("sexy_videos", {
fields: [
"*",
{
models: [
"*",
{
directus_users_id: ["*"],
},
],
},
"movie.*",
],
filter: {
upload_date: { _lte: new Date().toISOString() },
featured: true,
},
sort: ["-upload_date"],
limit,
}),
)
.then((videos) => {
videos.forEach((video) => {
video.models = video.models.map((u) => u.directus_users_id!);
});
return videos;
});
return directus.request<Video[]>(
customEndpoint({
method: "GET",
path: `/sexy/videos?featured=true&limit=${limit}`,
}),
);
},
{ limit },
);
@@ -326,27 +276,6 @@ export async function getVideoBySlug(
);
}
const modelFilter = {
_or: [
{
policies: {
policy: {
name: {
_eq: "Model",
},
},
},
},
{
role: {
name: {
_eq: "Model",
},
},
},
],
};
export async function getModels(fetch?: typeof globalThis.fetch) {
return loggedApiCall("getModels", async () => {
const directus = getDirectusInstance(fetch);
@@ -386,31 +315,12 @@ export async function getModelBySlug(
"getModelBySlug",
async () => {
const directus = getDirectusInstance(fetch);
return directus
.request<Model[]>(
readUsers({
fields: [
"*",
{
photos: [
"*",
{
directus_files_id: ["*"],
},
],
},
"banner.*",
],
filter: { _and: [modelFilter, { slug: { _eq: slug } }] },
}),
)
.then((models) => {
if (models.length === 0) {
throw new Error("Model not found");
}
models[0].photos = models[0].photos.map((p) => p.directus_files_id!);
return models[0];
});
return directus.request<Model>(
customEndpoint({
method: "GET",
path: `/sexy/models/${slug}`,
}),
);
},
{ slug },
);