mirror of
https://github.com/dataforcanada/d4c-service-main-site.git
synced 2026-06-13 14:00:51 +02:00
Update infrastructure & operating costs, added smart node 03. Updated high-level overview to D3.js based map. Colors used are those of countries under jurisdiction. Totally vibe coded this map, I have not touched d3.js since 2014 😂
This commit is contained in:
@@ -6,106 +6,320 @@ sidebar:
|
||||
|
||||
## High-Level Overview
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
%% ---------------------------------------------------------
|
||||
%% STYLING
|
||||
%% ---------------------------------------------------------
|
||||
style Canada_Region fill:#ffe6e6,stroke:#ff0000,stroke-width:2px
|
||||
style USA_Region fill:#e6f2ff,stroke:#0066cc,stroke-width:2px
|
||||
style Europe_Region fill:#e6ffe6,stroke:#009900,stroke-width:2px
|
||||
|
||||
%% Highlight Primary Storage
|
||||
style R2 fill:#fffde7,stroke:#fbc02d,stroke-width:4px
|
||||
{{< rawhtml >}}
|
||||
<style>
|
||||
.land {
|
||||
fill: #ddd;
|
||||
stroke: #999;
|
||||
stroke-width: 0.5;
|
||||
}
|
||||
|
||||
%% ---------------------------------------------------------
|
||||
%% REGION: CANADA
|
||||
%% ---------------------------------------------------------
|
||||
subgraph Canada_Region ["🇨🇦 Physical Location: Canada"]
|
||||
direction TB
|
||||
NodeTO["Smart Node 01
|
||||
Location: Toronto, CA
|
||||
Specs: 50Gbps / 50Gbps, 950GB Flash Storage
|
||||
Jurisdiction: Singapore"]
|
||||
|
||||
IA_Van["Internet Archive Mirror
|
||||
Location: Vancouver, CA
|
||||
Protocol: HTTP
|
||||
Jurisdiction: USA"]
|
||||
end
|
||||
.graticule {
|
||||
fill: none;
|
||||
stroke: #ccc;
|
||||
stroke-width: 0.5;
|
||||
}
|
||||
|
||||
%% ---------------------------------------------------------
|
||||
%% REGION: USA
|
||||
%% ---------------------------------------------------------
|
||||
subgraph USA_Region ["🇺🇸 Physical Location: USA"]
|
||||
direction TB
|
||||
SourceCoop["Source Cooperative
|
||||
(Mirror)
|
||||
AWS S3 (us-west-2)
|
||||
Location: Oregon, USA
|
||||
Protocol: HTTP
|
||||
Jurisdiction: USA"]
|
||||
|
||||
R2["☁️ Cloudflare R2
|
||||
(Primary Object Storage)
|
||||
Location: Eastern North America
|
||||
Protocol: HTTP
|
||||
Jurisdiction: USA"]
|
||||
|
||||
IA_SF["The Internet Archive
|
||||
Location: San Francisco, California, USA
|
||||
Protocol: HTTP
|
||||
Jurisdiction: USA"]
|
||||
|
||||
Netcup["VPS 01
|
||||
Location: Manassas, Virginia, USA
|
||||
Specs: 2.5Gbps / 2.5Gbps, 512GB Flash Storage
|
||||
Protocol: HTTP & P2P
|
||||
Jurisdiction: Germany"]
|
||||
end
|
||||
.connection {
|
||||
fill: none;
|
||||
stroke-width: 1.5;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
%% ---------------------------------------------------------
|
||||
%% REGION: EUROPE
|
||||
%% ---------------------------------------------------------
|
||||
subgraph Europe_Region ["🇪🇺 Physical Location: Europe"]
|
||||
direction TB
|
||||
subgraph Netherlands ["🇳🇱 Netherlands"]
|
||||
NodeAMS["Smart Node 02
|
||||
Location: Amsterdam, NL
|
||||
Specs: 50Gbps / 50Gbps, 950GB Flash Storage
|
||||
Jurisdiction: Singapore"]
|
||||
end
|
||||
|
||||
subgraph Switzerland ["🇨🇭 Switzerland"]
|
||||
Zenodo["Zenodo
|
||||
Location: Geneva, CH
|
||||
(Replicated in Budapest, HU)
|
||||
Protocol: HTTP
|
||||
Jurisdiction: Switzerland"]
|
||||
end
|
||||
subgraph Hungary["🇭🇺 Hungary"]
|
||||
ZenodoMirror["Zenodo Mirror
|
||||
Location: Budapest, HU
|
||||
Protocol: HTTP
|
||||
Jurisdiction: Switzerland"]
|
||||
end
|
||||
end
|
||||
.node {
|
||||
cursor: pointer;
|
||||
stroke: #fff;
|
||||
stroke-width: 1;
|
||||
}
|
||||
|
||||
%% ---------------------------------------------------------
|
||||
%% CONNECTIONS
|
||||
%% ---------------------------------------------------------
|
||||
|
||||
NodeTO <==>|P2P| NodeAMS
|
||||
IA_SF -.->|Internal Replication| IA_Van
|
||||
Zenodo -.->|Internal Replication| ZenodoMirror
|
||||
|
||||
NodeTO -.->|HTTP Pull| SourceCoop
|
||||
NodeTO ==> R2
|
||||
NodeTO -.->|HTTP Pull| Zenodo
|
||||
NodeTO -.->|HTTP Pull| IA_SF
|
||||
NodeTO -.->|HTTP or P2P| Netcup
|
||||
.tooltip {
|
||||
position: absolute;
|
||||
background: white;
|
||||
border: 1px solid #ccc;
|
||||
padding: 10px;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
||||
max-width: 350px;
|
||||
}
|
||||
|
||||
```
|
||||
.tooltip.visible {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.tooltip-title {
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.tooltip-detail {
|
||||
font-size: 14px;
|
||||
margin: 2px 0;
|
||||
}
|
||||
</style>
|
||||
<svg id="map"></svg>
|
||||
<div class="tooltip"></div>
|
||||
<script type="module">
|
||||
import * as d3 from 'https://cdn.jsdelivr.net/npm/d3@7/+esm';
|
||||
|
||||
// Infrastructure nodes
|
||||
const nodes = [
|
||||
{
|
||||
id: 'smart-node-01',
|
||||
name: 'Smart Node 01',
|
||||
location: 'Toronto, Ontario, Canada',
|
||||
coords: [-79.38, 43.65],
|
||||
specs: '50Gbps / 50Gbps, 950GB Flash Storage',
|
||||
jurisdiction: 'Singapore',
|
||||
color: '#9966CC'
|
||||
},
|
||||
{
|
||||
id: 'vancouver',
|
||||
name: 'Internet Archive Mirror',
|
||||
location: 'Vancouver, Canada',
|
||||
coords: [-123.12, 49.28],
|
||||
protocol: 'HTTP',
|
||||
jurisdiction: 'USA',
|
||||
color: '#002147'
|
||||
},
|
||||
{
|
||||
id: 'source-cooperative-oregon',
|
||||
name: 'Source Cooperative',
|
||||
location: 'Oregon, USA',
|
||||
coords: [-122.68, 45.52],
|
||||
specs: 'AWS S3 (us-west-2)',
|
||||
protocol: 'HTTP',
|
||||
jurisdiction: 'USA',
|
||||
color: '#002147'
|
||||
},
|
||||
{
|
||||
id: 'r2',
|
||||
name: 'Cloudflare R2',
|
||||
location: 'Eastern North America',
|
||||
coords: [-73.94, 40.71],
|
||||
specs: 'Primary Object Storage',
|
||||
protocol: 'HTTP',
|
||||
jurisdiction: 'USA',
|
||||
color: '#002147'
|
||||
},
|
||||
{
|
||||
id: 'san-francisco',
|
||||
name: 'The Internet Archive',
|
||||
location: 'San Francisco, California, USA',
|
||||
coords: [-122.42, 37.77],
|
||||
protocol: 'HTTP',
|
||||
jurisdiction: 'USA',
|
||||
color: '#002147'
|
||||
},
|
||||
{
|
||||
id: 'vps-01',
|
||||
name: 'VPS 01',
|
||||
location: 'Manassas, Virginia, USA',
|
||||
coords: [-77.48, 38.75],
|
||||
specs: '2.5Gbps / 2.5Gbps, 512GB Flash Storage',
|
||||
protocol: 'HTTP & P2P',
|
||||
jurisdiction: 'Germany',
|
||||
color: '#FFCC00'
|
||||
},
|
||||
{
|
||||
id: 'smart-node-02',
|
||||
name: 'Smart Node 02',
|
||||
location: 'Amsterdam, Netherlands',
|
||||
coords: [4.90, 52.37],
|
||||
specs: '50Gbps / 50Gbps, 950GB Flash Storage',
|
||||
jurisdiction: 'Singapore',
|
||||
color: '#9966CC'
|
||||
},
|
||||
{
|
||||
id: 'smart-node-03',
|
||||
name: 'Smart Node 03',
|
||||
location: 'Amsterdam, Netherlands',
|
||||
coords: [4.90, 52.37],
|
||||
specs: '50Gbps / 50Gbps, 6TB HDD Storage',
|
||||
jurisdiction: 'Singapore',
|
||||
color: '#9966CC'
|
||||
},
|
||||
{
|
||||
id: 'geneva',
|
||||
name: 'Zenodo',
|
||||
location: 'Geneva, Switzerland',
|
||||
coords: [6.14, 46.20],
|
||||
specs: 'Replicated in Budapest',
|
||||
protocol: 'HTTP',
|
||||
jurisdiction: 'Switzerland',
|
||||
color: '#D52B1E'
|
||||
},
|
||||
{
|
||||
id: 'budapest',
|
||||
name: 'Zenodo Mirror',
|
||||
location: 'Budapest, Hungary',
|
||||
coords: [19.04, 47.50],
|
||||
protocol: 'HTTP',
|
||||
jurisdiction: 'Switzerland',
|
||||
color: '#D52B1E'
|
||||
}
|
||||
];
|
||||
|
||||
// Connections between nodes
|
||||
const connections = [
|
||||
{ source: 'smart-node-01', target: 'smart-node-02', color: '#9966CC' },
|
||||
{ source: 'smart-node-01', target: 'smart-node-03', color: '#9966CC' },
|
||||
{ source: 'san-francisco', target: 'vancouver', color: '#999' },
|
||||
{ source: 'geneva', target: 'budapest', color: '#D52B1E' },
|
||||
{ source: 'smart-node-01', target: 'source-cooperative-oregon', color: '#9966CC' },
|
||||
{ source: 'smart-node-01', target: 'r2', color: '#9966CC' },
|
||||
{ source: 'smart-node-01', target: 'geneva', color: '#9966CC' },
|
||||
{ source: 'smart-node-01', target: 'san-francisco', color: '#9966CC' },
|
||||
{ source: 'smart-node-01', target: 'vps-01', color: '#9966CC' },
|
||||
{ source: 'smart-node-02', target: 'source-cooperative-oregon', color: '#9966CC' },
|
||||
{ source: 'smart-node-02', target: 'r2', color: '#9966CC' },
|
||||
{ source: 'smart-node-02', target: 'geneva', color: '#9966CC' },
|
||||
{ source: 'smart-node-02', target: 'san-francisco', color: '#9966CC' },
|
||||
{ source: 'smart-node-02', target: 'vps-01', color: '#9966CC' },
|
||||
{ source: 'smart-node-03', target: 'source-cooperative-oregon', color: '#9966CC' },
|
||||
{ source: 'smart-node-03', target: 'r2', color: '#9966CC' },
|
||||
{ source: 'smart-node-03', target: 'geneva', color: '#9966CC' },
|
||||
{ source: 'smart-node-03', target: 'san-francisco', color: '#9966CC' },
|
||||
{ source: 'smart-node-03', target: 'vps-01', color: '#9966CC' },
|
||||
{ source: 'vps-01', target: 'smart-node-02', color: '#FFCC00' },
|
||||
{ source: 'vps-01', target: 'smart-node-03', color: '#FFCC00' },
|
||||
{ source: 'vps-01', target: 'r2', color: '#FFCC00' },
|
||||
{ source: 'vps-01', target: 'san-francisco', color: '#FFCC00' },
|
||||
{ source: 'vps-01', target: 'source-cooperative-oregon', color: '#FFCC00' },
|
||||
{ source: 'vps-01', target: 'geneva', color: '#FFCC00' }
|
||||
];
|
||||
|
||||
const width = 960;
|
||||
const height = 600;
|
||||
|
||||
const svg = d3.select('#map')
|
||||
.attr('width', width)
|
||||
.attr('height', height);
|
||||
|
||||
const projection = d3.geoNaturalEarth1()
|
||||
.scale(180)
|
||||
.translate([width / 2, height / 2]);
|
||||
|
||||
const path = d3.geoPath().projection(projection);
|
||||
const graticule = d3.geoGraticule();
|
||||
|
||||
const g = svg.append('g');
|
||||
|
||||
// Draw graticule
|
||||
g.append('path')
|
||||
.datum(graticule)
|
||||
.attr('class', 'graticule')
|
||||
.attr('d', path);
|
||||
|
||||
// Group nodes by location to handle overlapping
|
||||
const nodesByLocation = {};
|
||||
nodes.forEach(node => {
|
||||
const key = node.coords.join(',');
|
||||
if (!nodesByLocation[key]) {
|
||||
nodesByLocation[key] = [];
|
||||
}
|
||||
nodesByLocation[key].push(node);
|
||||
});
|
||||
|
||||
// Calculate offsets for nodes at same location
|
||||
const nodeOffsets = {};
|
||||
Object.entries(nodesByLocation).forEach(([key, nodesAtLocation]) => {
|
||||
if (nodesAtLocation.length === 1) {
|
||||
nodeOffsets[nodesAtLocation[0].id] = { dx: 0, dy: 0 };
|
||||
} else {
|
||||
// Create circular offset pattern for multiple nodes
|
||||
const radius = 8;
|
||||
nodesAtLocation.forEach((node, i) => {
|
||||
const angle = (i / nodesAtLocation.length) * 2 * Math.PI;
|
||||
nodeOffsets[node.id] = {
|
||||
dx: Math.cos(angle) * radius,
|
||||
dy: Math.sin(angle) * radius
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Tooltip
|
||||
const tooltip = d3.select('.tooltip');
|
||||
|
||||
function showTooltip(event, nodesAtLocation) {
|
||||
let html = '';
|
||||
nodesAtLocation.forEach((node, i) => {
|
||||
if (i > 0) html += '<hr style="margin: 8px 0; border: none; border-top: 1px solid #ccc;">';
|
||||
html += `<div class="tooltip-title">${node.name}</div>`;
|
||||
html += `<div class="tooltip-detail">Location: ${node.location}</div>`;
|
||||
if (node.specs) html += `<div class="tooltip-detail">Specs: ${node.specs}</div>`;
|
||||
if (node.protocol) html += `<div class="tooltip-detail">Protocol: ${node.protocol}</div>`;
|
||||
html += `<div class="tooltip-detail">Jurisdiction: ${node.jurisdiction}</div>`;
|
||||
});
|
||||
|
||||
tooltip
|
||||
.html(html)
|
||||
.classed('visible', true)
|
||||
.style('left', (event.pageX + 15) + 'px')
|
||||
.style('top', (event.pageY + 15) + 'px');
|
||||
}
|
||||
|
||||
function hideTooltip() {
|
||||
tooltip.classed('visible', false);
|
||||
}
|
||||
|
||||
// Load world map (GeoJSON)
|
||||
d3.json('https://raw.githubusercontent.com/nvkelso/natural-earth-vector/master/geojson/ne_110m_land.geojson').then(landData => {
|
||||
// Draw land
|
||||
g.append('g')
|
||||
.selectAll('path')
|
||||
.data(landData.features)
|
||||
.enter().append('path')
|
||||
.attr('class', 'land')
|
||||
.attr('d', path);
|
||||
|
||||
// Draw connections
|
||||
const connectionGroup = g.append('g');
|
||||
connections.forEach(conn => {
|
||||
const source = nodes.find(n => n.id === conn.source);
|
||||
const target = nodes.find(n => n.id === conn.target);
|
||||
if (source && target) {
|
||||
connectionGroup.append('path')
|
||||
.datum({
|
||||
type: 'LineString',
|
||||
coordinates: [source.coords, target.coords]
|
||||
})
|
||||
.attr('class', 'connection')
|
||||
.attr('d', path)
|
||||
.attr('stroke', conn.color);
|
||||
}
|
||||
});
|
||||
|
||||
// Draw nodes with offsets for overlapping positions
|
||||
const nodeGroup = g.append('g');
|
||||
|
||||
// Draw each node with its offset
|
||||
nodes.forEach(node => {
|
||||
const coords = projection(node.coords);
|
||||
const offset = nodeOffsets[node.id];
|
||||
const locationKey = node.coords.join(',');
|
||||
const nodesAtThisLocation = nodesByLocation[locationKey];
|
||||
|
||||
nodeGroup.append('circle')
|
||||
.attr('class', 'node')
|
||||
.attr('cx', coords[0] + offset.dx)
|
||||
.attr('cy', coords[1] + offset.dy)
|
||||
.attr('r', 4)
|
||||
.attr('fill', node.color)
|
||||
.on('mouseover', (event) => showTooltip(event, nodesAtThisLocation))
|
||||
.on('mouseout', hideTooltip)
|
||||
.on('mousemove', (event) => {
|
||||
tooltip
|
||||
.style('left', (event.pageX + 15) + 'px')
|
||||
.style('top', (event.pageY + 15) + 'px');
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{{< /rawhtml >}}
|
||||
|
||||
## Infrastructure & Operating Costs
|
||||
|
||||
@@ -115,8 +329,9 @@ Jurisdiction: Switzerland"]
|
||||
| **CDN Services** | [Object Storage - Cloudflare Details](https://www.cloudflare.com/products/r2/) & [Serverless - Cloudflare Details](https://www.cloudflare.com/en-ca/plans/developer-platform/) (Variable) | $32.71 | $23.93 | €20.26 |
|
||||
| **Smart Node 01** | [Decentralized Distribution - SlashN Services Details](https://ultra.cc/#plan-pricing) - Dedicated <abbr title="Peer-to-Peer">P2P</abbr> client | $28.98 | $21.21 | €17.95 |
|
||||
| **Smart Node 02** | [Decentralized Distribution - SlashN Services Details](https://ultra.cc/#plan-pricing) - Dedicated <abbr title="Peer-to-Peer">P2P</abbr> client | $28.98 | $21.21 | €17.95 |
|
||||
| **Smart Node 03** | [Decentralized Distribution - SlashN Services Details](https://ultra.cc/#plan-pricing) - Dedicated <abbr title="Peer-to-Peer">P2P</abbr> client | $27.31 | $20.13 | €16.95 |
|
||||
| **VPS 01** | [Geospatial Services - Netcup Details](https://www.netcup.com/en/server/root-server) - ARM64 | $14.64 | $10.72 | €9.07 |
|
||||
| **TOTAL** | **Monthly Run Rate** | **$136.21** | **$99.67** | **€84.36** |
|
||||
| **TOTAL** | **Monthly Run Rate** | **$163.52** | **$119.80** | **€101.31** |
|
||||
|
||||
**Note:** Currency conversions are based on rates from February 16, 2026.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user