-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathco2.ts
96 lines (86 loc) · 2.92 KB
/
co2.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import { CO2, db, eq } from "astro:db";
import { defineMiddleware } from "astro:middleware";
// Source: memory-intensive load for c5 processor
// https://medium.com/teads-engineering/estimating-aws-ec2-instances-power-consumption-c9745e347959
const awsTotalMachineWatts = 174;
// Fudge factor since we're not using the full machine
const awsUtilization = 0.1;
const awskW = (awsTotalMachineWatts * awsUtilization) / 1000;
// Source: cloud carbon footprint
// https://github.com/cloud-carbon-footprint/cloud-carbon-footprint/blob/e48c659f6dafc8b783e570053024f28b88aafc79/microsite/docs/Methodology.md#aws-2
const awsCO2g_per_kWh = 0.000415755 * Math.pow(10, 6);
// Source: global averages per Sustainable Web Design model
// https://sustainablewebdesign.org/estimating-digital-emissions/#faq
const userOperationalkWh_per_GB =
(421 * Math.pow(10, 9)) / (5.29 * Math.pow(10, 12));
const userEmbodiedkWh_per_GB =
(430 * Math.pow(10, 9)) / (5.29 * Math.pow(10, 12));
const userkWh_per_GB = userOperationalkWh_per_GB + userEmbodiedkWh_per_GB;
const userCO2g_per_kWh = 494;
export const co2 = defineMiddleware(async (context, next) => {
if (context.url.pathname.endsWith("_actions/getCo2")) return next();
const referer = context.request.headers.get("Referer");
// If no referer, we don't have a key to track for co2 analytics
if (!referer) return next();
const start = performance.now();
const response = await next();
if (context.url.href === referer) {
await db.delete(CO2).where(eq(CO2.referer, referer));
}
if (!response.body) {
await db
.insert(CO2)
.values({
route: context.url.pathname,
referer,
client: 0,
server: getServerCO2(start),
})
.onConflictDoUpdate({
target: CO2.route,
set: {
referer,
client: 0,
server: getServerCO2(start),
},
});
return response;
}
async function* render() {
let clientBytes = 0;
for await (const chunk of response.body as ReadableStream<ArrayBuffer>) {
clientBytes += chunk.byteLength;
yield chunk;
}
await db
.insert(CO2)
.values({
route: context.url.pathname,
referer,
client: getClientCO2(clientBytes),
server: getServerCO2(start),
})
.onConflictDoUpdate({
target: CO2.route,
set: {
referer,
client: getClientCO2(clientBytes),
server: getServerCO2(start),
},
});
}
// @ts-expect-error generator not assignable to ReadableStream
return new Response(render(), { headers: response.headers });
});
function getServerCO2(start: number) {
const time = performance.now() - start;
const hours = time / 1000 / 3600;
const kWh = hours * awskW;
const co2 = kWh * awsCO2g_per_kWh;
return co2;
}
function getClientCO2(bytes: number) {
const kWh = (bytes / Math.pow(10, 12)) * userkWh_per_GB;
const co2 = kWh * userCO2g_per_kWh;
return co2;
}