Complete implementation of the Goa Government e-Licensing platform with: Backend: - NestJS API with JWT authentication - PostgreSQL database with Knex ORM - Redis caching and session management - MinIO document storage - Hyperledger Besu blockchain integration - Multi-department workflow system - Comprehensive API tests (266/282 passing) Frontend: - Angular 21 with standalone components - Angular Material + TailwindCSS UI - Visual workflow builder - Document upload with progress tracking - Blockchain explorer integration - Role-based dashboards (Admin, Department, Citizen) - E2E tests with Playwright (37 tests) Infrastructure: - Docker Compose orchestration - Blockscout blockchain explorer - Development and production configurations
1427 lines
29 KiB
JavaScript
1427 lines
29 KiB
JavaScript
const pptxgen = require("pptxgenjs");
|
|
|
|
let pres = new pptxgen();
|
|
pres.layout = "LAYOUT_16x9";
|
|
pres.author = "Goa Government";
|
|
pres.title = "Goa GEL Blockchain Document Verification Platform";
|
|
|
|
// Color palette - Professional government/tech
|
|
const colors = {
|
|
primary: "0F4C81", // Deep blue
|
|
secondary: "1B7BAA", // Teal blue
|
|
accent: "00B4D8", // Bright cyan
|
|
lightBg: "F0F4F8", // Light blue-gray
|
|
darkBg: "0B2E4D", // Dark navy
|
|
white: "FFFFFF",
|
|
text: "1A1A1A",
|
|
lightText: "666666",
|
|
success: "06A77D",
|
|
};
|
|
|
|
// Helper function for fresh shadow objects
|
|
const makeShadow = () => ({
|
|
type: "outer",
|
|
blur: 6,
|
|
offset: 2,
|
|
color: "000000",
|
|
opacity: 0.12,
|
|
});
|
|
|
|
// Utility: Add title with accent bar
|
|
function addTitleWithBar(slide, title, x = 0.5, y = 0.4, w = 9, barColor = colors.accent) {
|
|
slide.addShape(pres.shapes.RECTANGLE, {
|
|
x: x,
|
|
y: y,
|
|
w: 0.08,
|
|
h: 0.6,
|
|
fill: { color: barColor },
|
|
line: { type: "none" },
|
|
});
|
|
|
|
slide.addText(title, {
|
|
x: x + 0.15,
|
|
y: y,
|
|
w: w - 0.2,
|
|
h: 0.6,
|
|
fontSize: 40,
|
|
bold: true,
|
|
color: colors.white,
|
|
fontFace: "Calibri",
|
|
valign: "middle",
|
|
margin: 0,
|
|
});
|
|
}
|
|
|
|
// ===== SLIDE 1: TITLE SLIDE =====
|
|
let slide1 = pres.addSlide();
|
|
slide1.background = { color: colors.darkBg };
|
|
|
|
slide1.addText("Goa GEL", {
|
|
x: 0.5,
|
|
y: 1.5,
|
|
w: 9,
|
|
h: 0.8,
|
|
fontSize: 60,
|
|
bold: true,
|
|
color: colors.accent,
|
|
align: "center",
|
|
fontFace: "Calibri",
|
|
});
|
|
|
|
slide1.addText("Blockchain-Based Document Verification Platform", {
|
|
x: 0.5,
|
|
y: 2.4,
|
|
w: 9,
|
|
h: 0.6,
|
|
fontSize: 36,
|
|
color: colors.white,
|
|
align: "center",
|
|
fontFace: "Calibri",
|
|
});
|
|
|
|
slide1.addText("for Government of Goa", {
|
|
x: 0.5,
|
|
y: 3.1,
|
|
w: 9,
|
|
h: 0.4,
|
|
fontSize: 20,
|
|
color: colors.lightBg,
|
|
align: "center",
|
|
fontFace: "Calibri",
|
|
});
|
|
|
|
slide1.addText("Technical Architecture Overview", {
|
|
x: 0.5,
|
|
y: 4.3,
|
|
w: 9,
|
|
h: 0.4,
|
|
fontSize: 18,
|
|
italic: true,
|
|
color: colors.accent,
|
|
align: "center",
|
|
fontFace: "Calibri",
|
|
});
|
|
|
|
// ===== SLIDE 2: AGENDA =====
|
|
let slide2 = pres.addSlide();
|
|
slide2.background = { color: colors.lightBg };
|
|
|
|
addTitleWithBar(slide2, "Agenda");
|
|
|
|
const agendaItems = [
|
|
"Problem Statement",
|
|
"Solution Overview",
|
|
"NBF Alignment",
|
|
"System Architecture",
|
|
"Blockchain Architecture",
|
|
"Smart Contracts",
|
|
"Technology Stack",
|
|
"Workflow Engine",
|
|
"Data Flow",
|
|
"Security Architecture",
|
|
"Deployment & POC Scope",
|
|
"Success Criteria & Timeline",
|
|
];
|
|
|
|
slide2.addText(
|
|
agendaItems.map((item, idx) => ({
|
|
text: item,
|
|
options: {
|
|
bullet: true,
|
|
breakLine: idx < agendaItems.length - 1,
|
|
},
|
|
})),
|
|
{
|
|
x: 1,
|
|
y: 1.2,
|
|
w: 8,
|
|
h: 3.8,
|
|
fontSize: 16,
|
|
color: colors.text,
|
|
fontFace: "Calibri",
|
|
}
|
|
);
|
|
|
|
// ===== SLIDE 3: PROBLEM STATEMENT =====
|
|
let slide3 = pres.addSlide();
|
|
slide3.background = { color: colors.white };
|
|
|
|
addTitleWithBar(slide3, "Problem Statement", 0.5, 0.3);
|
|
|
|
const problems = [
|
|
{
|
|
title: "Fragmented Mechanisms",
|
|
desc: "Multiple online/offline document processes",
|
|
},
|
|
{
|
|
title: "Lack of Trust",
|
|
desc: "No transparent verification mechanism",
|
|
},
|
|
{
|
|
title: "Poor Traceability",
|
|
desc: "Document history and approvals unclear",
|
|
},
|
|
{
|
|
title: "Tampering Risks",
|
|
desc: "No protection against document modification",
|
|
},
|
|
];
|
|
|
|
let yPos = 1.3;
|
|
problems.forEach((problem, idx) => {
|
|
const bgColor = [colors.primary, colors.secondary, colors.accent, "FF6B6B"][idx];
|
|
|
|
slide3.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0.5 + idx * 2.25,
|
|
y: yPos,
|
|
w: 2.1,
|
|
h: 3.5,
|
|
fill: { color: bgColor },
|
|
line: { type: "none" },
|
|
shadow: makeShadow(),
|
|
});
|
|
|
|
slide3.addText(problem.title, {
|
|
x: 0.65 + idx * 2.25,
|
|
y: yPos + 0.3,
|
|
w: 1.8,
|
|
h: 1,
|
|
fontSize: 16,
|
|
bold: true,
|
|
color: colors.white,
|
|
align: "center",
|
|
fontFace: "Calibri",
|
|
});
|
|
|
|
slide3.addText(problem.desc, {
|
|
x: 0.65 + idx * 2.25,
|
|
y: yPos + 1.5,
|
|
w: 1.8,
|
|
h: 1.6,
|
|
fontSize: 13,
|
|
color: colors.white,
|
|
align: "center",
|
|
valign: "middle",
|
|
fontFace: "Calibri",
|
|
});
|
|
});
|
|
|
|
// ===== SLIDE 4: SOLUTION OVERVIEW =====
|
|
let slide4 = pres.addSlide();
|
|
slide4.background = { color: colors.lightBg };
|
|
|
|
addTitleWithBar(slide4, "Solution Overview: GEL Platform");
|
|
|
|
const solutions = [
|
|
{
|
|
title: "Single Ledger",
|
|
icon: "✓",
|
|
},
|
|
{
|
|
title: "Multi-Stakeholder Consensus",
|
|
icon: "✓",
|
|
},
|
|
{
|
|
title: "End-to-End Traceability",
|
|
icon: "✓",
|
|
},
|
|
{
|
|
title: "REST API Interoperability",
|
|
icon: "✓",
|
|
},
|
|
];
|
|
|
|
let xPos = 0.7;
|
|
solutions.forEach((sol) => {
|
|
slide4.addShape(pres.shapes.RECTANGLE, {
|
|
x: xPos,
|
|
y: 1.4,
|
|
w: 2.0,
|
|
h: 2.8,
|
|
fill: { color: colors.white },
|
|
line: { type: "none" },
|
|
shadow: makeShadow(),
|
|
});
|
|
|
|
slide4.addText(sol.icon, {
|
|
x: xPos,
|
|
y: 1.6,
|
|
w: 2.0,
|
|
h: 0.5,
|
|
fontSize: 48,
|
|
color: colors.success,
|
|
align: "center",
|
|
fontFace: "Calibri",
|
|
});
|
|
|
|
slide4.addText(sol.title, {
|
|
x: xPos + 0.1,
|
|
y: 2.2,
|
|
w: 1.8,
|
|
h: 1.8,
|
|
fontSize: 14,
|
|
bold: true,
|
|
color: colors.text,
|
|
align: "center",
|
|
valign: "middle",
|
|
fontFace: "Calibri",
|
|
});
|
|
|
|
xPos += 2.15;
|
|
});
|
|
|
|
// ===== SLIDE 5: NBF ALIGNMENT =====
|
|
let slide5 = pres.addSlide();
|
|
slide5.background = { color: colors.white };
|
|
|
|
addTitleWithBar(slide5, "National Blockchain Framework Alignment");
|
|
|
|
slide5.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0.5,
|
|
y: 1.2,
|
|
w: 9,
|
|
h: 3.8,
|
|
fill: { color: colors.lightBg },
|
|
line: { type: "none" },
|
|
});
|
|
|
|
slide5.addText(
|
|
[
|
|
{ text: "NBF Document Chain Integration Path\n", options: { bold: true, fontSize: 18, breakLine: true } },
|
|
{
|
|
text:
|
|
"GEL Platform designed with future NBF compliance in mind. Current implementation follows recommended blockchain patterns and can be seamlessly integrated with National Blockchain Framework when available.\n\n",
|
|
options: { fontSize: 14, breakLine: true },
|
|
},
|
|
{
|
|
text: "Key Alignment Features:",
|
|
options: { bold: true, fontSize: 14, breakLine: true },
|
|
},
|
|
{
|
|
text: "QBFT consensus mechanism",
|
|
options: { bullet: true, breakLine: true },
|
|
},
|
|
{ text: "Validator node architecture", options: { bullet: true, breakLine: true } },
|
|
{ text: "Decentralized governance model", options: { bullet: true, breakLine: true } },
|
|
{ text: "Cross-chain interoperability", options: { bullet: true } },
|
|
],
|
|
{
|
|
x: 0.8,
|
|
y: 1.4,
|
|
w: 8.4,
|
|
h: 3.4,
|
|
fontSize: 14,
|
|
color: colors.text,
|
|
fontFace: "Calibri",
|
|
}
|
|
);
|
|
|
|
// ===== SLIDE 6: SYSTEM ARCHITECTURE =====
|
|
let slide6 = pres.addSlide();
|
|
slide6.background = { color: colors.lightBg };
|
|
|
|
addTitleWithBar(slide6, "System Architecture - High Level");
|
|
|
|
const components = [
|
|
{ name: "Frontend\n(Next.js 14)", x: 0.6, color: colors.primary },
|
|
{ name: "Backend API\n(NestJS)", x: 2.8, color: colors.secondary },
|
|
{ name: "Smart Contracts\n(ERC-721)", x: 5.0, color: colors.accent },
|
|
{ name: "Database\n(PostgreSQL)", x: 7.2, color: "FF6B6B" },
|
|
];
|
|
|
|
components.forEach((comp) => {
|
|
slide6.addShape(pres.shapes.RECTANGLE, {
|
|
x: comp.x,
|
|
y: 1.2,
|
|
w: 1.8,
|
|
h: 1.2,
|
|
fill: { color: comp.color },
|
|
line: { type: "none" },
|
|
shadow: makeShadow(),
|
|
});
|
|
|
|
slide6.addText(comp.name, {
|
|
x: comp.x,
|
|
y: 1.2,
|
|
w: 1.8,
|
|
h: 1.2,
|
|
fontSize: 12,
|
|
bold: true,
|
|
color: colors.white,
|
|
align: "center",
|
|
valign: "middle",
|
|
fontFace: "Calibri",
|
|
});
|
|
|
|
slide6.addShape(pres.shapes.LINE, {
|
|
x: comp.x + 1.8,
|
|
y: 1.8,
|
|
w: 0.8,
|
|
h: 0,
|
|
line: { color: colors.text, width: 2 },
|
|
});
|
|
});
|
|
|
|
// Remove last line
|
|
slide6.addShape(pres.shapes.RECTANGLE, {
|
|
x: 8.8,
|
|
y: 1.7,
|
|
w: 0.4,
|
|
h: 0.2,
|
|
fill: { color: colors.lightBg },
|
|
line: { type: "none" },
|
|
});
|
|
|
|
slide6.addText("Storage Layer (MinIO) | Blockchain (Hyperledger Besu)", {
|
|
x: 0.6,
|
|
y: 2.8,
|
|
w: 8.8,
|
|
h: 0.4,
|
|
fontSize: 13,
|
|
color: colors.text,
|
|
align: "center",
|
|
fontFace: "Calibri",
|
|
});
|
|
|
|
slide6.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0.6,
|
|
y: 3.5,
|
|
w: 8.8,
|
|
h: 1.5,
|
|
fill: { color: colors.white },
|
|
line: { type: "none" },
|
|
shadow: makeShadow(),
|
|
});
|
|
|
|
slide6.addText(
|
|
[
|
|
{ text: "Architectural Principles\n", options: { bold: true, fontSize: 12, breakLine: true } },
|
|
{ text: "Microservices: Independent, scalable components", options: { bullet: true, breakLine: true } },
|
|
{ text: "API-First: RESTful interfaces for all services", options: { bullet: true, breakLine: true } },
|
|
{ text: "Event-Driven: Async communication between layers", options: { bullet: true } },
|
|
],
|
|
{
|
|
x: 0.9,
|
|
y: 3.6,
|
|
w: 8.2,
|
|
h: 1.3,
|
|
fontSize: 11,
|
|
color: colors.text,
|
|
fontFace: "Calibri",
|
|
}
|
|
);
|
|
|
|
// ===== SLIDE 7: BLOCKCHAIN ARCHITECTURE =====
|
|
let slide7 = pres.addSlide();
|
|
slide7.background = { color: colors.white };
|
|
|
|
addTitleWithBar(slide7, "Blockchain Architecture - Deep Dive");
|
|
|
|
slide7.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0.5,
|
|
y: 1.2,
|
|
w: 4.2,
|
|
h: 3.8,
|
|
fill: { color: colors.lightBg },
|
|
line: { type: "none" },
|
|
});
|
|
|
|
slide7.addText(
|
|
[
|
|
{ text: "Hyperledger Besu\n\n", options: { bold: true, fontSize: 14, breakLine: true } },
|
|
{ text: "QBFT Consensus", options: { bullet: true, breakLine: true } },
|
|
{ text: "4 Validator Nodes", options: { bullet: true, breakLine: true } },
|
|
{ text: "Immediate Finality", options: { bullet: true, breakLine: true } },
|
|
{ text: "Enterprise-Grade", options: { bullet: true, breakLine: true } },
|
|
{ text: "EVM Compatible", options: { bullet: true } },
|
|
],
|
|
{
|
|
x: 0.8,
|
|
y: 1.4,
|
|
w: 3.6,
|
|
h: 3.4,
|
|
fontSize: 13,
|
|
color: colors.text,
|
|
fontFace: "Calibri",
|
|
}
|
|
);
|
|
|
|
slide7.addShape(pres.shapes.RECTANGLE, {
|
|
x: 5.2,
|
|
y: 1.2,
|
|
w: 4.2,
|
|
h: 3.8,
|
|
fill: { color: colors.primary },
|
|
line: { type: "none" },
|
|
});
|
|
|
|
slide7.addText(
|
|
[
|
|
{ text: "Network Topology\n\n", options: { bold: true, fontSize: 14, color: colors.white, breakLine: true } },
|
|
{
|
|
text: "Validator 1",
|
|
options: { bullet: true, color: colors.white, breakLine: true },
|
|
},
|
|
{
|
|
text: "Validator 2",
|
|
options: { bullet: true, color: colors.white, breakLine: true },
|
|
},
|
|
{
|
|
text: "Validator 3",
|
|
options: { bullet: true, color: colors.white, breakLine: true },
|
|
},
|
|
{
|
|
text: "Validator 4",
|
|
options: { bullet: true, color: colors.white, breakLine: true },
|
|
},
|
|
{
|
|
text: "Public RPC Nodes",
|
|
options: { bullet: true, color: colors.white },
|
|
},
|
|
],
|
|
{
|
|
x: 5.5,
|
|
y: 1.4,
|
|
w: 3.6,
|
|
h: 3.4,
|
|
fontSize: 13,
|
|
color: colors.white,
|
|
fontFace: "Calibri",
|
|
}
|
|
);
|
|
|
|
// ===== SLIDE 8: SMART CONTRACTS =====
|
|
let slide8 = pres.addSlide();
|
|
slide8.background = { color: colors.lightBg };
|
|
|
|
addTitleWithBar(slide8, "Smart Contracts - NFT-Based Approach");
|
|
|
|
const contracts = [
|
|
{
|
|
name: "LicenseRequestNFT",
|
|
desc: "ERC-721 Soulbound",
|
|
y: 1.3,
|
|
},
|
|
{
|
|
name: "ApprovalManager",
|
|
desc: "Multi-stage approvals",
|
|
y: 2.4,
|
|
},
|
|
{
|
|
name: "DepartmentRegistry",
|
|
desc: "Department governance",
|
|
y: 3.5,
|
|
},
|
|
{
|
|
name: "WorkflowRegistry",
|
|
desc: "Process orchestration",
|
|
y: 4.6,
|
|
},
|
|
];
|
|
|
|
contracts.forEach((contract) => {
|
|
slide8.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0.6,
|
|
y: contract.y,
|
|
w: 0.08,
|
|
h: 0.7,
|
|
fill: { color: colors.accent },
|
|
line: { type: "none" },
|
|
});
|
|
|
|
slide8.addText(contract.name, {
|
|
x: 0.8,
|
|
y: contract.y,
|
|
w: 3.5,
|
|
h: 0.35,
|
|
fontSize: 14,
|
|
bold: true,
|
|
color: colors.text,
|
|
fontFace: "Calibri",
|
|
valign: "top",
|
|
margin: 0,
|
|
});
|
|
|
|
slide8.addText(contract.desc, {
|
|
x: 0.8,
|
|
y: contract.y + 0.35,
|
|
w: 3.5,
|
|
h: 0.35,
|
|
fontSize: 12,
|
|
color: colors.lightText,
|
|
fontFace: "Calibri",
|
|
valign: "top",
|
|
margin: 0,
|
|
});
|
|
|
|
slide8.addShape(pres.shapes.RECTANGLE, {
|
|
x: 4.5,
|
|
y: contract.y,
|
|
w: 5.0,
|
|
h: 0.7,
|
|
fill: { color: colors.white },
|
|
line: { type: "none" },
|
|
shadow: makeShadow(),
|
|
});
|
|
|
|
const details = [
|
|
["Status Tracking", "Access Control", "Event Logging"],
|
|
["Sequential Flow", "Parallel Stages", "Conditional Logic"],
|
|
["Department IDs", "Role Binding", "Approval Rights"],
|
|
["Stage Management", "Versioning", "State Transitions"],
|
|
];
|
|
|
|
slide8.addText(details[contracts.indexOf(contract)].join(" | "), {
|
|
x: 4.7,
|
|
y: contract.y + 0.15,
|
|
w: 4.6,
|
|
h: 0.4,
|
|
fontSize: 11,
|
|
color: colors.text,
|
|
align: "left",
|
|
fontFace: "Calibri",
|
|
});
|
|
});
|
|
|
|
// ===== SLIDE 9: TECHNOLOGY STACK =====
|
|
let slide9 = pres.addSlide();
|
|
slide9.background = { color: colors.white };
|
|
|
|
addTitleWithBar(slide9, "Technology Stack");
|
|
|
|
const stackLayers = [
|
|
{
|
|
title: "Frontend",
|
|
tech: "Next.js 14, React, TypeScript",
|
|
y: 1.3,
|
|
},
|
|
{
|
|
title: "Backend",
|
|
tech: "NestJS, TypeScript, Node.js",
|
|
y: 2.3,
|
|
},
|
|
{
|
|
title: "Database",
|
|
tech: "PostgreSQL (relational), Redis (caching)",
|
|
y: 3.3,
|
|
},
|
|
{
|
|
title: "Storage",
|
|
tech: "MinIO (S3-compatible object storage)",
|
|
y: 4.3,
|
|
},
|
|
];
|
|
|
|
stackLayers.forEach((layer, idx) => {
|
|
const colors_alt = [colors.primary, colors.secondary, colors.accent, "FF6B6B"];
|
|
|
|
slide9.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0.6,
|
|
y: layer.y,
|
|
w: 8.8,
|
|
h: 0.8,
|
|
fill: { color: colors_alt[idx] },
|
|
line: { type: "none" },
|
|
});
|
|
|
|
slide9.addText(layer.title, {
|
|
x: 0.9,
|
|
y: layer.y + 0.15,
|
|
w: 2.0,
|
|
h: 0.5,
|
|
fontSize: 14,
|
|
bold: true,
|
|
color: colors.white,
|
|
valign: "middle",
|
|
fontFace: "Calibri",
|
|
margin: 0,
|
|
});
|
|
|
|
slide9.addText(layer.tech, {
|
|
x: 3.2,
|
|
y: layer.y + 0.15,
|
|
w: 6.0,
|
|
h: 0.5,
|
|
fontSize: 13,
|
|
color: colors.white,
|
|
valign: "middle",
|
|
fontFace: "Calibri",
|
|
margin: 0,
|
|
});
|
|
});
|
|
|
|
// ===== SLIDE 10: WORKFLOW ENGINE =====
|
|
let slide10 = pres.addSlide();
|
|
slide10.background = { color: colors.lightBg };
|
|
|
|
addTitleWithBar(slide10, "Workflow Engine");
|
|
|
|
slide10.addText("Multi-Department Approval System", {
|
|
x: 0.5,
|
|
y: 1.2,
|
|
w: 9,
|
|
h: 0.3,
|
|
fontSize: 16,
|
|
bold: true,
|
|
color: colors.text,
|
|
fontFace: "Calibri",
|
|
});
|
|
|
|
const stages = [
|
|
"Submission",
|
|
"Department Review",
|
|
"Senior Approval",
|
|
"Final Authorization",
|
|
"NFT Minted",
|
|
];
|
|
|
|
let stageX = 0.8;
|
|
stages.forEach((stage, idx) => {
|
|
const boxW = 1.6;
|
|
slide10.addShape(pres.shapes.RECTANGLE, {
|
|
x: stageX,
|
|
y: 1.8,
|
|
w: boxW,
|
|
h: 0.8,
|
|
fill: { color: idx < 3 ? colors.primary : idx === 3 ? colors.secondary : colors.success },
|
|
line: { type: "none" },
|
|
});
|
|
|
|
slide10.addText(stage, {
|
|
x: stageX,
|
|
y: 1.8,
|
|
w: boxW,
|
|
h: 0.8,
|
|
fontSize: 11,
|
|
bold: true,
|
|
color: colors.white,
|
|
align: "center",
|
|
valign: "middle",
|
|
fontFace: "Calibri",
|
|
});
|
|
|
|
if (idx < stages.length - 1) {
|
|
slide10.addShape(pres.shapes.LINE, {
|
|
x: stageX + boxW,
|
|
y: 2.2,
|
|
w: 0.35,
|
|
h: 0,
|
|
line: { color: colors.text, width: 2 },
|
|
});
|
|
}
|
|
|
|
stageX += boxW + 0.45;
|
|
});
|
|
|
|
const features = [
|
|
{
|
|
title: "Sequential & Parallel",
|
|
desc: "Configure approval stages as sequential or parallel",
|
|
},
|
|
{
|
|
title: "Document Versioning",
|
|
desc: "Track all document revisions through workflow",
|
|
},
|
|
{
|
|
title: "Auto-Invalidation",
|
|
desc: "Automatically invalidate approvals upon document change",
|
|
},
|
|
];
|
|
|
|
let featureY = 3.0;
|
|
features.forEach((feature) => {
|
|
slide10.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0.8,
|
|
y: featureY,
|
|
w: 0.06,
|
|
h: 0.4,
|
|
fill: { color: colors.accent },
|
|
line: { type: "none" },
|
|
});
|
|
|
|
slide10.addText(feature.title, {
|
|
x: 0.95,
|
|
y: featureY,
|
|
w: 3.0,
|
|
h: 0.25,
|
|
fontSize: 12,
|
|
bold: true,
|
|
color: colors.text,
|
|
fontFace: "Calibri",
|
|
margin: 0,
|
|
});
|
|
|
|
slide10.addText(feature.desc, {
|
|
x: 0.95,
|
|
y: featureY + 0.28,
|
|
w: 3.0,
|
|
h: 0.35,
|
|
fontSize: 11,
|
|
color: colors.lightText,
|
|
fontFace: "Calibri",
|
|
margin: 0,
|
|
});
|
|
|
|
featureY += 1.35;
|
|
});
|
|
|
|
// Right side - diagram
|
|
slide10.addShape(pres.shapes.RECTANGLE, {
|
|
x: 4.5,
|
|
y: 3.0,
|
|
w: 4.8,
|
|
h: 2.2,
|
|
fill: { color: colors.white },
|
|
line: { type: "none" },
|
|
shadow: makeShadow(),
|
|
});
|
|
|
|
slide10.addText(
|
|
[
|
|
{ text: "Approval Flow Example\n", options: { bold: true, fontSize: 12, breakLine: true } },
|
|
{ text: "1. Submit Document", options: { bullet: true, breakLine: true } },
|
|
{ text: "2. Department A Reviews", options: { bullet: true, breakLine: true } },
|
|
{ text: "3. Department B Reviews (parallel)", options: { bullet: true, breakLine: true } },
|
|
{ text: "4. Director Approves", options: { bullet: true, breakLine: true } },
|
|
{ text: "5. NFT Minted on Chain", options: { bullet: true } },
|
|
],
|
|
{
|
|
x: 4.8,
|
|
y: 3.2,
|
|
w: 4.2,
|
|
h: 1.8,
|
|
fontSize: 11,
|
|
color: colors.text,
|
|
fontFace: "Calibri",
|
|
}
|
|
);
|
|
|
|
// ===== SLIDE 11: DATA FLOW =====
|
|
let slide11 = pres.addSlide();
|
|
slide11.background = { color: colors.white };
|
|
|
|
addTitleWithBar(slide11, "Data Flow - License Approval Process");
|
|
|
|
const flowSteps = [
|
|
{
|
|
num: "1",
|
|
title: "Submission",
|
|
desc: "User submits license request with documents",
|
|
},
|
|
{
|
|
num: "2",
|
|
title: "Review",
|
|
desc: "Department staff reviews document",
|
|
},
|
|
{
|
|
num: "3",
|
|
title: "Approval",
|
|
desc: "Document approved by authority",
|
|
},
|
|
{
|
|
num: "4",
|
|
title: "NFT Minting",
|
|
desc: "Approved document minted as NFT",
|
|
},
|
|
];
|
|
|
|
let flowX = 0.7;
|
|
flowSteps.forEach((step, idx) => {
|
|
const boxW = 2.0;
|
|
|
|
// Step box
|
|
slide11.addShape(pres.shapes.RECTANGLE, {
|
|
x: flowX,
|
|
y: 1.5,
|
|
w: boxW,
|
|
h: 1.2,
|
|
fill: { color: colors.primary },
|
|
line: { type: "none" },
|
|
shadow: makeShadow(),
|
|
});
|
|
|
|
slide11.addText(step.num, {
|
|
x: flowX + 0.2,
|
|
y: 1.6,
|
|
w: 0.5,
|
|
h: 0.4,
|
|
fontSize: 20,
|
|
bold: true,
|
|
color: colors.accent,
|
|
fontFace: "Calibri",
|
|
margin: 0,
|
|
});
|
|
|
|
slide11.addText(step.title, {
|
|
x: flowX + 0.8,
|
|
y: 1.6,
|
|
w: 1.0,
|
|
h: 0.4,
|
|
fontSize: 12,
|
|
bold: true,
|
|
color: colors.white,
|
|
fontFace: "Calibri",
|
|
margin: 0,
|
|
});
|
|
|
|
slide11.addText(step.desc, {
|
|
x: flowX + 0.2,
|
|
y: 2.1,
|
|
w: 1.6,
|
|
h: 0.5,
|
|
fontSize: 10,
|
|
color: colors.lightBg,
|
|
fontFace: "Calibri",
|
|
margin: 0,
|
|
});
|
|
|
|
// Arrow
|
|
if (idx < flowSteps.length - 1) {
|
|
slide11.addShape(pres.shapes.LINE, {
|
|
x: flowX + boxW,
|
|
y: 2.1,
|
|
w: 0.4,
|
|
h: 0,
|
|
line: { color: colors.text, width: 2 },
|
|
});
|
|
|
|
slide11.addText("→", {
|
|
x: flowX + boxW + 0.1,
|
|
y: 1.95,
|
|
w: 0.2,
|
|
h: 0.3,
|
|
fontSize: 16,
|
|
color: colors.text,
|
|
align: "center",
|
|
fontFace: "Calibri",
|
|
});
|
|
}
|
|
|
|
flowX += boxW + 0.5;
|
|
});
|
|
|
|
// Database integration info
|
|
slide11.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0.7,
|
|
y: 3.2,
|
|
w: 8.6,
|
|
h: 1.9,
|
|
fill: { color: colors.lightBg },
|
|
line: { type: "none" },
|
|
});
|
|
|
|
slide11.addText(
|
|
[
|
|
{ text: "Data Storage Strategy\n", options: { bold: true, fontSize: 13, breakLine: true } },
|
|
{ text: "Document Metadata (PostgreSQL): Status, timestamps, approver info", options: { bullet: true, breakLine: true } },
|
|
{ text: "Document Files (MinIO): Original and versioned documents", options: { bullet: true, breakLine: true } },
|
|
{ text: "Blockchain Record (Besu): Immutable approval chain and NFT reference", options: { bullet: true, breakLine: true } },
|
|
{ text: "Event Log (Event Bus): Real-time updates for UI and integrations", options: { bullet: true } },
|
|
],
|
|
{
|
|
x: 0.95,
|
|
y: 3.35,
|
|
w: 8.1,
|
|
h: 1.6,
|
|
fontSize: 11,
|
|
color: colors.text,
|
|
fontFace: "Calibri",
|
|
}
|
|
);
|
|
|
|
// ===== SLIDE 12: SECURITY ARCHITECTURE =====
|
|
let slide12 = pres.addSlide();
|
|
slide12.background = { color: colors.lightBg };
|
|
|
|
addTitleWithBar(slide12, "Security Architecture");
|
|
|
|
const securityLayers = [
|
|
{
|
|
layer: "API Level",
|
|
features: ["API Key Authentication", "JWT Tokens", "Rate Limiting"],
|
|
},
|
|
{
|
|
layer: "Wallet Level",
|
|
features: ["Custodial Wallet Management", "Private Key Protection", "Transaction Signing"],
|
|
},
|
|
{
|
|
layer: "Integration Level",
|
|
features: ["Mock DigiLocker (POC)", "Production: Real DigiLocker", "Digital Signature Verification"],
|
|
},
|
|
];
|
|
|
|
let secY = 1.3;
|
|
securityLayers.forEach((sec) => {
|
|
slide12.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0.6,
|
|
y: secY,
|
|
w: 8.8,
|
|
h: 0.9,
|
|
fill: { color: colors.white },
|
|
line: { type: "none" },
|
|
shadow: makeShadow(),
|
|
});
|
|
|
|
slide12.addText(sec.layer, {
|
|
x: 0.85,
|
|
y: secY + 0.15,
|
|
w: 1.8,
|
|
h: 0.6,
|
|
fontSize: 13,
|
|
bold: true,
|
|
color: colors.primary,
|
|
valign: "middle",
|
|
fontFace: "Calibri",
|
|
margin: 0,
|
|
});
|
|
|
|
slide12.addText(sec.features.join(" • "), {
|
|
x: 2.7,
|
|
y: secY + 0.15,
|
|
w: 6.5,
|
|
h: 0.6,
|
|
fontSize: 11,
|
|
color: colors.text,
|
|
valign: "middle",
|
|
fontFace: "Calibri",
|
|
margin: 0,
|
|
});
|
|
|
|
secY += 1.1;
|
|
});
|
|
|
|
// Security principles
|
|
slide12.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0.6,
|
|
y: 4.6,
|
|
w: 8.8,
|
|
h: 0.8,
|
|
fill: { color: colors.accent },
|
|
line: { type: "none" },
|
|
});
|
|
|
|
slide12.addText(
|
|
"Zero Trust Architecture | Defense in Depth | Least Privilege Access",
|
|
{
|
|
x: 0.85,
|
|
y: 4.65,
|
|
w: 8.3,
|
|
h: 0.7,
|
|
fontSize: 12,
|
|
bold: true,
|
|
color: colors.white,
|
|
align: "center",
|
|
valign: "middle",
|
|
fontFace: "Calibri",
|
|
margin: 0,
|
|
}
|
|
);
|
|
|
|
// ===== SLIDE 13: DEPLOYMENT ARCHITECTURE =====
|
|
let slide13 = pres.addSlide();
|
|
slide13.background = { color: colors.white };
|
|
|
|
addTitleWithBar(slide13, "Deployment Architecture");
|
|
|
|
slide13.addText("Docker Compose for POC", {
|
|
x: 0.5,
|
|
y: 1.2,
|
|
w: 9,
|
|
h: 0.3,
|
|
fontSize: 14,
|
|
bold: true,
|
|
color: colors.text,
|
|
fontFace: "Calibri",
|
|
});
|
|
|
|
const services = [
|
|
{
|
|
name: "Frontend",
|
|
port: ":3000",
|
|
y: 1.7,
|
|
},
|
|
{
|
|
name: "API Service",
|
|
port: ":3001",
|
|
y: 2.4,
|
|
},
|
|
{
|
|
name: "PostgreSQL",
|
|
port: ":5432",
|
|
y: 3.1,
|
|
},
|
|
{
|
|
name: "MinIO Storage",
|
|
port: ":9000",
|
|
y: 3.8,
|
|
},
|
|
{
|
|
name: "Besu Node 1",
|
|
port: ":8545",
|
|
y: 4.5,
|
|
},
|
|
];
|
|
|
|
services.forEach((svc) => {
|
|
slide13.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0.6,
|
|
y: svc.y,
|
|
w: 3.5,
|
|
h: 0.5,
|
|
fill: { color: colors.primary },
|
|
line: { type: "none" },
|
|
});
|
|
|
|
slide13.addText(svc.name, {
|
|
x: 0.85,
|
|
y: svc.y + 0.05,
|
|
w: 2.0,
|
|
h: 0.4,
|
|
fontSize: 12,
|
|
bold: true,
|
|
color: colors.white,
|
|
valign: "middle",
|
|
fontFace: "Calibri",
|
|
margin: 0,
|
|
});
|
|
|
|
slide13.addText(svc.port, {
|
|
x: 3.0,
|
|
y: svc.y + 0.05,
|
|
w: 1.0,
|
|
h: 0.4,
|
|
fontSize: 11,
|
|
color: colors.accent,
|
|
valign: "middle",
|
|
fontFace: "Calibri",
|
|
margin: 0,
|
|
});
|
|
|
|
slide13.addShape(pres.shapes.RECTANGLE, {
|
|
x: 4.3,
|
|
y: svc.y,
|
|
w: 5.1,
|
|
h: 0.5,
|
|
fill: { color: colors.lightBg },
|
|
line: { type: "none" },
|
|
});
|
|
});
|
|
|
|
slide13.addText("Health Checks & Auto-Recovery | Persistent Volumes | Network Isolation", {
|
|
x: 0.6,
|
|
y: 5.3,
|
|
w: 8.8,
|
|
h: 0.35,
|
|
fontSize: 11,
|
|
color: colors.text,
|
|
align: "center",
|
|
fontFace: "Calibri",
|
|
});
|
|
|
|
// ===== SLIDE 14: POC SCOPE =====
|
|
let slide14 = pres.addSlide();
|
|
slide14.background = { color: colors.lightBg };
|
|
|
|
addTitleWithBar(slide14, "POC Scope - Resort License Demo");
|
|
|
|
// In-scope column
|
|
slide14.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0.5,
|
|
y: 1.2,
|
|
w: 4.4,
|
|
h: 4.0,
|
|
fill: { color: colors.white },
|
|
line: { type: "none" },
|
|
shadow: makeShadow(),
|
|
});
|
|
|
|
slide14.addText("In-Scope Features", {
|
|
x: 0.7,
|
|
y: 1.35,
|
|
w: 4.0,
|
|
h: 0.4,
|
|
fontSize: 14,
|
|
bold: true,
|
|
color: colors.success,
|
|
fontFace: "Calibri",
|
|
});
|
|
|
|
slide14.addText(
|
|
[
|
|
{ text: "End-to-end license workflow", options: { bullet: true, breakLine: true } },
|
|
{ text: "3-department approval chain", options: { bullet: true, breakLine: true } },
|
|
{ text: "Document upload & storage", options: { bullet: true, breakLine: true } },
|
|
{ text: "NFT minting on Besu", options: { bullet: true, breakLine: true } },
|
|
{ text: "API endpoints", options: { bullet: true, breakLine: true } },
|
|
{ text: "Basic UI workflows", options: { bullet: true } },
|
|
],
|
|
{
|
|
x: 0.8,
|
|
y: 1.85,
|
|
w: 4.0,
|
|
h: 3.1,
|
|
fontSize: 11,
|
|
color: colors.text,
|
|
fontFace: "Calibri",
|
|
}
|
|
);
|
|
|
|
// Out-of-scope column
|
|
slide14.addShape(pres.shapes.RECTANGLE, {
|
|
x: 5.1,
|
|
y: 1.2,
|
|
w: 4.4,
|
|
h: 4.0,
|
|
fill: { color: colors.white },
|
|
line: { type: "none" },
|
|
shadow: makeShadow(),
|
|
});
|
|
|
|
slide14.addText("Out-of-Scope (Future)", {
|
|
x: 5.3,
|
|
y: 1.35,
|
|
w: 4.0,
|
|
h: 0.4,
|
|
fontSize: 14,
|
|
bold: true,
|
|
color: "FF6B6B",
|
|
fontFace: "Calibri",
|
|
});
|
|
|
|
slide14.addText(
|
|
[
|
|
{ text: "Real DigiLocker integration", options: { bullet: true, breakLine: true } },
|
|
{ text: "Multi-language support", options: { bullet: true, breakLine: true } },
|
|
{ text: "Advanced analytics", options: { bullet: true, breakLine: true } },
|
|
{ text: "Mobile applications", options: { bullet: true, breakLine: true } },
|
|
{ text: "Scaling to production", options: { bullet: true, breakLine: true } },
|
|
{ text: "External API integrations", options: { bullet: true } },
|
|
],
|
|
{
|
|
x: 5.3,
|
|
y: 1.85,
|
|
w: 4.0,
|
|
h: 3.1,
|
|
fontSize: 11,
|
|
color: colors.text,
|
|
fontFace: "Calibri",
|
|
}
|
|
);
|
|
|
|
// ===== SLIDE 15: SUCCESS CRITERIA =====
|
|
let slide15 = pres.addSlide();
|
|
slide15.background = { color: colors.white };
|
|
|
|
addTitleWithBar(slide15, "Success Criteria");
|
|
|
|
const criteria = [
|
|
{
|
|
title: "Complete Workflow",
|
|
metric: "100%",
|
|
desc: "End-to-end document flow functional",
|
|
},
|
|
{
|
|
title: "Department Integration",
|
|
metric: "3",
|
|
desc: "3 departments successfully integrated",
|
|
},
|
|
{
|
|
title: "Version Control",
|
|
metric: "100%",
|
|
desc: "Document versioning operational",
|
|
},
|
|
{
|
|
title: "Visual Builder",
|
|
metric: "MVP",
|
|
desc: "Workflow configuration UI ready",
|
|
},
|
|
];
|
|
|
|
let critX = 0.55;
|
|
criteria.forEach((crit) => {
|
|
const boxW = 2.15;
|
|
|
|
slide15.addShape(pres.shapes.RECTANGLE, {
|
|
x: critX,
|
|
y: 1.3,
|
|
w: boxW,
|
|
h: 3.5,
|
|
fill: { color: colors.white },
|
|
line: { type: "none" },
|
|
shadow: makeShadow(),
|
|
});
|
|
|
|
slide15.addText(crit.metric, {
|
|
x: critX,
|
|
y: 1.5,
|
|
w: boxW,
|
|
h: 0.7,
|
|
fontSize: 36,
|
|
bold: true,
|
|
color: colors.accent,
|
|
align: "center",
|
|
fontFace: "Calibri",
|
|
});
|
|
|
|
slide15.addText(crit.title, {
|
|
x: critX + 0.15,
|
|
y: 2.3,
|
|
w: boxW - 0.3,
|
|
h: 0.8,
|
|
fontSize: 12,
|
|
bold: true,
|
|
color: colors.text,
|
|
align: "center",
|
|
fontFace: "Calibri",
|
|
});
|
|
|
|
slide15.addText(crit.desc, {
|
|
x: critX + 0.15,
|
|
y: 3.2,
|
|
w: boxW - 0.3,
|
|
h: 1.4,
|
|
fontSize: 10,
|
|
color: colors.lightText,
|
|
align: "center",
|
|
valign: "middle",
|
|
fontFace: "Calibri",
|
|
});
|
|
|
|
critX += boxW + 0.15;
|
|
});
|
|
|
|
// ===== SLIDE 16: TIMELINE & NEXT STEPS =====
|
|
let slide16 = pres.addSlide();
|
|
slide16.background = { color: colors.lightBg };
|
|
|
|
addTitleWithBar(slide16, "Timeline & Next Steps");
|
|
|
|
const timeline = [
|
|
{ phase: "Phase 1", duration: "Week 1-2", milestone: "Architecture Setup" },
|
|
{ phase: "Phase 2", duration: "Week 3-4", milestone: "Core Development" },
|
|
{ phase: "Phase 3", duration: "Week 5-6", milestone: "Integration & Testing" },
|
|
{ phase: "Phase 4", duration: "Week 7-8", milestone: "POC Demonstration" },
|
|
];
|
|
|
|
let timelineY = 1.4;
|
|
timeline.forEach((item, idx) => {
|
|
slide16.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0.6,
|
|
y: timelineY,
|
|
w: 8.8,
|
|
h: 0.75,
|
|
fill: { color: colors.white },
|
|
line: { type: "none" },
|
|
shadow: makeShadow(),
|
|
});
|
|
|
|
slide16.addText(item.phase, {
|
|
x: 0.8,
|
|
y: timelineY + 0.12,
|
|
w: 1.2,
|
|
h: 0.5,
|
|
fontSize: 12,
|
|
bold: true,
|
|
color: colors.primary,
|
|
valign: "middle",
|
|
fontFace: "Calibri",
|
|
margin: 0,
|
|
});
|
|
|
|
slide16.addText(item.duration, {
|
|
x: 2.2,
|
|
y: timelineY + 0.12,
|
|
w: 1.8,
|
|
h: 0.5,
|
|
fontSize: 11,
|
|
color: colors.text,
|
|
valign: "middle",
|
|
fontFace: "Calibri",
|
|
margin: 0,
|
|
});
|
|
|
|
slide16.addText(item.milestone, {
|
|
x: 4.2,
|
|
y: timelineY + 0.12,
|
|
w: 5.0,
|
|
h: 0.5,
|
|
fontSize: 11,
|
|
color: colors.text,
|
|
valign: "middle",
|
|
fontFace: "Calibri",
|
|
margin: 0,
|
|
});
|
|
|
|
timelineY += 0.95;
|
|
});
|
|
|
|
// Next steps
|
|
slide16.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0.6,
|
|
y: 4.5,
|
|
w: 8.8,
|
|
h: 0.85,
|
|
fill: { color: colors.accent },
|
|
line: { type: "none" },
|
|
});
|
|
|
|
slide16.addText(
|
|
[
|
|
{ text: "Next Steps: ", options: { bold: true, breakLine: true } },
|
|
{ text: "Stakeholder approval", options: { bullet: true, breakLine: true } },
|
|
{ text: "Resource allocation", options: { bullet: true, breakLine: true } },
|
|
{ text: "Project kickoff", options: { bullet: true } },
|
|
],
|
|
{
|
|
x: 0.9,
|
|
y: 4.55,
|
|
w: 8.2,
|
|
h: 0.75,
|
|
fontSize: 11,
|
|
color: colors.white,
|
|
fontFace: "Calibri",
|
|
}
|
|
);
|
|
|
|
// ===== SLIDE 17: Q&A =====
|
|
let slide17 = pres.addSlide();
|
|
slide17.background = { color: colors.darkBg };
|
|
|
|
slide17.addShape(pres.shapes.RECTANGLE, {
|
|
x: 2.0,
|
|
y: 1.8,
|
|
w: 6.0,
|
|
h: 2.0,
|
|
fill: { color: colors.primary },
|
|
line: { type: "none" },
|
|
shadow: makeShadow(),
|
|
});
|
|
|
|
slide17.addText("Questions?", {
|
|
x: 2.0,
|
|
y: 2.2,
|
|
w: 6.0,
|
|
h: 0.8,
|
|
fontSize: 54,
|
|
bold: true,
|
|
color: colors.accent,
|
|
align: "center",
|
|
fontFace: "Calibri",
|
|
});
|
|
|
|
slide17.addText("Thank you for your attention", {
|
|
x: 2.0,
|
|
y: 3.1,
|
|
w: 6.0,
|
|
h: 0.4,
|
|
fontSize: 18,
|
|
color: colors.white,
|
|
align: "center",
|
|
fontFace: "Calibri",
|
|
});
|
|
|
|
// Save presentation
|
|
pres.writeFile({ fileName: "/sessions/cool-elegant-faraday/mnt/Goa-GEL/Goa-GEL-Architecture-Presentation.pptx" });
|
|
|
|
console.log("Presentation created successfully!");
|