This commit is contained in:
@@ -78,4 +78,21 @@ export async function registerAdminExportRoutes(app, options) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.get('/exports/jobs/:id/download', async (request, reply) => {
|
||||||
|
const job = await adminExportService.getExportJob({
|
||||||
|
sellerId: request.admin.sellerId,
|
||||||
|
jobId: request.params.id,
|
||||||
|
});
|
||||||
|
if (!job) {
|
||||||
|
throw createApplicationError('EXPORT_JOB_NOT_FOUND', 'Export job was not found.', 404);
|
||||||
|
}
|
||||||
|
if (job.status !== 'COMPLETED' || !Array.isArray(job.output)) {
|
||||||
|
throw createApplicationError('EXPORT_NOT_READY', 'Export is not ready for download.', 409);
|
||||||
|
}
|
||||||
|
return reply
|
||||||
|
.type('application/json; charset=utf-8')
|
||||||
|
.header('content-disposition', 'attachment; filename="inventory.json"')
|
||||||
|
.send(JSON.stringify(job.output));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,4 +125,9 @@ test('enqueues and polls an asynchronous inventory export', async (testContext)
|
|||||||
assert.equal(enqueue.json().data.id, 'job-1');
|
assert.equal(enqueue.json().data.id, 'job-1');
|
||||||
assert.equal(poll.statusCode, 200);
|
assert.equal(poll.statusCode, 200);
|
||||||
assert.deepEqual(poll.json().data.output, [{ id: 'item-1' }]);
|
assert.deepEqual(poll.json().data.output, [{ id: 'item-1' }]);
|
||||||
|
|
||||||
|
const download = await app.inject({ method: 'GET', url: '/api/v1/admin/exports/jobs/job-1/download' });
|
||||||
|
assert.equal(download.statusCode, 200);
|
||||||
|
assert.equal(download.headers['content-disposition'], 'attachment; filename="inventory.json"');
|
||||||
|
assert.deepEqual(JSON.parse(download.body), [{ id: 'item-1' }]);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ const inventory = ref([]);
|
|||||||
const selectedInventoryIds = ref([]);
|
const selectedInventoryIds = ref([]);
|
||||||
const inventoryTags = ref("");
|
const inventoryTags = ref("");
|
||||||
const exportStatus = ref("");
|
const exportStatus = ref("");
|
||||||
|
const exportJobId = ref("");
|
||||||
const listings = ref([]);
|
const listings = ref([]);
|
||||||
const sales = ref([]);
|
const sales = ref([]);
|
||||||
const acquisitions = ref([]);
|
const acquisitions = ref([]);
|
||||||
@@ -262,6 +263,7 @@ async function enqueueInventoryExport() {
|
|||||||
exportStatus.value = "Starting export...";
|
exportStatus.value = "Starting export...";
|
||||||
try {
|
try {
|
||||||
const job = await enqueueAdminInventoryExport(csrfToken());
|
const job = await enqueueAdminInventoryExport(csrfToken());
|
||||||
|
exportJobId.value = job.id;
|
||||||
const completed = await getAdminInventoryExportJob(job.id);
|
const completed = await getAdminInventoryExportJob(job.id);
|
||||||
exportStatus.value = `Export ${completed.status.toLowerCase()}.`;
|
exportStatus.value = `Export ${completed.status.toLowerCase()}.`;
|
||||||
} catch (requestError) {
|
} catch (requestError) {
|
||||||
@@ -542,6 +544,14 @@ onMounted(load);
|
|||||||
Queue JSON export
|
Queue JSON export
|
||||||
</button>
|
</button>
|
||||||
<span v-if="exportStatus" role="status">{{ exportStatus }}</span>
|
<span v-if="exportStatus" role="status">{{ exportStatus }}</span>
|
||||||
|
<a
|
||||||
|
v-if="exportJobId && exportStatus === 'Export completed.'"
|
||||||
|
class="button button--secondary"
|
||||||
|
:href="`/api/v1/admin/exports/jobs/${exportJobId}/download`"
|
||||||
|
download="inventory.json"
|
||||||
|
>
|
||||||
|
Download queued export
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p v-if="isLoading" class="state state--loading" role="status">
|
<p v-if="isLoading" class="state state--loading" role="status">
|
||||||
Loading inventory...
|
Loading inventory...
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ GET /api/v1/admin/exports/inventory.csv
|
|||||||
GET /api/v1/admin/exports/inventory.xls
|
GET /api/v1/admin/exports/inventory.xls
|
||||||
POST /api/v1/admin/exports/inventory.json/jobs
|
POST /api/v1/admin/exports/inventory.json/jobs
|
||||||
GET /api/v1/admin/exports/jobs/:id
|
GET /api/v1/admin/exports/jobs/:id
|
||||||
|
GET /api/v1/admin/exports/jobs/:id/download
|
||||||
GET /api/v1/admin/settings
|
GET /api/v1/admin/settings
|
||||||
PATCH /api/v1/admin/settings
|
PATCH /api/v1/admin/settings
|
||||||
|
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ Remaining roadmap areas are bulk status actions, asynchronous large exports, app
|
|||||||
- Admin inventory Excel-compatible export is implemented at `GET /api/v1/admin/exports/inventory.xls` using the XML workbook format and seller-scoped admin DTOs.
|
- Admin inventory Excel-compatible export is implemented at `GET /api/v1/admin/exports/inventory.xls` using the XML workbook format and seller-scoped admin DTOs.
|
||||||
- Seller-scoped admin settings are implemented through `GET/PATCH /api/v1/admin/settings` with strict category allowlists and server-side partial-update merging.
|
- Seller-scoped admin settings are implemented through `GET/PATCH /api/v1/admin/settings` with strict category allowlists and server-side partial-update merging.
|
||||||
- Large inventory JSON exports can be enqueued through `POST /api/v1/admin/exports/inventory.json/jobs` and polled through `GET /api/v1/admin/exports/jobs/:id`; the worker owns claim, completion, and failure processing.
|
- Large inventory JSON exports can be enqueued through `POST /api/v1/admin/exports/inventory.json/jobs` and polled through `GET /api/v1/admin/exports/jobs/:id`; the worker owns claim, completion, and failure processing.
|
||||||
|
- Completed asynchronous inventory JSON exports can be downloaded through `GET /api/v1/admin/exports/jobs/:id/download`.
|
||||||
- PostgreSQL restore verification and image snapshot restore scripts are implemented and covered by shell tests as part of the deployment drill workflow.
|
- PostgreSQL restore verification and image snapshot restore scripts are implemented and covered by shell tests as part of the deployment drill workflow.
|
||||||
- `npm audit --omit=dev` currently reports zero production vulnerabilities. Re-run the audit after dependency changes and review development-dependency findings separately before production releases.
|
- `npm audit --omit=dev` currently reports zero production vulnerabilities. Re-run the audit after dependency changes and review development-dependency findings separately before production releases.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user