Monorepo for wisp.place. A static site hosting service built on top of the AT Protocol. wisp.place

admin dashboard

+114 -1
+91 -1
apps/main-app/build.ts
··· 112 112 113 113 await Bun.write(`${distDir}/editor/index.html`, htmlContent) 114 114 115 + // Create admin directory 116 + await mkdir(`${distDir}/admin`, { recursive: true }) 117 + 118 + // Build the admin React app 119 + const adminResult = await Bun.build({ 120 + entrypoints: [`${publicDir}/admin/admin.tsx`], 121 + outdir: `${distDir}/admin`, 122 + target: 'browser', 123 + format: 'esm', 124 + minify: true, 125 + sourcemap: 'none', 126 + splitting: true, 127 + naming: { 128 + entry: '[name].[hash].js', 129 + chunk: '[name].[hash].js', 130 + asset: '[name].[hash][ext]' 131 + } 132 + }) 133 + 134 + if (!adminResult.success) { 135 + console.error('❌ Admin build failed:') 136 + for (const log of adminResult.logs) { 137 + console.error(log) 138 + } 139 + process.exit(1) 140 + } 141 + 142 + // Find the main entry bundle for admin 143 + const adminBundle = adminResult.outputs.find(o => o.path.includes('admin.') && o.path.endsWith('.js')) 144 + 145 + if (!adminBundle) { 146 + console.error('❌ Could not find admin bundle in outputs') 147 + process.exit(1) 148 + } 149 + 150 + const adminBundleName = path.basename(adminBundle.path) 151 + 152 + // Generate the production HTML for admin 153 + const adminHtmlContent = `<!doctype html> 154 + <html lang="en"> 155 + <head> 156 + <meta charset="UTF-8" /> 157 + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 158 + <title>Admin - wisp.place</title> 159 + <meta name="description" content="Admin dashboard for wisp.place decentralized static site hosting." /> 160 + <meta name="robots" content="noindex, nofollow" /> 161 + 162 + <!-- Theme --> 163 + <meta name="theme-color" content="#7c3aed" /> 164 + 165 + <link rel="icon" type="image/x-icon" href="/favicon.ico"> 166 + <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"> 167 + <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"> 168 + <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"> 169 + <link rel="manifest" href="/site.webmanifest"> 170 + <link rel="stylesheet" href="/dist/styles.css"> 171 + <style> 172 + /* Dark theme fallback styles for before JS loads */ 173 + @media (prefers-color-scheme: dark) { 174 + body { 175 + background-color: oklch(0.23 0.015 285); 176 + color: oklch(0.90 0.005 285); 177 + } 178 + 179 + pre { 180 + background-color: oklch(0.33 0.015 285) !important; 181 + color: oklch(0.90 0.005 285) !important; 182 + } 183 + 184 + .bg-muted { 185 + background-color: oklch(0.33 0.015 285) !important; 186 + } 187 + } 188 + </style> 189 + </head> 190 + <body> 191 + <div id="root"></div> 192 + <script type="module" src="/admin/${adminBundleName}"></script> 193 + </body> 194 + </html> 195 + ` 196 + 197 + await Bun.write(`${distDir}/admin/index.html`, adminHtmlContent) 198 + 115 199 console.log('✅ Build successful!') 116 - console.log(`📦 Generated ${editorResult.outputs.length + 1} file(s):`) 200 + console.log(`📦 Generated ${editorResult.outputs.length + adminResult.outputs.length + 2} file(s):`) 201 + console.log(`\n Editor:`) 117 202 console.log(` - ${distDir}/editor/index.html`) 118 203 for (const output of editorResult.outputs) { 119 204 console.log(` - ${output.path}`) 120 205 } 206 + console.log(`\n Admin:`) 207 + console.log(` - ${distDir}/admin/index.html`) 208 + for (const output of adminResult.outputs) { 209 + console.log(` - ${output.path}`) 210 + }
+23
apps/main-app/src/index.ts
··· 226 226 }) 227 227 : (app) => app 228 228 ) 229 + // Production only: serve built admin assets 230 + .use( 231 + Bun.env.NODE_ENV === 'production' 232 + ? await staticPlugin({ 233 + assets: './apps/main-app/dist/admin', 234 + prefix: '/admin' 235 + }) 236 + : (app) => app 237 + ) 238 + // Production only: serve built HTML for /admin 239 + .use( 240 + Bun.env.NODE_ENV === 'production' 241 + ? new Elysia() 242 + .get('/admin', async ({ set }) => { 243 + set.headers['Content-Type'] = 'text/html; charset=utf-8' 244 + return await Bun.file('./apps/main-app/dist/admin/index.html').text() 245 + }) 246 + .get('/admin/*', async ({ set }) => { 247 + set.headers['Content-Type'] = 'text/html; charset=utf-8' 248 + return await Bun.file('./apps/main-app/dist/admin/index.html').text() 249 + }) 250 + : (app) => app 251 + ) 229 252 .get('/wisp.css', ({ set }) => { 230 253 set.headers['Content-Type'] = 'text/css; charset=utf-8' 231 254 set.headers['Cache-Control'] = 'public, max-age=86400'