Release v2.14.1
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile, web, pulse-signage-web) (push) Successful in 1m53s
Publish Docker Image / build-and-push-existing-registry (./build/Dockerfile.player, player, pulse-signage-player) (push) Successful in 32s

This commit is contained in:
lzstealth committed 2026-10-11 18:54:43 +01:00
1 parent 635e1a9942
commit c3299abaac
174 files changed
+4483 -1687

No files matched your search

+61 -4
View File
@@ -1,10 +1,10 @@
// Slide data access helpers, including rich-text normalization and payload building.
const { fetchTemplateById } = require('./templates');
const { parseJsonSafe, validateMaxLength } = require('./utils');
const { fetchTemplateById } = require('#src/data/templates');
const { parseJsonSafe, validateMaxLength } = require('#src/data/utils');
const TITLE_MAX_LENGTH = 255;
const { buildQrCodeContent } = require('./qr-code');
const { buildQrCodeContent } = require('#src/data/qr-code');
const DEFAULT_FONT_SIZE = 32;
@@ -474,7 +474,64 @@ async function buildTemplateContent(pool, template, body, filesByField, existing
font_size: style.font_size,
font_color: style.font_color
};
} else if (!['text', 'image', 'video', 'webpage', 'qr-code', 'html', 'rtmp', 'rss', 'api', 'weather'].includes(String(region.region_type || '').trim())) {
} else if (region.region_type === 'chart') {
const current = existingContent && existingContent[region.region_key] && typeof existingContent[region.region_key] === 'object' ? existingContent[region.region_key] : {};
let manualRows = [];
let datasets = [];
try {
const parsedRows = JSON.parse(String(body[`region_chart_manual_rows_${region.id}`] || '[]'));
manualRows = Array.isArray(parsedRows) ? parsedRows.slice(0, 500) : [];
} catch (_error) {
manualRows = Array.isArray(current.manual_rows) ? current.manual_rows : [];
}
try {
const parsedDatasets = JSON.parse(String(body[`region_chart_datasets_${region.id}`] || '[]'));
datasets = Array.isArray(parsedDatasets) ? parsedDatasets.slice(0, 12) : [];
} catch (_error) {
datasets = Array.isArray(current.datasets) ? current.datasets : [];
}
const colors = String(body[`region_chart_colors_${region.id}`] === undefined ? (Array.isArray(current.colors) ? current.colors.join(',') : '') : body[`region_chart_colors_${region.id}`] || '')
.split(',')
.map((color) => String(color || '').trim())
.filter((color) => /^#[0-9a-fA-F]{3,8}$/.test(color))
.slice(0, 24);
const chartSourceKind = String(body[`region_chart_source_kind_${region.id}`] || current.source_kind || 'manual') === 'api' ? 'api' : 'manual';
const chartOptions = {
legend: ['automatic', 'show', 'hide'].includes(String(body[`region_chart_legend_${region.id}`] || current.chart_options?.legend || 'automatic')) ? String(body[`region_chart_legend_${region.id}`] || current.chart_options?.legend || 'automatic') : 'automatic',
begin_at_zero: body[`region_chart_begin_at_zero_control_${region.id}`] === undefined ? current.chart_options?.begin_at_zero !== false : body[`region_chart_begin_at_zero_${region.id}`] === 'on',
y_axis_min: String(body[`region_chart_y_axis_min_${region.id}`] === undefined ? current.chart_options?.y_axis_min || '' : body[`region_chart_y_axis_min_${region.id}`] || '').trim(),
y_axis_max: String(body[`region_chart_y_axis_max_${region.id}`] === undefined ? current.chart_options?.y_axis_max || '' : body[`region_chart_y_axis_max_${region.id}`] || '').trim(),
line_points: ['automatic', 'show', 'hide'].includes(String(body[`region_chart_line_points_${region.id}`] || current.chart_options?.line_points || 'automatic')) ? String(body[`region_chart_line_points_${region.id}`] || current.chart_options?.line_points || 'automatic') : 'automatic',
line_fill: body[`region_chart_line_fill_${region.id}`] === 'on',
bar_orientation: String(body[`region_chart_bar_orientation_${region.id}`] || current.chart_options?.bar_orientation || 'vertical') === 'horizontal' ? 'horizontal' : 'vertical',
value_format: ['number', 'percent', 'currency'].includes(String(body[`region_chart_value_format_${region.id}`] || current.chart_options?.value_format || 'number')) ? String(body[`region_chart_value_format_${region.id}`] || current.chart_options?.value_format || 'number') : 'number',
decimal_places: Math.max(0, Math.min(4, Number(body[`region_chart_decimal_places_${region.id}`] === undefined ? current.chart_options?.decimal_places || 0 : body[`region_chart_decimal_places_${region.id}`] || 0) || 0)),
text_size: Math.max(8, Math.min(48, Number(body[`region_chart_text_size_${region.id}`] === undefined ? current.chart_options?.text_size || 12 : body[`region_chart_text_size_${region.id}`] || 12) || 12)),
grid_color: /^#[0-9a-fA-F]{6}$/.test(String(body[`region_chart_grid_color_${region.id}`] || current.chart_options?.grid_color || '')) ? String(body[`region_chart_grid_color_${region.id}`] || current.chart_options?.grid_color) : '#000000',
text_color: /^#[0-9a-fA-F]{6}$/.test(String(body[`region_chart_text_color_${region.id}`] || current.chart_options?.text_color || '')) ? String(body[`region_chart_text_color_${region.id}`] || current.chart_options?.text_color) : '#000000'
};
const sourceOptions = {
sort: ['none', 'reverse-original', 'value-ascending', 'value-descending'].includes(String(body[`region_chart_source_sort_${region.id}`] || current.source_options?.sort || 'none')) ? String(body[`region_chart_source_sort_${region.id}`] || current.source_options?.sort || 'none') : 'none',
limit: Math.max(0, Math.min(500, Number(body[`region_chart_source_limit_${region.id}`] === undefined ? current.source_options?.limit || 0 : body[`region_chart_source_limit_${region.id}`] || 0) || 0)),
missing: ['skip', 'zero', 'gap'].includes(String(body[`region_chart_source_missing_${region.id}`] || current.source_options?.missing || 'gap')) ? String(body[`region_chart_source_missing_${region.id}`] || current.source_options?.missing || 'gap') : 'gap'
};
content[region.region_key] = {
type: 'chart',
chart_type: ['line', 'bar', 'doughnut', 'pie'].includes(String(body[`region_chart_type_${region.id}`] || current.chart_type || 'bar')) ? String(body[`region_chart_type_${region.id}`] || current.chart_type || 'bar') : 'bar',
source_kind: chartSourceKind,
source_id: chartSourceKind === 'api' ? (body[`region_chart_source_id_${region.id}`] === undefined ? (current.source_id || null) : (body[`region_chart_source_id_${region.id}`] === '' ? null : Number(body[`region_chart_source_id_${region.id}`]))) : null,
items_path: String(body[`region_chart_items_path_${region.id}`] === undefined ? current.items_path || '' : body[`region_chart_items_path_${region.id}`] || '').trim(),
label_path: String(body[`region_chart_label_path_${region.id}`] === undefined ? current.label_path || '' : body[`region_chart_label_path_${region.id}`] || '').trim(),
value_path: String(body[`region_chart_value_path_${region.id}`] === undefined ? current.value_path || '' : body[`region_chart_value_path_${region.id}`] || '').trim(),
dataset_label: String(body[`region_chart_dataset_label_${region.id}`] === undefined ? current.dataset_label || 'Value' : body[`region_chart_dataset_label_${region.id}`] || 'Value').trim() || 'Value',
color: /^#[0-9a-fA-F]{3,8}$/.test(String(body[`region_chart_color_${region.id}`] || current.color || '')) ? String(body[`region_chart_color_${region.id}`]) : (current.color || '#36a269'),
datasets,
colors,
manual_rows: manualRows,
chart_options: chartOptions,
source_options: sourceOptions
};
} else if (!['text', 'image', 'video', 'webpage', 'qr-code', 'html', 'rtmp', 'rss', 'api', 'weather', 'chart'].includes(String(region.region_type || '').trim())) {
const current = existingContent && existingContent[region.region_key] && typeof existingContent[region.region_key] === 'object' ? existingContent[region.region_key] : {};
const suffix = '_' + region.id;
const generic = {};