feat(usage): add consented beta capabilities and gallery photo totals
This commit is contained in:
@@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
const express = require('express');
|
||||
const { capabilityEvidence } = require('../usage/capabilityEvidence');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const multer = require('multer');
|
||||
@@ -414,6 +415,7 @@ router.post(
|
||||
handleAsync(async (req, res) => {
|
||||
validateRequest(req);
|
||||
const result = await contractService.convertToEvent(parseInt(req.params.id, 10), req.admin?.id);
|
||||
if (!result.alreadyConverted) capabilityEvidence(res, 'crm_document_conversion');
|
||||
return successResponse(res, result, 200,
|
||||
result.alreadyConverted ? 'Already converted to event' : 'Contract converted to event');
|
||||
}),
|
||||
@@ -427,6 +429,7 @@ router.post(
|
||||
handleAsync(async (req, res) => {
|
||||
validateRequest(req);
|
||||
const result = await contractService.convertToInvoiceOnly(parseInt(req.params.id, 10), req.admin?.id);
|
||||
if (!result.alreadyConverted) capabilityEvidence(res, 'crm_document_conversion');
|
||||
return successResponse(res, result, 200, 'Invoices created from contract');
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
const express = require('express');
|
||||
const { capabilityEvidence } = require('../usage/capabilityEvidence');
|
||||
const { body, param, query } = require('express-validator');
|
||||
const { adminAuth } = require('../middleware/auth');
|
||||
const { requirePermission } = require('../middleware/permissions');
|
||||
@@ -719,6 +720,7 @@ router.post('/:id/bill-combined', [
|
||||
{ includeHours: req.body.includeHours !== false, includeRebills: req.body.includeRebills !== false },
|
||||
req.admin.id,
|
||||
);
|
||||
if (result.invoiceId) capabilityEvidence(res, 'crm_combined_billing');
|
||||
successResponse(res, result, 201);
|
||||
}));
|
||||
|
||||
@@ -769,6 +771,7 @@ router.post('/:id/trigger-monthly-bill', [
|
||||
parseInt(req.params.id, 10),
|
||||
req.admin.id,
|
||||
);
|
||||
if (result.invoiceId) capabilityEvidence(res, 'crm_monthly_billing_manual');
|
||||
successResponse(res, result, 201);
|
||||
}));
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
*/
|
||||
|
||||
const express = require('express');
|
||||
const { capabilityEvidence } = require('../usage/capabilityEvidence');
|
||||
const { body, param, query } = require('express-validator');
|
||||
const multer = require('multer');
|
||||
const path = require('path');
|
||||
@@ -563,6 +564,7 @@ router.post(
|
||||
|
||||
const inserted = await db('invoices').insert(row).returning('id');
|
||||
const invoiceId = typeof inserted[0] === 'object' ? inserted[0].id : inserted[0];
|
||||
capabilityEvidence(res, 'crm_invoice_import');
|
||||
|
||||
return successResponse(res, {
|
||||
invoice: transformInvoice(await db('invoices').where({ id: invoiceId }).first()),
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
|
||||
const express = require('express');
|
||||
const { capabilityEvidence } = require('../usage/capabilityEvidence');
|
||||
const router = express.Router();
|
||||
const { body, query, validationResult } = require('express-validator');
|
||||
const { db, withRetry } = require('../database/db');
|
||||
@@ -228,6 +229,8 @@ router.post('/:eventId/export', adminAuth, requirePermission('photos.download'),
|
||||
admin_id: req.admin.id,
|
||||
});
|
||||
|
||||
if (format === 'xmp') capabilityEvidence(res, 'photo_xmp_export');
|
||||
|
||||
if (result.type === 'stream') {
|
||||
res.setHeader('Content-Type', result.contentType);
|
||||
res.setHeader('Content-Disposition', `attachment; filename="${result.filename}"`);
|
||||
|
||||
@@ -7,7 +7,7 @@ const { adminAuth } = require('../middleware/auth');
|
||||
const { requirePermission } = require('../middleware/permissions');
|
||||
const { ensureThumbnail } = require('../services/imageProcessor');
|
||||
const { isVideoMimeType } = require('../services/videoProcessor');
|
||||
const { acceptedUpload } = require('../usage/capabilityEvidence');
|
||||
const { acceptedUpload, capabilityEvidence } = require('../usage/capabilityEvidence');
|
||||
const { generatePhotoFilename, buildContentDisposition } = require('../utils/filenameSanitizer');
|
||||
const {
|
||||
getUseOriginalFilenames,
|
||||
@@ -358,6 +358,7 @@ router.post('/:eventId/upload', adminAuth, requirePermission('photos.upload'), r
|
||||
event,
|
||||
});
|
||||
if (result.success) {
|
||||
capabilityEvidence(res, 'photo_replacement');
|
||||
acceptedUpload(res, {
|
||||
video: isVideoMimeType(file.mimetype),
|
||||
raw: path.extname(file.originalname).toLowerCase() === '.dng',
|
||||
@@ -861,6 +862,7 @@ router.put('/:eventId/photos/:photoId/mark', adminAuth, requirePermission('photo
|
||||
parseInt(eventId, 10), photoId, req.admin.id, mark,
|
||||
);
|
||||
|
||||
capabilityEvidence(res, 'photo_admin_marks');
|
||||
res.json({ success: true, mark: result });
|
||||
} catch (error) {
|
||||
// Validation errors from the service are the caller's fault, not a 500.
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
*/
|
||||
|
||||
const express = require('express');
|
||||
const { capabilityEvidence } = require('../usage/capabilityEvidence');
|
||||
const { body, param, query } = require('express-validator');
|
||||
const { adminAuth } = require('../middleware/auth');
|
||||
const { requirePermission } = require('../middleware/permissions');
|
||||
@@ -484,6 +485,7 @@ router.post(
|
||||
validateRequest(req);
|
||||
const id = parseInt(req.params.id, 10);
|
||||
const result = await quoteService.convertToEvent(id, req.admin.id);
|
||||
if (!result.alreadyConverted) capabilityEvidence(res, 'crm_document_conversion');
|
||||
return successResponse(res, result, 200, result.alreadyConverted ? 'Already converted' : 'Quote converted');
|
||||
})
|
||||
);
|
||||
@@ -499,6 +501,7 @@ router.post(
|
||||
validateRequest(req);
|
||||
const id = parseInt(req.params.id, 10);
|
||||
const result = await quoteService.convertToInvoiceOnly(id, req.admin.id);
|
||||
if (!result.alreadyConverted) capabilityEvidence(res, 'crm_document_conversion');
|
||||
return successResponse(res, result, 200, 'Invoices created from quote');
|
||||
})
|
||||
);
|
||||
@@ -519,6 +522,7 @@ router.post(
|
||||
const contractService = require('../services/contractService');
|
||||
const id = parseInt(req.params.id, 10);
|
||||
const result = await contractService.createFromQuote(id, req.admin.id);
|
||||
if (!result.alreadyConverted) capabilityEvidence(res, 'crm_document_conversion');
|
||||
return successResponse(res, result, 200,
|
||||
result.alreadyConverted ? 'Already linked to a contract' : 'Contract drafted from quote');
|
||||
})
|
||||
|
||||
@@ -5,7 +5,7 @@ const { adminAuth } = require('../middleware/auth');
|
||||
const { requirePermission } = require('../middleware/permissions');
|
||||
const { ValidationError } = require('../utils/errors');
|
||||
const service = require('../services/productUsageService');
|
||||
const { ProtocolError } = require('../usage/protocol.cjs');
|
||||
const { ProtocolError, schemaForConsent } = require('../usage/protocol.cjs');
|
||||
const router = express.Router();
|
||||
const wrap = (fn) => (req, res, next) =>
|
||||
Promise.resolve(fn(req, res)).catch((error) => {
|
||||
@@ -82,9 +82,9 @@ router.post(
|
||||
router.post(
|
||||
'/consent',
|
||||
wrap(async (req, res) => {
|
||||
if (!req.body || Object.keys(req.body).length !== 1 || req.body.consent_version !== 'usage-consent.v2')
|
||||
throw new ValidationError('Explicit usage v2 consent is required');
|
||||
res.json(await service.command('consent', { consent_version: 'usage-consent.v2' }));
|
||||
if (!req.body || Object.keys(req.body).length !== 1 || !['usage.v2', 'usage.v3'].includes(schemaForConsent(req.body.consent_version)))
|
||||
throw new ValidationError('Explicit usage consent is required');
|
||||
res.json(await service.command('consent', { consent_version: req.body.consent_version }));
|
||||
})
|
||||
);
|
||||
router.post(
|
||||
|
||||
@@ -20,7 +20,9 @@ const {
|
||||
LEGACY_FEATURE_KEYS,
|
||||
CATALOG,
|
||||
CURRENT_SCHEMA_VERSION,
|
||||
CURRENT_CONSENT_VERSION,
|
||||
CONSENT_VERSIONS,
|
||||
schemaForConsent,
|
||||
schemaRank,
|
||||
featureKeysFor,
|
||||
observesUse,
|
||||
LAYOUTS
|
||||
@@ -130,8 +132,7 @@ class UsageService {
|
||||
}
|
||||
|
||||
schemaVersion(state) {
|
||||
return state?.consent_version === CURRENT_CONSENT_VERSION
|
||||
? CURRENT_SCHEMA_VERSION : 'usage.v1';
|
||||
return schemaForConsent(state?.consent_version) || 'usage.v1';
|
||||
}
|
||||
constructor(db, options = {}) {
|
||||
this.db = db;
|
||||
@@ -343,7 +344,7 @@ class UsageService {
|
||||
return this.status();
|
||||
}
|
||||
async enable(consent) {
|
||||
if (!['usage-consent.v1', CURRENT_CONSENT_VERSION].includes(consent))
|
||||
if (!Object.values(CONSENT_VERSIONS).includes(consent))
|
||||
throw new ValidationError('Explicit usage consent is required');
|
||||
// Read BEFORE the lease, deliberately. locked() claims the lease and then
|
||||
// reads the row in a second statement; a /disable completing between
|
||||
@@ -670,7 +671,7 @@ class UsageService {
|
||||
await this.db.transaction(async (tx) => {
|
||||
const upgraded = await tx('product_usage_state')
|
||||
.where({ id: 1, status: 'active', installation_id: packet.installation_id })
|
||||
.update({ ...update, consent_version: CURRENT_CONSENT_VERSION });
|
||||
.update({ ...update, consent_version: packet.payload.consent_version });
|
||||
if (upgraded) await tx('product_usage_markers').delete();
|
||||
});
|
||||
} else {
|
||||
@@ -816,7 +817,7 @@ class UsageService {
|
||||
.first();
|
||||
if (destination && parse(destination.setting_value) === 's3') {
|
||||
allowed.push('s3_storage');
|
||||
if (version === CURRENT_SCHEMA_VERSION) allowed.push('s3_backups');
|
||||
if (version !== 'usage.v1') allowed.push('s3_backups');
|
||||
}
|
||||
}
|
||||
await tx('product_usage_markers')
|
||||
@@ -841,7 +842,7 @@ class UsageService {
|
||||
rows.map((r) => [r.setting_key, parse(r.setting_value)])
|
||||
);
|
||||
const flagRows = await this.db('feature_flags')
|
||||
.whereIn('key', version === CURRENT_SCHEMA_VERSION
|
||||
.whereIn('key', version !== 'usage.v1'
|
||||
? [...new Set([...Object.values(FLAG_MAP), 'incomingMail', ...Object.values(CATALOG.features).map((f) => f.flag).filter(Boolean)])]
|
||||
: Object.values(FLAG_MAP))
|
||||
.select('key', 'value');
|
||||
@@ -947,15 +948,16 @@ class UsageService {
|
||||
features.custom_css.used = true;
|
||||
}
|
||||
const now = new Date(this.now()).toISOString();
|
||||
const expanded = version === CURRENT_SCHEMA_VERSION
|
||||
? await require('./expandedSnapshot').expandSnapshot(this.db, { features, flags, used, now: this.now() })
|
||||
const expanded = version !== 'usage.v1'
|
||||
? await require('./expandedSnapshot').expandSnapshot(this.db, { features, flags, used, now: this.now(), version })
|
||||
: features;
|
||||
return {
|
||||
picpeak_version: this.version,
|
||||
report_date: now.slice(0, 10),
|
||||
generated_at: now,
|
||||
features: expanded,
|
||||
gallery_layouts: [...layouts].sort()
|
||||
gallery_layouts: [...layouts].sort(),
|
||||
...(version === 'usage.v3' ? { inventory: await require('./inventorySnapshot').inventorySnapshot(this.db) } : {})
|
||||
};
|
||||
}
|
||||
|
||||
@@ -974,14 +976,17 @@ class UsageService {
|
||||
throw new ConflictError('Retry the pending usage operation first');
|
||||
if (!['feedback', 'vote', 'session', 'consent'].includes(action))
|
||||
throw new ValidationError('Invalid usage action');
|
||||
if (action === 'consent' && state.consent_version === CURRENT_CONSENT_VERSION)
|
||||
const targetVersion = action === 'consent' ? schemaForConsent(payload?.consent_version) : null;
|
||||
if (action === 'consent' && (!targetVersion || targetVersion === 'usage.v1'))
|
||||
throw new ValidationError('Explicit usage consent is required');
|
||||
if (action === 'consent' && schemaRank(targetVersion) <= schemaRank(this.schemaVersion(state)))
|
||||
throw new ConflictError('Usage consent is already current');
|
||||
const packet = makePacket(
|
||||
state,
|
||||
action,
|
||||
Number(state.sequence) + 1,
|
||||
payload,
|
||||
action === 'consent' ? CURRENT_SCHEMA_VERSION : this.schemaVersion(state)
|
||||
action === 'consent' ? targetVersion : this.schemaVersion(state)
|
||||
);
|
||||
// Validate the complete packet before storing an un-sendable operation.
|
||||
verifyEnvelope(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
'use strict';
|
||||
const { CATALOG, emptyFeatures } = require('./schema.cjs');
|
||||
const { CATALOGS, emptyFeatures } = require('./schema.cjs');
|
||||
const { formatBoolean } = require('../utils/dbCompat');
|
||||
|
||||
const truth = (value) => value === true || value === 1 || value === '1';
|
||||
@@ -14,8 +14,8 @@ const parse = (value) => {
|
||||
// Technical configuration only. Never read photos, feedback contents, guest /
|
||||
// customer / admin profiles, messages, audit logs, delivery logs or counts.
|
||||
// Presence queries return a literal 1, not even a row's identifying primary key.
|
||||
async function expandSnapshot(db, { features, flags, used, now }) {
|
||||
const result = { ...emptyFeatures('usage.v2'), ...features };
|
||||
async function expandSnapshot(db, { features, flags, used, now, version = 'usage.v2' }) {
|
||||
const result = { ...emptyFeatures(version), ...features };
|
||||
const effective = { analytics: true, userManagement: true, ...flags };
|
||||
if (!effective.quotes) effective.bills = false;
|
||||
if (effective.bills) effective.accounting = true;
|
||||
@@ -27,7 +27,7 @@ async function expandSnapshot(db, { features, flags, used, now }) {
|
||||
effective.clients = ['customerPortal', 'quotes', 'bills', 'contracts', 'projects', 'calendar', 'hoursLogging', 'newsletters']
|
||||
.some((flag) => effective[flag]);
|
||||
if (['1', 'true', 'yes'].includes(String(process.env.PICPEAK_SINGLE_CONTAINER || '').toLowerCase())) effective.faces = false;
|
||||
for (const [key, definition] of Object.entries(CATALOG.features)) {
|
||||
for (const [key, definition] of Object.entries(CATALOGS[version].features)) {
|
||||
if (definition.configuration === 'builtin') result[key].configured = true;
|
||||
if (definition.flag) result[key].configured = Boolean(effective[definition.flag]);
|
||||
if (definition.used && key !== 'custom_css') result[key].used = used.has(key);
|
||||
@@ -116,6 +116,24 @@ async function expandSnapshot(db, { features, flags, used, now }) {
|
||||
query.where({ feedback_enabled: formatBoolean(true), [column]: formatBoolean(true) }));
|
||||
result.gallery_guest_accounts.configured = await exists('event_feedback_settings', ['feedback_enabled', 'identity_mode'], (query) =>
|
||||
query.where('feedback_enabled', formatBoolean(true)).whereIn('identity_mode', ['guest', 'shared']));
|
||||
if (version === 'usage.v3') {
|
||||
result.gallery_folders.configured = await exists('photo_categories', ['is_folder', 'event_id'], (query) =>
|
||||
query.where('is_folder', formatBoolean(true)).where((q) => q.whereNull('event_id').orWhereIn('event_id', db('events').select('id'))));
|
||||
result.transfer_upload_links.configured = Boolean(effective.transfers) && await exists('transfers',
|
||||
['allow_uploads', 'deleted_at', 'upload_expires_at', 'expires_at', 'upload_token'], (query) =>
|
||||
query.where('allow_uploads', formatBoolean(true)).whereNull('deleted_at').whereNotNull('upload_token').whereNot('upload_token', '')
|
||||
.where((q) => q.where('upload_expires_at', '>', new Date(now).toISOString())
|
||||
.orWhere((fallback) => fallback.whereNull('upload_expires_at').where((expiry) =>
|
||||
expiry.whereNull('expires_at').orWhere('expires_at', '>', new Date(now).toISOString())))));
|
||||
result.workflow_automation_enabled.configured = Boolean(effective.workflows) && await enabled('workflows', 'enabled');
|
||||
result.s3_auto_import.configured = result.s3_photo_storage.configured && process.env.STORAGE_AUTO_IMPORT === 'true';
|
||||
result.crm_combined_billing.configured = Boolean(effective.bills && effective.incomingInvoices);
|
||||
result.crm_document_conversion.configured = Boolean(effective.quotes || effective.contracts);
|
||||
result.gallery_capture_date_sort.configured = await exists('events', ['default_photo_sort'], (query) =>
|
||||
query.whereIn('default_photo_sort', ['capture_date_asc', 'capture_date_desc']));
|
||||
const originalNames = await db('app_settings').where({ setting_key: 'general_use_original_filenames_for_downloads' }).first('setting_value');
|
||||
result.download_original_filenames.configured = truth(parse(originalNames?.setting_value));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
module.exports = { expandSnapshot };
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
'use strict';
|
||||
const { MAX_INVENTORY_COUNT } = require('./schema.cjs');
|
||||
|
||||
// Two installation totals only. No entity rows, IDs, names, file metadata,
|
||||
// groupings, logs, processing results or visitor actions reach this layer.
|
||||
async function inventorySnapshot(db) {
|
||||
return db.transaction(async (tx) => {
|
||||
const [{ count: galleries }] = await tx('events').count('* as count');
|
||||
const photosQuery = tx('photos');
|
||||
if (await tx.schema.hasColumn('photos', 'media_type'))
|
||||
photosQuery.where((q) => q.whereNull('media_type').orWhereNot('media_type', 'video'));
|
||||
const [{ count: photos }] = await photosQuery.count('* as count');
|
||||
const inventory = { galleries: Number(galleries), photos: Number(photos) };
|
||||
for (const count of Object.values(inventory)) {
|
||||
if (!Number.isSafeInteger(count) || count < 0 || count > MAX_INVENTORY_COUNT)
|
||||
throw new Error('Usage inventory total is outside the supported range');
|
||||
}
|
||||
return inventory;
|
||||
}, db.client.config.client === 'pg' ? { isolationLevel: 'repeatable read', readOnly: true } : {});
|
||||
}
|
||||
module.exports = { inventorySnapshot };
|
||||
@@ -1,10 +1,17 @@
|
||||
"use strict";
|
||||
|
||||
// Vendored byte-identical in PicPeak. v1 stays immutable; a larger allowlist
|
||||
// has a new wire version and requires explicit, signed v2 consent.
|
||||
const CATALOG = require("./features.v2.json");
|
||||
const CURRENT_SCHEMA_VERSION = "usage.v2";
|
||||
const CURRENT_CONSENT_VERSION = "usage-consent.v2";
|
||||
// Vendored byte-identical in PicPeak. Existing wire versions stay immutable;
|
||||
// every expansion requires explicit consent to its own version.
|
||||
const CATALOG = require("./features.v3.json");
|
||||
const CATALOGS = { "usage.v2": require("./features.v2.json"), "usage.v3": CATALOG };
|
||||
const CONSENT_VERSIONS = { "usage.v1": "usage-consent.v1", "usage.v2": "usage-consent.v2", "usage.v3": "usage-consent.v3" };
|
||||
const CURRENT_SCHEMA_VERSION = "usage.v3";
|
||||
const CURRENT_CONSENT_VERSION = CONSENT_VERSIONS[CURRENT_SCHEMA_VERSION];
|
||||
const schemaForConsent = (consent) => Object.keys(CONSENT_VERSIONS).find((version) => CONSENT_VERSIONS[version] === consent);
|
||||
const schemaRank = (version) => Object.keys(CONSENT_VERSIONS).indexOf(version);
|
||||
const INVENTORY_KEYS = ["galleries", "photos"];
|
||||
// At the collector's 100,000-reporter limit, sums remain safe JS integers.
|
||||
const MAX_INVENTORY_COUNT = 1000000000;
|
||||
const LEGACY_FEATURE_KEYS = [
|
||||
"crm", "crm_quotes", "crm_invoices", "crm_contracts", "crm_projects",
|
||||
"crm_calendar", "crm_hours", "customer_portal", "accounting", "workflows",
|
||||
@@ -22,9 +29,9 @@ const timestamp = { type: "string", pattern: "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2
|
||||
const text = (maxLength, minLength = 1) => ({ type: "string", minLength, maxLength });
|
||||
const boolean = { type: "boolean" };
|
||||
const featureKeysFor = (version = CURRENT_SCHEMA_VERSION) =>
|
||||
version === "usage.v1" ? LEGACY_FEATURE_KEYS : version === "usage.v2" ? FEATURE_KEYS : [];
|
||||
version === "usage.v1" ? LEGACY_FEATURE_KEYS : Object.keys(CATALOGS[version]?.features || {});
|
||||
const observesUse = (key, version = CURRENT_SCHEMA_VERSION) =>
|
||||
version === "usage.v1" || CATALOG.features[key]?.measurement === "configuration_and_use";
|
||||
version === "usage.v1" || CATALOGS[version]?.features[key]?.measurement === "configuration_and_use";
|
||||
const emptyFeatures = (version = CURRENT_SCHEMA_VERSION) => Object.fromEntries(
|
||||
featureKeysFor(version).map(key => [key, {
|
||||
configured: false, ...(observesUse(key, version) ? { used: false } : {})
|
||||
@@ -38,6 +45,9 @@ const report = (version) => object({
|
||||
key, object({ configured: boolean, ...(observesUse(key, version) ? { used: boolean } : {}) })
|
||||
]))),
|
||||
gallery_layouts: { type: "array", uniqueItems: true, maxItems: LAYOUTS.length, items: { enum: LAYOUTS } },
|
||||
...(version === "usage.v3" ? { inventory: object(Object.fromEntries(INVENTORY_KEYS.map(key => [key,
|
||||
{ type: "integer", minimum: 0, maximum: MAX_INVENTORY_COUNT }
|
||||
]))) } : {}),
|
||||
});
|
||||
const feedback = object({
|
||||
feedback_id: uuid, kind: { enum: ["feedback", "feature_request", "testimonial"] },
|
||||
@@ -45,14 +55,14 @@ const feedback = object({
|
||||
allow_public: boolean, allow_marketing: boolean,
|
||||
});
|
||||
const makePayloads = (version) => ({
|
||||
register: object({ consent_version: { const: version === "usage.v1" ? "usage-consent.v1" : CURRENT_CONSENT_VERSION } }),
|
||||
register: object({ consent_version: { const: CONSENT_VERSIONS[version] } }),
|
||||
report: report(version),
|
||||
delete: object({}), feedback,
|
||||
vote: object({ feedback_id: uuid, voted: boolean }),
|
||||
session: object({}),
|
||||
...(version === "usage.v2" ? { consent: object({ consent_version: { const: CURRENT_CONSENT_VERSION } }) } : {}),
|
||||
...(version !== "usage.v1" ? { consent: object({ consent_version: { const: CONSENT_VERSIONS[version] } }) } : {}),
|
||||
});
|
||||
const payloadsByVersion = Object.fromEntries(["usage.v1", "usage.v2"].map(version => [version, makePayloads(version)]));
|
||||
const payloadsByVersion = Object.fromEntries(Object.keys(CONSENT_VERSIONS).map(version => [version, makePayloads(version)]));
|
||||
const envelopeSchemas = Object.fromEntries(Object.entries(payloadsByVersion).map(([version, actions]) => [version, {
|
||||
$schema: "http://json-schema.org/draft-07/schema#",
|
||||
$id: `https://usage.picpeak.app/schema/${version}.json`,
|
||||
@@ -73,7 +83,8 @@ const envelopeSchemas = Object.fromEntries(Object.entries(payloadsByVersion).map
|
||||
const envelopeSchema = envelopeSchemas[CURRENT_SCHEMA_VERSION];
|
||||
const payloads = payloadsByVersion[CURRENT_SCHEMA_VERSION];
|
||||
module.exports = {
|
||||
FEATURE_KEYS, LEGACY_FEATURE_KEYS, LAYOUTS, CATALOG, CURRENT_SCHEMA_VERSION,
|
||||
FEATURE_KEYS, LEGACY_FEATURE_KEYS, LAYOUTS, CATALOG, CATALOGS, CONSENT_VERSIONS,
|
||||
schemaForConsent, schemaRank, INVENTORY_KEYS, MAX_INVENTORY_COUNT, CURRENT_SCHEMA_VERSION,
|
||||
CURRENT_CONSENT_VERSION, featureKeysFor, observesUse, emptyFeatures,
|
||||
envelopeSchema, envelopeSchemas, payloads, payloadsByVersion,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user