proof-of-concept endpointow dla directusa

This commit is contained in:
2026-04-30 16:38:16 +02:00
parent e03257f6fb
commit 045c65c363
4 changed files with 8811 additions and 0 deletions

View File

@@ -0,0 +1,128 @@
export default (router) => {
router.get('/', (req, res) => res.send('Hello, World!'));
};
export default {
id: 'mayo-api',
handler: (router, { database }) => {
router.get('/products/by-order/:orderNumber/:orderYear/:productIndex/timeline', async (req, res) => {
if (!req.accountability?.user) {
return res.status(401).json({ error: 'Unauthorized' })
}
const { orderNumber, orderYear, productIndex } = req.params
const normalizedOrderNumber = Number(orderNumber)
const normalizedOrderYear = Number(orderYear)
const normalizedProductIndex = Number(productIndex)
if (
Number.isNaN(normalizedOrderNumber) ||
Number.isNaN(normalizedOrderYear) ||
Number.isNaN(normalizedProductIndex)
) {
return res.status(400).json({ error: 'Invalid order code' })
}
const orderProduct = await database('mayo_order_products as op')
.select(
'op.id as order_product_id',
'op.product_id',
'op.produc_order_idx',
'o.id as order_id',
'o.order_number',
'o.order_year',
'p.note',
'm.id as model_id',
'm.name as model_name'
)
.join('mayo_orders as o', 'op.order_id', 'o.id')
.join('mayo_products as p', 'op.product_id', 'p.id')
.leftJoin('mayo_models as m', 'p.model_id', 'm.id')
.where('o.order_number', normalizedOrderNumber)
.where('o.order_year', normalizedOrderYear)
.where('op.produc_order_idx', normalizedProductIndex)
.first()
if (!orderProduct) {
return res.status(404).json({ error: 'Product not found' })
}
const events = await database('mayo_events as e')
.select(
'e.id',
'e.ordinal',
'e.event_kind',
'e.event_date',
'p.id as part_id',
'p.part_type'
)
.join('mayo_parts as p', 'e.part_id', 'p.id')
.where('p.product_id', orderProduct.product_id)
.orderBy('e.ordinal', 'asc')
const eventIds = events.map((event) => event.id)
const [operations, notes, photos] = await Promise.all([
database('mayo_event_operations as eo')
.select(
'eo.event_id',
'o.id as operation_id',
'o.name',
'o.description'
)
.leftJoin('mayo_operations as o', 'eo.operation_id', 'o.id')
.whereIn('eo.event_id', eventIds),
database('mayo_event_notes')
.select('event_id', 'note_type', 'note')
.whereIn('event_id', eventIds),
database('mayo_event_photos')
.select('event_id', 'photo_url')
.whereIn('event_id', eventIds),
])
const operationsByEventId = new Map(operations.map((operation) => [operation.event_id, operation]))
const notesByEventId = new Map(notes.map((note) => [note.event_id, note]))
const photosByEventId = photos.reduce((acc, photo) => {
if (!acc.has(photo.event_id)) acc.set(photo.event_id, [])
acc.get(photo.event_id).push({
file_id: photo.photo_url,
url: photo.photo_url ? `/assets/${photo.photo_url}` : null,
})
return acc
}, new Map())
const timeline = events.map((event) => ({
id: event.id,
ordinal: event.ordinal,
kind: event.event_kind,
date: event.event_date,
part: {
id: event.part_id,
type: event.part_type,
},
operation: operationsByEventId.get(event.id) ?? null,
note: notesByEventId.get(event.id) ?? null,
photos: photosByEventId.get(event.id) ?? [],
}))
return res.json({
product: {
id: orderProduct.product_id,
order_code: `${String(orderProduct.order_number).padStart(4, '0')}/${orderProduct.order_year}/${orderProduct.produc_order_idx}`,
model: {
id: orderProduct.model_id,
name: orderProduct.model_name,
},
note: orderProduct.note,
},
timeline,
})
})
},
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,30 @@
{
"name": "directus-extension-mayo-api",
"description": "Please enter a description for your extension",
"icon": "extension",
"version": "1.0.1",
"keywords": [
"directus",
"directus-extension",
"directus-extension-endpoint"
],
"type": "module",
"files": [
"dist"
],
"directus:extension": {
"type": "endpoint",
"path": "dist/index.js",
"source": "src/index.js",
"host": "^10.10.0"
},
"scripts": {
"build": "directus-extension build",
"dev": "directus-extension build -w --no-minify",
"link": "directus-extension link",
"validate": "directus-extension validate"
},
"devDependencies": {
"@directus/extensions-sdk": "17.1.3"
}
}

View File

@@ -0,0 +1,252 @@
export default {
id: "mayo-api",
handler: (router, context) => {
const { services, getSchema, database } = context;
const { ItemsService } = services;
router.get("/products", async (req, res) => {
const productsService = new ItemsService("mayo_products", {
schema: await getSchema(),
accountability: req.accountability,
});
const partsService = new ItemsService("mayo_parts", {
schema: await getSchema(),
accountability: req.accountability,
});
try {
const products = await productsService.readByQuery({
fields: [
"id",
"note",
"model_id.name",
"model_id.strings_count",
"model_id.scale",
],
limit: -1,
});
const productIds = products.map((product) => product.id);
const parts = await partsService.readByQuery({
fields: [
"id",
"product_id",
"part_type",
"top_color_id.name",
"back_color_id.name",
"top_finish",
"back_finish",
],
filter: {
product_id: {
_in: productIds,
},
},
limit: -1,
});
const partsByProductId = new Map();
for (const part of parts) {
const productId =
typeof part.product_id === "object"
? part.product_id.id
: part.product_id;
if (!partsByProductId.has(productId)) {
partsByProductId.set(productId, []);
}
partsByProductId.get(productId).push(part);
}
const response = products.map((product) => ({
...product,
parts: partsByProductId.get(product.id) ?? [],
}));
res.json(response);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
router.get("/products-new", async (req, res) => {
try {
const products = await database("mayo_products as product")
.select(
"product.id",
"product.note",
"model.name as model_name",
"model.strings_count as model_strings_count",
"model.scale as model_scale",
)
.leftJoin("mayo_models as model", "product.model_id", "model.id");
const productIds = products.map((product) => product.id);
const parts = productIds.length
? await database("mayo_parts as part")
.select(
"part.id",
"part.product_id",
"part.part_type",
"part.top_finish",
"part.back_finish",
"top_color.name as top_color_name",
"back_color.name as back_color_name",
)
.leftJoin(
"mayo_colors as top_color",
"part.top_color_id",
"top_color.id",
)
.leftJoin(
"mayo_colors as back_color",
"part.back_color_id",
"back_color.id",
)
.whereIn("part.product_id", productIds)
: [];
const partsByProductId = new Map();
for (const part of parts) {
if (!partsByProductId.has(part.product_id)) {
partsByProductId.set(part.product_id, []);
}
partsByProductId.get(part.product_id).push({
id: part.id,
product_id: part.product_id,
part_type: part.part_type,
top_color_id: {
name: part.top_color_name,
},
back_color_id: {
name: part.back_color_name,
},
top_finish: part.top_finish,
back_finish: part.back_finish,
});
}
const response = products.map((product) => ({
id: product.id,
note: product.note,
model_id: {
name: product.model_name,
strings_count: product.model_strings_count,
scale: product.model_scale,
},
parts: partsByProductId.get(product.id) ?? [],
}));
res.json(response);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
router.get("/orders", async (req, res) => {
const itemsService = new ItemsService("mayo_order_products", {
schema: await getSchema(),
accountability: req.accountability,
});
try {
const items = await itemsService.readByQuery({
fields: [
"id",
"product_order_index",
"order_id.order_number",
"order_id.order_year",
"order_id.client_id.name",
"product_id.id",
"product_id.note",
"product_id.model_id.name",
"product_id.model_id.strings_count",
"product_id.model_id.scale",
],
limit: -1,
});
res.json(items);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
router.get("/my-products", async (req, res) => {
try {
const products = await database("mayo_products as mp")
.select(
"mp.id",
"mm.name as model",
"mo.order_number",
"mo.order_year",
"mop.product_order_index as order_index",
)
.leftJoin("mayo_order_products as mop", "mop.product_id", "mp.id")
.leftJoin("mayo_orders as mo", "mo.id", "mop.order_id")
.leftJoin("mayo_models as mm", "mm.id", "mp.model_id");
const productIds = products.map((product) => product.id);
const parts = productIds.length
? await database("mayo_parts as part")
.select("part.product_id", "part.part_type")
.whereIn("part.product_id", productIds)
: [];
const partTypesByProductId = new Map();
for (const part of parts) {
if (!partTypesByProductId.has(part.product_id)) {
partTypesByProductId.set(part.product_id, []);
}
if (part.part_type) {
partTypesByProductId.get(part.product_id).push(part.part_type);
}
}
const response = products.map((product) => ({
...product,
part_types: partTypesByProductId.get(product.id) ?? [],
}));
res.json(response);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
router.get("/my-products2", async (req, res) => {
const productsService = new ItemsService("mayo_products", {
schema: await getSchema(),
accountability: req.accountability,
});
const partsService = new ItemsService("mayo_parts", {
schema: await getSchema(),
accountability: req.accountability,
});
try {
const products = await productsService.readByQuery({
fields: [
"id",
"note",
"model_id.name",
"model_id.strings_count",
"model_id.scale",
],
limit: -1,
});
res.json(products);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
},
};