forked from Yara724/api
YARA-898
This commit is contained in:
@@ -14,6 +14,34 @@ import { ClientDbService } from "./entities/db-service/client.db.service";
|
||||
*/
|
||||
export const DEFAULT_CAR_BODY_ACCIDENT_MAX_AGE_DAYS = 7;
|
||||
|
||||
/**
|
||||
* Media kinds we enforce upload-size bounds for. Each kind has its own
|
||||
* default window (see {@link DEFAULT_MEDIA_LIMITS}) and an optional
|
||||
* per-client override under `settings.media.<kind>`.
|
||||
*/
|
||||
export type MediaKind = "video" | "image" | "voice";
|
||||
|
||||
export interface MediaLimits {
|
||||
/** Inclusive lower bound. `0` allows any size from zero up. */
|
||||
minBytes: number;
|
||||
/** Inclusive upper bound. */
|
||||
maxBytes: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* System defaults applied when a client (or kind) has no explicit override.
|
||||
*
|
||||
* Important: each upload route also has a `multer.limits.fileSize` hard
|
||||
* ceiling — those route ceilings are the absolute maximum the API can
|
||||
* receive. Per-client `maxBytes` cannot legitimately go above its route's
|
||||
* multer ceiling, so the defaults below are kept at-or-below those.
|
||||
*/
|
||||
export const DEFAULT_MEDIA_LIMITS: Record<MediaKind, MediaLimits> = {
|
||||
video: { minBytes: 256 * 1024, maxBytes: 20 * 1024 * 1024 },
|
||||
image: { minBytes: 5 * 1024, maxBytes: 8 * 1024 * 1024 },
|
||||
voice: { minBytes: 5 * 1024, maxBytes: 8 * 1024 * 1024 },
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class ClientService {
|
||||
constructor(private readonly clientDbService: ClientDbService) {}
|
||||
@@ -61,6 +89,43 @@ export class ClientService {
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the per-client byte bounds for a media kind. Missing bounds fall
|
||||
* back to {@link DEFAULT_MEDIA_LIMITS}. If no clientId is supplied (or it
|
||||
* doesn't resolve to a client document) the defaults are returned.
|
||||
*
|
||||
* The returned object is always fully populated — callers can compare
|
||||
* directly against `file.size`.
|
||||
*/
|
||||
async getMediaLimits(
|
||||
clientId: string | Types.ObjectId | null | undefined,
|
||||
kind: MediaKind,
|
||||
): Promise<MediaLimits> {
|
||||
const defaults = DEFAULT_MEDIA_LIMITS[kind];
|
||||
if (!clientId) return { ...defaults };
|
||||
|
||||
const idString = String(clientId);
|
||||
if (!Types.ObjectId.isValid(idString)) return { ...defaults };
|
||||
|
||||
const client = await this.clientDbService.findOne({
|
||||
_id: new Types.ObjectId(idString),
|
||||
});
|
||||
const override = client?.settings?.media?.[kind];
|
||||
const minBytes =
|
||||
typeof override?.minBytes === "number" &&
|
||||
Number.isFinite(override.minBytes) &&
|
||||
override.minBytes >= 0
|
||||
? override.minBytes
|
||||
: defaults.minBytes;
|
||||
const maxBytes =
|
||||
typeof override?.maxBytes === "number" &&
|
||||
Number.isFinite(override.maxBytes) &&
|
||||
override.maxBytes > 0
|
||||
? override.maxBytes
|
||||
: defaults.maxBytes;
|
||||
return { minBytes, maxBytes };
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the per-client CAR_BODY accident-age window (in days).
|
||||
*
|
||||
|
||||
@@ -2,6 +2,41 @@ import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
|
||||
|
||||
export type ClientDocument = ClientModel & Document;
|
||||
|
||||
/**
|
||||
* Per-media (video/image/voice) byte bounds. Either bound is optional so a
|
||||
* tenant can configure only one side (e.g. raise `maxBytes` without setting
|
||||
* a floor). When a bound is missing the system-wide default is used (see
|
||||
* {@link ClientService.getMediaLimits} for the defaults table).
|
||||
*/
|
||||
export class ClientMediaLimits {
|
||||
@Prop({ type: Number, required: false })
|
||||
minBytes?: number;
|
||||
|
||||
@Prop({ type: Number, required: false })
|
||||
maxBytes?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bag of per-media-kind limits. Each kind is optional and any unset values
|
||||
* fall back to the system default (see `ClientService.getMediaLimits`).
|
||||
*
|
||||
* Note: the system also keeps an absolute multer ceiling per upload route
|
||||
* (e.g. 50MB for car-capture videos, 20MB for blame videos, 10MB for
|
||||
* voice/signature/image). A client-configured `maxBytes` cannot exceed the
|
||||
* route's multer ceiling because multer rejects oversize requests before
|
||||
* the request reaches the policy check.
|
||||
*/
|
||||
export class ClientMediaSettings {
|
||||
@Prop({ type: ClientMediaLimits, required: false })
|
||||
video?: ClientMediaLimits;
|
||||
|
||||
@Prop({ type: ClientMediaLimits, required: false })
|
||||
image?: ClientMediaLimits;
|
||||
|
||||
@Prop({ type: ClientMediaLimits, required: false })
|
||||
voice?: ClientMediaLimits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Per-tenant tunables. Add new policy fields here; consumers read them via
|
||||
* `ClientService` with documented defaults so older client documents keep
|
||||
@@ -15,6 +50,13 @@ export class ClientSettings {
|
||||
*/
|
||||
@Prop({ type: Number, required: false })
|
||||
carBodyAccidentMaxAgeDays?: number;
|
||||
|
||||
/**
|
||||
* Per-kind media upload bounds (V2 upload endpoints). Unset kinds /
|
||||
* bounds fall back to the defaults in `ClientService.getMediaLimits`.
|
||||
*/
|
||||
@Prop({ type: ClientMediaSettings, required: false })
|
||||
media?: ClientMediaSettings;
|
||||
}
|
||||
|
||||
@Schema({ collection: "clients", versionKey: false })
|
||||
|
||||
Reference in New Issue
Block a user