fix: basic

This commit is contained in:
Oleg Proskurin 2025-11-26 00:11:48 +07:00
parent 8623442157
commit 6803a23aa3
3 changed files with 43 additions and 16 deletions

View File

@ -13,7 +13,7 @@ import { validateAndNormalizePagination } from '@/utils/validators';
import { buildPaginatedResponse } from '@/utils/helpers';
import { toImageResponse } from '@/types/responses';
import { db } from '@/db';
import { flows } from '@banatie/database';
import { flows, type Image } from '@banatie/database';
import { eq } from 'drizzle-orm';
import type {
UploadImageResponse,
@ -704,13 +704,14 @@ imagesRouter.put(
);
/**
* Assign a project-scoped alias to an image
* Assign or remove a project-scoped alias from an image
*
* Sets or updates the project-scoped alias for an image:
* - Alias must start with @ symbol
* Sets, updates, or removes the project-scoped alias for an image:
* - Alias must start with @ symbol (when assigning)
* - Must be unique within the project
* - Replaces existing alias if image already has one
* - Used for alias resolution in generations and CDN access
* - Set alias to null to remove existing alias
*
* This is a dedicated endpoint introduced in Section 6.1 to separate
* alias assignment from general metadata updates.
@ -722,24 +723,30 @@ imagesRouter.put(
* @param {string} req.params.id_or_alias - Image ID (UUID) or alias (@-prefixed)
* @param {string} [req.query.flowId] - Flow ID for flow-scoped alias resolution
* @param {object} req.body - Request body
* @param {string} req.body.alias - Project-scoped alias (e.g., "@hero-bg")
* @param {string|null} req.body.alias - Project-scoped alias (e.g., "@hero-bg") or null to remove
*
* @returns {UpdateImageResponse} 200 - Updated image with new alias
* @returns {UpdateImageResponse} 200 - Updated image with new/removed alias
* @returns {object} 404 - Image not found or access denied
* @returns {object} 400 - Missing or invalid alias
* @returns {object} 400 - Invalid alias format
* @returns {object} 401 - Missing or invalid API key
* @returns {object} 409 - Alias already exists
*
* @throws {Error} IMAGE_NOT_FOUND - Image does not exist
* @throws {Error} VALIDATION_ERROR - Alias is required
* @throws {Error} VALIDATION_ERROR - Invalid alias format
* @throws {Error} ALIAS_CONFLICT - Alias already assigned to another image
*
* @example UUID identifier
* @example Assign alias
* PUT /api/v1/images/550e8400-e29b-41d4-a716-446655440000/alias
* {
* "alias": "@hero-background"
* }
*
* @example Remove alias
* PUT /api/v1/images/550e8400-e29b-41d4-a716-446655440000/alias
* {
* "alias": null
* }
*
* @example Project-scoped alias identifier
* PUT /api/v1/images/@old-hero/alias
* {
@ -762,11 +769,12 @@ imagesRouter.put(
const { flowId } = req.query;
const { alias } = req.body;
if (!alias || typeof alias !== 'string') {
// Validate: alias must be null (to remove) or a non-empty string
if (alias !== null && (typeof alias !== 'string' || alias.trim() === '')) {
res.status(400).json({
success: false,
error: {
message: 'Alias is required and must be a string',
message: 'Alias must be null (to remove) or a non-empty string',
code: 'VALIDATION_ERROR',
},
});
@ -815,7 +823,16 @@ imagesRouter.put(
return;
}
const updated = await service.assignProjectAlias(imageId, alias);
// Either remove alias (null) or assign new one (override behavior per Section 5.2)
let updated: Image;
if (alias === null) {
// Remove alias
updated = await service.update(imageId, { alias: null });
} else {
// Reassign alias (clears from any existing image, then assigns to this one)
await service.reassignProjectAlias(alias, imageId, image.projectId);
updated = (await service.getById(imageId))!;
}
res.json({
success: true,

View File

@ -89,7 +89,7 @@ export class ImageService {
async update(
id: string,
updates: {
alias?: string;
alias?: string | null;
focalPoint?: { x: number; y: number };
meta?: Record<string, unknown>;
}

View File

@ -139,11 +139,21 @@ export async function uploadFile(
): Promise<any> {
const formData = new FormData();
// Read file
// Read file and detect MIME type from extension
const fs = await import('fs/promises');
const path = await import('path');
const fileBuffer = await fs.readFile(filepath);
const blob = new Blob([fileBuffer]);
formData.append('file', blob, 'test-image.png');
const ext = path.extname(filepath).toLowerCase();
const mimeTypes: Record<string, string> = {
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.webp': 'image/webp',
};
const mimeType = mimeTypes[ext] || 'application/octet-stream';
const filename = path.basename(filepath);
const blob = new Blob([fileBuffer], { type: mimeType });
formData.append('file', blob, filename);
// Add other fields
for (const [key, value] of Object.entries(fields)) {