Skip to content

Commit 52c1fa9

Browse files
committed
init
1 parent 945eb71 commit 52c1fa9

File tree

5 files changed

+202
-0
lines changed

5 files changed

+202
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Local Netlify folder
2+
.netlify

index.html

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Document</title>
7+
<script src="main.js" type="module"></script>
8+
</head>
9+
<body>
10+
<h1>Netlify Logs</h1>
11+
<input type="text" id="deployId" placeholder="Deploy ID" />
12+
<input type="text" id="siteId" placeholder="Site ID" />
13+
<button id="connect">Connect</button>
14+
<div id="logs"></div>
15+
</body>
16+
</html>

main.js

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
async function getAccessControlToken(options) {
2+
const tokenResp = await fetch(
3+
`.netlify/functions/generate-token?deploy_id=${options.deployId}&site_id=${options.siteId}`,
4+
{
5+
credentials: "include",
6+
}
7+
);
8+
9+
if (tokenResp.status !== 200) {
10+
throw new Error("failed to access control token for user");
11+
}
12+
13+
const { accessControlToken } = await tokenResp.json();
14+
15+
return accessControlToken;
16+
}
17+
18+
class NetlifyLogsService {
19+
constructor(options = {}) {
20+
this.options = options;
21+
this.logs = [];
22+
this.shouldReconnect = true;
23+
}
24+
25+
destroy() {
26+
this.shouldReconnect = false;
27+
window.clearInterval(this.reconnectTimeout);
28+
this.notifyLogsUpdated.cancel();
29+
this.ws.close();
30+
}
31+
32+
connect(options) {
33+
const deployId = options.deployId || this.options.deployId;
34+
const siteId = options.siteId || this.options.siteId;
35+
this.ws = new WebSocket("wss://socketeer.services.netlify.com/build/logs");
36+
this.ws.addEventListener("open", () => {
37+
getAccessControlToken({
38+
deployId,
39+
siteId,
40+
})
41+
.then((accessToken) => {
42+
this.ws.send(
43+
JSON.stringify({
44+
deploy_id: deployId,
45+
site_id: siteId,
46+
access_token: accessToken,
47+
})
48+
);
49+
})
50+
.catch((error) => {
51+
console.error(
52+
"NetlifyLogsService failed to get access control token",
53+
error
54+
);
55+
});
56+
});
57+
this.ws.addEventListener("message", (event) => {
58+
try {
59+
const data = JSON.parse(event.data);
60+
const ts = new Date(data.ts).getTime();
61+
62+
if (data.type === "error") {
63+
throw data;
64+
}
65+
66+
this.logs ??= [];
67+
this.logs.push({
68+
id: `${ts}${this.logs.length}`,
69+
timestamp: ts,
70+
message: data.message,
71+
});
72+
this.notifyLogsUpdated();
73+
} catch (e) {
74+
if (e?.type === "error" && e.status === 401) {
75+
console.error("NetlifyLogsService no permission");
76+
this.options.onForbidden?.();
77+
return;
78+
}
79+
console.error(`NetlifyLogsService can't decode socket message`, e);
80+
}
81+
});
82+
this.ws.addEventListener("close", () => {
83+
console.info(`NetlifyLogsService socket closed`);
84+
if (this.shouldReconnect) {
85+
this.reconnectTimeout = window.setTimeout(
86+
() => this.connect(),
87+
this.options.reconnect ?? 1000
88+
);
89+
}
90+
});
91+
this.ws.addEventListener("error", (error) => {
92+
console.error(`NetlifyLogsService socket got error`, error);
93+
this.ws.close();
94+
});
95+
return this.ws;
96+
}
97+
98+
notifyLogsUpdated = (function () {
99+
let timeout;
100+
return function () {
101+
clearTimeout(timeout);
102+
timeout = setTimeout(() => {
103+
this.options.onLogsUpdated?.([...(this.logs ?? [])]);
104+
}, 250);
105+
};
106+
})();
107+
}
108+
109+
const logsService = new NetlifyLogsService({
110+
deployId: "your-deploy-id",
111+
siteId: "your-site-id",
112+
onLogsUpdated: (logs) => {
113+
// Handle updated logs
114+
},
115+
onForbidden: () => {
116+
// Handle forbidden access
117+
},
118+
reconnect: 2000, // Optional reconnect timeout in ms
119+
});
120+
121+
// Initialize the logs service
122+
const netlifyLogs = new NetlifyLogsService({
123+
onLogsUpdated: (logs) => {
124+
// Update UI with new logs
125+
const logContainer = document.querySelector("#logs");
126+
if (logContainer) {
127+
logContainer.innerHTML = logs
128+
.map(
129+
(log) => `
130+
<div class="log-entry">
131+
<span class="timestamp">${new Date(
132+
log.timestamp
133+
).toLocaleTimeString()}</span>
134+
<span class="message">${log.message}</span>
135+
</div>
136+
`
137+
)
138+
.join("");
139+
}
140+
},
141+
onForbidden: () => {
142+
console.error("Access forbidden - please check your credentials");
143+
// Optionally show error message to user
144+
alert("Unable to access logs - permission denied");
145+
},
146+
reconnect: 3000,
147+
});
148+
149+
document.getElementById("connect").addEventListener("click", () => {
150+
const deployId = document.getElementById("deployId").value;
151+
const siteId = document.getElementById("siteId").value;
152+
netlifyLogs.connect({ deployId, siteId });
153+
});
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export default async (req, context) => {
2+
const url = new URL(req.url);
3+
const siteId = url.searchParams.get("site_id");
4+
const deployId = url.searchParams.get("deploy_id");
5+
try {
6+
const response = await fetch(
7+
`https://app.netlify.com/access-control/generate-access-control-token?site_id=${siteId}&deploy_id=${deployId}`,
8+
{
9+
method: "GET",
10+
headers: {
11+
"Content-Type": "application/json",
12+
Authorization: `Bearer ${process.env.NETLIFY_ACCESS_CONTROL_TOKEN}`,
13+
},
14+
}
15+
);
16+
const data = await response.json();
17+
return new Response(JSON.stringify(data));
18+
} catch (error) {
19+
console.error(error);
20+
return new Response("Error generating token", { status: 500 });
21+
}
22+
};

package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "netlify-logs",
3+
"version": "1.0.0",
4+
"description": "A simple tool to view Netlify logs",
5+
"main": "index.html",
6+
"scripts": {
7+
"start": "netlify serve"
8+
}
9+
}

0 commit comments

Comments
 (0)