setup server

Seto committed Dec 2, 2025 at 21:41 UTC fa2c66f14d17510d5336a8d8d4a26cce87127fdf
5 files changed +168 -5
astro.config.ts
+17 -5
@@ -12,23 +12,29 @@ import rehypeCopyCode from './src/plugins/rehype-copy-code.mjs'
12 import remarkTOC from './src/plugins/remark-toc.mjs'
13 import { SITE } from './src/config'
14 import path from 'path'
15 -
16 -import tailwindcss from '@tailwindcss/vite';
15 +import tailwindcss from '@tailwindcss/vite'
16 +import node from '@astrojs/node'
17
18 export default defineConfig({
19 site: SITE.website,
20 + base: '/',
21 +
22 devToolbar: {
23 enabled: false
24 },
23 - output: 'static',
25 +
26 + output: 'server',
27 +
28 build: {
29 inlineStylesheets: 'auto'
30 },
31 +
32 image: {
33 service: {
34 entrypoint: 'astro/assets/services/sharp'
35 }
36 },
37 +
38 markdown: {
39 shikiConfig: {
40 theme: 'css-variables',
@@ -37,7 +43,9 @@ export default defineConfig({
43 remarkPlugins: [remarkMath, remarkDirective, remarkEmbeddedMedia, remarkReadingTime, remarkTOC],
44 rehypePlugins: [rehypeKatex, rehypeCleanup, rehypeImageProcessor, rehypeCopyCode]
45 },
46 +
47 integrations: [mdx(), sitemap()],
48 +
49 vite: {
50 resolve: {
51 alias: {
@@ -61,5 +69,9 @@ export default defineConfig({
69 },
70
71 plugins: [tailwindcss()]
64 - }
65 -})
\ No newline at end of file
72 + },
73 +
74 + adapter: node({
75 + mode: 'middleware'
76 + })
77 +})
package.json
+1
@@ -19,6 +19,7 @@
19 },
20 "dependencies": {
21 "@astrojs/mdx": "^4.3.12",
22 + "@astrojs/node": "^9.5.1",
23 "@astrojs/sitemap": "^3.6.0",
24 "@tailwindcss/vite": "^4.1.17",
25 "astro": "^5.16.3",
pnpm-lock.yaml
+138
@@ -11,6 +11,9 @@ importers:
11 '@astrojs/mdx':
12 specifier: ^4.3.12
13 version: 4.3.12(astro@5.16.3(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(tsx@4.21.0)(typescript@5.9.3))
14 + '@astrojs/node':
15 + specifier: ^9.5.1
16 + version: 9.5.1(astro@5.16.3(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(tsx@4.21.0)(typescript@5.9.3))
17 '@astrojs/sitemap':
18 specifier: ^3.6.0
19 version: 3.6.0
@@ -102,6 +105,11 @@ packages:
105 peerDependencies:
106 astro: ^5.0.0
107
108 + '@astrojs/node@9.5.1':
109 + resolution: {integrity: sha512-7k+SU877OUQylPr0mFcWrGvNuC78Lp9w+GInY8Rwc+LkHyDP9xls+nZAioK0WDWd+fyeQnlHbpDGURO3ZHuDVg==}
110 + peerDependencies:
111 + astro: ^5.14.3
112 +
113 '@astrojs/prism@3.3.0':
114 resolution: {integrity: sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==}
115 engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0}
@@ -1259,6 +1267,10 @@ packages:
1267 defu@6.1.4:
1268 resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
1269
1270 + depd@2.0.0:
1271 + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
1272 + engines: {node: '>= 0.8'}
1273 +
1274 dequal@2.0.3:
1275 resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
1276 engines: {node: '>=6'}
@@ -1307,12 +1319,19 @@ packages:
1319 resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==}
1320 engines: {node: '>=4'}
1321
1322 + ee-first@1.1.1:
1323 + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
1324 +
1325 emoji-regex@10.6.0:
1326 resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==}
1327
1328 emoji-regex@8.0.0:
1329 resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
1330
1331 + encodeurl@2.0.0:
1332 + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
1333 + engines: {node: '>= 0.8'}
1334 +
1335 enhanced-resolve@5.18.3:
1336 resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==}
1337 engines: {node: '>=10.13.0'}
@@ -1344,6 +1363,9 @@ packages:
1363 engines: {node: '>=18'}
1364 hasBin: true
1365
1366 + escape-html@1.0.3:
1367 + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
1368 +
1369 escape-string-regexp@4.0.0:
1370 resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
1371 engines: {node: '>=10'}
@@ -1436,6 +1458,10 @@ packages:
1458 resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
1459 engines: {node: '>=0.10.0'}
1460
1461 + etag@1.8.1:
1462 + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
1463 + engines: {node: '>= 0.6'}
1464 +
1465 eventemitter3@5.0.1:
1466 resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
1467
@@ -1496,6 +1522,10 @@ packages:
1522 fontkit@2.0.4:
1523 resolution: {integrity: sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==}
1524
1525 + fresh@2.0.0:
1526 + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
1527 + engines: {node: '>= 0.8'}
1528 +
1529 fsevents@2.3.3:
1530 resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
1531 engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -1591,6 +1621,10 @@ packages:
1621 http-cache-semantics@4.2.0:
1622 resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==}
1623
1624 + http-errors@2.0.1:
1625 + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
1626 + engines: {node: '>= 0.8'}
1627 +
1628 ignore@5.3.2:
1629 resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
1630 engines: {node: '>= 4'}
@@ -1610,6 +1644,9 @@ packages:
1644 resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
1645 engines: {node: '>=0.8.19'}
1646
1647 + inherits@2.0.4:
1648 + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
1649 +
1650 inline-style-parser@0.2.7:
1651 resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==}
1652
@@ -1975,6 +2012,14 @@ packages:
2012 resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
2013 engines: {node: '>=8.6'}
2014
2015 + mime-db@1.54.0:
2016 + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==}
2017 + engines: {node: '>= 0.6'}
2018 +
2019 + mime-types@3.0.2:
2020 + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==}
2021 + engines: {node: '>=18'}
2022 +
2023 minimatch@3.1.2:
2024 resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
2025
@@ -2023,6 +2068,10 @@ packages:
2068 ohash@2.0.11:
2069 resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==}
2070
2071 + on-finished@2.4.1:
2072 + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
2073 + engines: {node: '>= 0.8'}
2074 +
2075 oniguruma-parser@0.12.1:
2076 resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==}
2077
@@ -2139,6 +2188,10 @@ packages:
2188 radix3@1.1.2:
2189 resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==}
2190
2191 + range-parser@1.2.1:
2192 + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
2193 + engines: {node: '>= 0.6'}
2194 +
2195 readdirp@4.1.2:
2196 resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
2197 engines: {node: '>= 14.18.0'}
@@ -2260,6 +2313,16 @@ packages:
2313 engines: {node: '>=10'}
2314 hasBin: true
2315
2316 + send@1.2.0:
2317 + resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==}
2318 + engines: {node: '>= 18'}
2319 +
2320 + server-destroy@1.0.1:
2321 + resolution: {integrity: sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==}
2322 +
2323 + setprototypeof@1.2.0:
2324 + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
2325 +
2326 sharp@0.34.5:
2327 resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==}
2328 engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
@@ -2298,6 +2361,10 @@ packages:
2361 space-separated-tokens@2.0.2:
2362 resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
2363
2364 + statuses@2.0.2:
2365 + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
2366 + engines: {node: '>= 0.8'}
2367 +
2368 stream-replace-string@2.0.0:
2369 resolution: {integrity: sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==}
2370
@@ -2368,6 +2435,10 @@ packages:
2435 resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
2436 engines: {node: '>=8.0'}
2437
2438 + toidentifier@1.0.1:
2439 + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
2440 + engines: {node: '>=0.6'}
2441 +
2442 trim-lines@3.0.1:
2443 resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
2444
@@ -2712,6 +2783,15 @@ snapshots:
2783 transitivePeerDependencies:
2784 - supports-color
2785
2786 + '@astrojs/node@9.5.1(astro@5.16.3(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(tsx@4.21.0)(typescript@5.9.3))':
2787 + dependencies:
2788 + '@astrojs/internal-helpers': 0.7.5
2789 + astro: 5.16.3(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(tsx@4.21.0)(typescript@5.9.3)
2790 + send: 1.2.0
2791 + server-destroy: 1.0.1
2792 + transitivePeerDependencies:
2793 + - supports-color
2794 +
2795 '@astrojs/prism@3.3.0':
2796 dependencies:
2797 prismjs: 1.30.0
@@ -3760,6 +3840,8 @@ snapshots:
3840
3841 defu@6.1.4: {}
3842
3843 + depd@2.0.0: {}
3844 +
3845 dequal@2.0.3: {}
3846
3847 destr@2.0.5: {}
@@ -3802,10 +3884,14 @@ snapshots:
3884
3885 dset@3.1.4: {}
3886
3887 + ee-first@1.1.1: {}
3888 +
3889 emoji-regex@10.6.0: {}
3890
3891 emoji-regex@8.0.0: {}
3892
3893 + encodeurl@2.0.0: {}
3894 +
3895 enhanced-resolve@5.18.3:
3896 dependencies:
3897 graceful-fs: 4.2.11
@@ -3889,6 +3975,8 @@ snapshots:
3975 '@esbuild/win32-ia32': 0.27.0
3976 '@esbuild/win32-x64': 0.27.0
3977
3978 + escape-html@1.0.3: {}
3979 +
3980 escape-string-regexp@4.0.0: {}
3981
3982 escape-string-regexp@5.0.0: {}
@@ -4019,6 +4107,8 @@ snapshots:
4107
4108 esutils@2.0.3: {}
4109
4110 + etag@1.8.1: {}
4111 +
4112 eventemitter3@5.0.1: {}
4113
4114 extend@3.0.2: {}
@@ -4084,6 +4174,8 @@ snapshots:
4174 unicode-properties: 1.4.1
4175 unicode-trie: 2.0.0
4176
4177 + fresh@2.0.0: {}
4178 +
4179 fsevents@2.3.3:
4180 optional: true
4181
@@ -4272,6 +4364,14 @@ snapshots:
4364
4365 http-cache-semantics@4.2.0: {}
4366
4367 + http-errors@2.0.1:
4368 + dependencies:
4369 + depd: 2.0.0
4370 + inherits: 2.0.4
4371 + setprototypeof: 1.2.0
4372 + statuses: 2.0.2
4373 + toidentifier: 1.0.1
4374 +
4375 ignore@5.3.2: {}
4376
4377 ignore@7.0.5: {}
@@ -4285,6 +4385,8 @@ snapshots:
4385
4386 imurmurhash@0.1.4: {}
4387
4388 + inherits@2.0.4: {}
4389 +
4390 inline-style-parser@0.2.7: {}
4391
4392 iron-webcrypto@1.2.1: {}
@@ -4914,6 +5016,12 @@ snapshots:
5016 braces: 3.0.3
5017 picomatch: 2.3.1
5018
5019 + mime-db@1.54.0: {}
5020 +
5021 + mime-types@3.0.2:
5022 + dependencies:
5023 + mime-db: 1.54.0
5024 +
5025 minimatch@3.1.2:
5026 dependencies:
5027 brace-expansion: 1.1.12
@@ -4954,6 +5062,10 @@ snapshots:
5062
5063 ohash@2.0.11: {}
5064
5065 + on-finished@2.4.1:
5066 + dependencies:
5067 + ee-first: 1.1.1
5068 +
5069 oniguruma-parser@0.12.1: {}
5070
5071 oniguruma-to-es@4.3.4:
@@ -5071,6 +5183,8 @@ snapshots:
5183
5184 radix3@1.1.2: {}
5185
5186 + range-parser@1.2.1: {}
5187 +
5188 readdirp@4.1.2: {}
5189
5190 reading-time@1.5.0: {}
@@ -5298,6 +5412,26 @@ snapshots:
5412
5413 semver@7.7.3: {}
5414
5415 + send@1.2.0:
5416 + dependencies:
5417 + debug: 4.4.3
5418 + encodeurl: 2.0.0
5419 + escape-html: 1.0.3
5420 + etag: 1.8.1
5421 + fresh: 2.0.0
5422 + http-errors: 2.0.1
5423 + mime-types: 3.0.2
5424 + ms: 2.1.3
5425 + on-finished: 2.4.1
5426 + range-parser: 1.2.1
5427 + statuses: 2.0.2
5428 + transitivePeerDependencies:
5429 + - supports-color
5430 +
5431 + server-destroy@1.0.1: {}
5432 +
5433 + setprototypeof@1.2.0: {}
5434 +
5435 sharp@0.34.5:
5436 dependencies:
5437 '@img/colour': 1.0.0
@@ -5363,6 +5497,8 @@ snapshots:
5497
5498 space-separated-tokens@2.0.2: {}
5499
5500 + statuses@2.0.2: {}
5501 +
5502 stream-replace-string@2.0.0: {}
5503
5504 string-width@4.2.3:
@@ -5439,6 +5575,8 @@ snapshots:
5575 dependencies:
5576 is-number: 7.0.0
5577
5578 + toidentifier@1.0.1: {}
5579 +
5580 trim-lines@3.0.1: {}
5581
5582 trough@2.2.0: {}
run-server.js new
+11
@@ -0,0 +1,11 @@
1 +import express from 'express'
2 +import { handler as ssrHandler } from './dist/server/entry.mjs'
3 +
4 +const app = express()
5 +// Change this based on your astro.config.mjs, `base` option.
6 +// They should match. The default value is "/".
7 +const base = '/'
8 +app.use(base, express.static('dist/client/'))
9 +app.use(ssrHandler)
10 +
11 +app.listen(3021)
src/pages/[...slug].astro
+1
@@ -1,4 +1,5 @@
1 ---
2 +export const prerender = true
3 import { type CollectionEntry, getCollection } from 'astro:content'
4 import PostLayout from '@/layouts/PostLayout.astro'
5 import { render } from 'astro:content'