-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.js
More file actions
138 lines (131 loc) · 9.79 KB
/
server.js
File metadata and controls
138 lines (131 loc) · 9.79 KB
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
const express = require("express");
const path = require("path");
const fs = require("fs");
const app = express();
const PORT = process.env.PORT || 8090;
const ROOT = __dirname;
function getWorkshopTitle(dir) {
try {
const html = fs.readFileSync(path.join(ROOT, dir, "index.html"), "utf8");
const match = html.match(/<title>(.*?)<\/title>/i);
return match ? match[1].replace(/&/g, "&") : dir;
} catch {
return dir;
}
}
// Serve each workshop folder as static files
app.use(express.static(ROOT, { extensions: ["html"] }));
// Landing page: list available workshops
app.get("/", (_req, res) => {
const workshops = fs
.readdirSync(ROOT, { withFileTypes: true })
.filter(
(d) =>
d.isDirectory() &&
!d.name.startsWith(".") &&
d.name !== "node_modules" &&
fs.existsSync(path.join(ROOT, d.name, "index.html"))
)
.map((d) => ({
slug: d.name,
title: getWorkshopTitle(d.name),
}));
const cards = workshops
.map(
(w) => `
<a href="/${w.slug}/" class="card">
<h2>${w.title}</h2>
<p class="slug">/${w.slug}</p>
</a>`
)
.join("\n");
res.send(`<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Workshops — Crunchloop</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
background: #f6f8fa;
color: #24292e;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
padding: 4em 2em;
}
header {
text-align: center;
margin-bottom: 3em;
}
header h1 {
font-size: 2.4em;
font-weight: 700;
margin-bottom: 0.3em;
}
header p {
color: #586069;
font-size: 1.1em;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1.5em;
width: 100%;
max-width: 900px;
}
.card {
background: #fff;
border: 1px solid #e1e4e8;
border-radius: 12px;
padding: 2em;
text-decoration: none;
color: inherit;
transition: box-shadow 0.2s, border-color 0.2s;
}
.card:hover {
border-color: #0366d6;
box-shadow: 0 4px 12px rgba(3, 102, 214, 0.15);
}
.card h2 {
font-size: 1.3em;
margin-bottom: 0.5em;
}
.card .slug {
color: #586069;
font-size: 0.9em;
font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace;
}
.logo {
margin-bottom: 1.5em;
opacity: 0.7;
}
</style>
</head>
<body>
<header>
<svg class="logo" width="140" height="29" viewBox="0 0 172 36" fill="#24292e" xmlns="http://www.w3.org/2000/svg">
<path d="M0.485735 20.5126C0.485735 16.0319 3.92937 12.6475 8.29169 12.6475C11.8903 12.6475 14.9366 15.0851 15.3959 18.3793L11.9551 18.3793C11.6198 16.945 10.0332 15.8177 8.23533 15.8177C5.73573 15.8177 3.81383 17.7904 3.81383 20.5126C3.81383 23.2348 5.73573 25.1793 8.23533 25.1793C10.0332 25.1793 11.617 24.0802 11.9861 22.4965H15.4016C15.0042 25.9711 11.9241 28.3496 8.29731 28.3496C7.2649 28.3656 6.23982 28.1737 5.28315 27.7852C4.32648 27.3967 3.45779 26.8197 2.72886 26.0884C1.99993 25.3571 1.42567 24.4865 1.04029 23.5286C0.654921 22.5707 0.466321 21.545 0.485735 20.5126Z"/>
<path d="M21.9012 14.9049C22.3104 14.324 22.8545 13.8511 23.4867 13.5267C24.1188 13.2022 24.8203 13.036 25.5308 13.0422H27.0864V16.0011H25.3786C23.2144 16.0011 21.9012 17.3735 21.9012 19.6645L21.9012 27.9524H18.5195L18.5195 13.0422H21.9012V14.9049Z"/>
<path d="M43.0355 27.9522H39.7018V26.4277C39.1411 27.033 38.4616 27.5162 37.7056 27.8468C36.9497 28.1775 36.1336 28.3487 35.3085 28.3496C31.8029 28.3496 29.3906 25.7879 29.3906 21.6116L29.4216 13.042H32.8371L32.8061 21.5186C32.8061 23.6237 33.9953 25.2103 36.1004 25.2103C38.1434 25.2103 39.637 23.7139 39.668 21.3383V13.042H43.0496L43.0355 27.9522Z"/>
<path d="M46.6562 13.042H49.9787V14.5665C50.5388 13.9614 51.2179 13.4785 51.9734 13.1483C52.729 12.818 53.5446 12.6475 54.3692 12.6475C57.8748 12.6475 60.2871 15.2091 60.2871 19.3854V27.9522H56.8688V19.4699C56.8688 17.3677 55.6796 15.7811 53.5773 15.7811C51.5343 15.7811 50.0407 17.2747 50.0407 19.6531V27.9522H46.6591L46.6562 13.042Z"/>
<path d="M63.0287 20.5121C63.0094 19.4789 63.1977 18.4524 63.5824 17.4933C63.9671 16.5342 64.5404 15.6621 65.2683 14.9287C65.9962 14.1953 66.864 13.6154 67.8201 13.2234C68.7763 12.8315 69.8014 12.6355 70.8346 12.647C74.4333 12.647 77.4824 15.0846 77.9389 18.3788L74.4924 18.3788C74.1571 16.9445 72.5734 15.8172 70.7726 15.8172C68.273 15.8172 66.354 17.7899 66.354 20.5121C66.354 23.2343 68.273 25.1788 70.7726 25.1788C72.5734 25.1788 74.1543 24.0797 74.5234 22.496H77.9389C77.5444 25.9706 74.4643 28.3491 70.8346 28.3491C69.8028 28.364 68.7785 28.1712 67.8227 27.7822C66.8668 27.3933 65.999 26.8161 65.2707 26.0849C64.5425 25.3538 63.9687 24.4837 63.5835 23.5263C63.1984 22.5689 63.0097 21.5439 63.0287 20.5121Z"/>
<path d="M81.0469 6.6084L84.3693 6.6084V14.5665C84.9298 13.9619 85.609 13.4794 86.3645 13.1492C87.1199 12.819 87.9354 12.6482 88.7598 12.6474C92.2683 12.6474 94.6777 15.209 94.6777 19.3854V27.9522L91.2651 27.9522V19.4699C91.2651 17.3677 90.0759 15.7811 87.9708 15.7811C85.9277 15.7811 84.4341 17.2747 84.4341 19.6531V27.9522H81.0525L81.0469 6.6084Z"/>
<path d="M102.315 27.9522H98.9336V6.6084L102.315 6.6084V27.9522Z"/>
<path d="M142.78 26.4277V33.6841H139.398V13.042H142.78V14.5666C144.085 13.3216 145.823 12.6325 147.627 12.6447C151.896 12.6447 155.129 15.877 155.129 20.4506C155.129 25.0243 151.896 28.3411 147.627 28.3411C145.824 28.3559 144.087 27.6698 142.78 26.4277ZM147.289 25.1145C149.825 25.1145 151.772 23.1024 151.772 20.5098C151.772 17.858 149.822 15.908 147.289 15.908C144.699 15.908 142.78 17.7679 142.78 20.5098C142.78 23.1644 144.713 25.106 147.303 25.106L147.289 25.1145Z"/>
<path d="M120.999 23.784L121.855 23.784C122.245 23.784 122.635 23.7822 123.024 23.7861C123.094 23.7861 123.106 23.7651 123.105 23.7018C123.103 22.9328 123.103 22.1638 123.105 21.3946C123.1 21.3656 123.104 21.3358 123.116 21.3088C123.197 21.4588 123.273 21.6088 123.353 21.7521C123.693 22.3678 124.075 22.9538 124.548 23.4772C125.307 24.3169 126.24 24.8354 127.36 25.0154C127.974 25.114 128.592 25.1527 129.211 25.0993C130.769 24.965 131.964 24.2359 132.758 22.88C133.121 22.2601 133.319 21.5836 133.406 20.8728C133.513 20.0031 133.466 19.1438 133.224 18.2993C133 17.5154 132.619 16.8184 132.04 16.2387C131.404 15.6017 130.637 15.213 129.752 15.0514C129.09 14.9314 128.43 14.9656 127.773 15.0864C127.303 15.1728 126.845 15.2946 126.412 15.5024C125.614 15.8775 124.934 16.4634 124.445 17.1969C124.153 17.6329 123.925 18.1035 123.696 18.5746C123.216 19.5595 122.461 19.8942 121.367 20.0367C121.288 20.0472 121.199 20.0517 121.062 20.0744C121.005 20.0744 121.034 20.2145 121.023 20.3725C121.028 20.4925 121.025 20.6125 121.025 20.7324C121.025 21.4339 121.025 22.1357 121.025 22.8372C121.025 22.9316 121.007 23.0253 120.971 23.1128C120.631 23.9717 120.129 24.729 119.515 25.4139C118.836 26.1736 118.03 26.8089 117.133 27.2913C116.294 27.7469 115.382 28.0523 114.438 28.1937C113.039 28.4019 111.664 28.2921 110.321 27.845C109.289 27.5045 108.343 26.9441 107.549 26.2024C106.529 25.2538 105.822 24.1061 105.392 22.7835C105.168 22.0898 105.031 21.3707 104.986 20.6431C104.909 19.477 105.042 18.3368 105.409 17.2263C105.772 16.1271 106.336 15.1434 107.12 14.2872C107.776 13.5706 108.562 12.9865 109.438 12.5666C110.177 12.212 110.966 11.9724 111.777 11.8562C112.343 11.7709 112.915 11.7399 113.487 11.7635C114.402 11.8049 115.277 12.0184 116.118 12.3756C117.063 12.7769 117.911 13.3326 118.703 13.9804C119.342 14.5031 119.933 15.0771 120.495 15.6794C120.657 15.8527 120.817 16.0272 120.978 16.2015C120.998 16.2228 121.015 16.2456 121.035 16.2696C121.009 16.281 120.979 16.2844 120.95 16.2795C120.308 16.2795 119.666 16.2795 119.024 16.2795C118.959 16.2795 118.941 16.2939 118.941 16.3613C118.943 17.133 118.942 17.9043 118.942 18.676V18.7579L118.932 18.7621C118.87 18.6451 118.808 18.5272 118.745 18.4109C118.415 17.8039 118.047 17.2245 117.596 16.6993C116.793 15.7654 115.789 15.2016 114.567 15.0316C113.941 14.9443 113.312 14.9062 112.685 14.9797C111.13 15.1623 109.965 15.9379 109.216 17.3189C108.881 17.9361 108.701 18.6025 108.628 19.2983C108.531 20.2055 108.599 21.0977 108.884 21.9674C109.13 22.7172 109.525 23.377 110.102 23.9213C110.702 24.4845 111.408 24.8378 112.212 25.001C112.95 25.1509 113.682 25.0951 114.414 24.9566C115.23 24.8068 115.995 24.4549 116.64 23.933C117.18 23.4987 117.584 22.9553 117.918 22.3549C118.103 22.025 118.246 21.8097 118.428 21.4789C118.928 20.5759 119.618 20.1998 120.647 20.0738C120.722 20.0645 120.91 20.0514 120.984 20.0514C121.035 20.0514 121.022 20.0663 121.023 19.6297C121.021 18.9354 121.023 18.3014 121.023 17.6071C121.026 17.5648 121.026 17.5223 121.023 17.48C121.041 17.0937 121.17 16.7722 121.303 16.4903C121.686 15.6806 122.124 15.0661 122.745 14.4255C123.866 13.2696 125.196 12.4641 126.757 12.0502C127.668 11.8088 128.594 11.7335 129.533 11.7962C130.48 11.8595 131.393 12.0601 132.268 12.4296C133.292 12.8566 134.21 13.5053 134.953 14.3289C136.022 15.5093 136.663 16.8964 136.944 18.4574C137.086 19.2572 137.119 20.0727 137.043 20.8815C136.94 22.0064 136.641 23.0771 136.113 24.0785C135.3 25.626 134.111 26.789 132.525 27.5364C131.698 27.9262 130.826 28.1563 129.919 28.2561C129.346 28.3234 128.767 28.331 128.192 28.2789C127.356 28.1943 126.541 27.9738 125.776 27.626C124.742 27.1624 123.827 26.5194 122.976 25.7777C122.293 25.1794 121.669 24.5268 121.061 23.8598C121.042 23.8388 121.026 23.8172 120.999 23.784Z"/>
</svg>
<h1>Workshops</h1>
<p>Internal workshop hub</p>
</header>
<div class="grid">
${cards}
</div>
</body>
</html>`);
});
app.listen(PORT, () => {
console.log(`Workshops server running at http://localhost:${PORT}`);
});