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

supporters get 700MB

+75 -6
+1
.gitignore
··· 1 1 .research/ 2 + binaries/ 2 3 cache/ 3 4 # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 4 5 .env
+5 -4
apps/firehose-service/src/lib/cache-writer.ts
··· 10 10 import { extractBlobCid, getPdsForDid } from '@wispplace/atproto-utils'; 11 11 import { collectFileCidsFromEntries, countFilesInDirectory, normalizeFileCids } from '@wispplace/fs-utils'; 12 12 import { shouldCompressMimeType } from '@wispplace/atproto-utils/compression'; 13 - import { MAX_BLOB_SIZE, MAX_FILE_COUNT, MAX_SITE_SIZE } from '@wispplace/constants'; 13 + import { MAX_BLOB_SIZE, MAX_FILE_COUNT, MAX_SITE_SIZE, MAX_SITE_SIZE_SUPPORTER } from '@wispplace/constants'; 14 14 import { createLogger } from '@wispplace/observability'; 15 15 import { writeFile, deleteFile, listFiles } from './storage'; 16 - import { getSiteCache, upsertSiteCache, deleteSiteCache, upsertSiteSettingsCache, deleteSiteSettingsCache, upsertSite, deleteSite } from './db'; 16 + import { getSiteCache, upsertSiteCache, deleteSiteCache, upsertSiteSettingsCache, deleteSiteSettingsCache, upsertSite, deleteSite, isSupporter } from './db'; 17 17 import { rewriteHtmlPaths, isHtmlFile } from './html-rewriter'; 18 18 import { gunzipSync } from 'zlib'; 19 19 import { publishCacheInvalidation } from './cache-invalidation'; ··· 477 477 } 478 478 479 479 const totalSize = calculateTotalBlobSize(expandedRoot); 480 - if (totalSize > MAX_SITE_SIZE) { 481 - logger.error(`Site exceeds size limit: ${totalSize} > ${MAX_SITE_SIZE}`); 480 + const sizeLimit = await isSupporter(did) ? MAX_SITE_SIZE_SUPPORTER : MAX_SITE_SIZE; 481 + if (totalSize > sizeLimit) { 482 + logger.error(`Site exceeds size limit: ${totalSize} > ${sizeLimit}`); 482 483 return; 483 484 } 484 485
+5
apps/firehose-service/src/lib/db.ts
··· 162 162 await sql`DELETE FROM sites WHERE did = ${did} AND rkey = ${rkey}`; 163 163 } 164 164 165 + export async function isSupporter(did: string): Promise<boolean> { 166 + const rows = await sql`SELECT 1 FROM supporter WHERE did = ${did} LIMIT 1`; 167 + return rows.length > 0; 168 + } 169 + 165 170 export async function closeDatabase(): Promise<void> { 166 171 await sql.end({ timeout: 5 }); 167 172 logger.info('[DB] Database connections closed');
+5
apps/hosting-service/src/lib/db.ts
··· 206 206 `; 207 207 } 208 208 209 + export async function isSupporter(did: string): Promise<boolean> { 210 + const rows = await sql`SELECT 1 FROM supporter WHERE did = ${did} LIMIT 1`; 211 + return rows.length > 0; 212 + } 213 + 209 214 export { sql };
+28 -2
apps/hosting-service/src/lib/on-demand-cache.ts
··· 14 14 import { extractBlobCid, getPdsForDid } from '@wispplace/atproto-utils'; 15 15 import { shouldCompressMimeType } from '@wispplace/atproto-utils/compression'; 16 16 import { collectFileCidsFromEntries, countFilesInDirectory } from '@wispplace/fs-utils'; 17 - import { MAX_BLOB_SIZE, MAX_FILE_COUNT, MAX_SITE_SIZE } from '@wispplace/constants'; 17 + import { MAX_BLOB_SIZE, MAX_FILE_COUNT, MAX_SITE_SIZE, MAX_SITE_SIZE_SUPPORTER } from '@wispplace/constants'; 18 18 import { expandSubfsNodes } from './utils'; 19 19 import { storage } from './storage'; 20 - import { upsertSiteCache, tryAcquireLock, releaseLock } from './db'; 20 + import { upsertSiteCache, tryAcquireLock, releaseLock, isSupporter } from './db'; 21 21 import { enqueueRevalidate } from './revalidate-queue'; 22 22 import { gunzipSync } from 'zlib'; 23 23 import { createLogger } from '@wispplace/observability'; ··· 115 115 return false; 116 116 } 117 117 118 + const totalSize = calculateTotalBlobSize(expandedRoot); 119 + const sizeLimit = await isSupporter(did) ? MAX_SITE_SIZE_SUPPORTER : MAX_SITE_SIZE; 120 + if (totalSize > sizeLimit) { 121 + logger.error('Site exceeds size limit', { did, rkey, totalSize, sizeLimit }); 122 + return false; 123 + } 124 + 118 125 // Collect files 119 126 const files = collectFileInfo(expandedRoot.entries); 120 127 ··· 159 166 } finally { 160 167 await releaseLock(lockKey); 161 168 } 169 + } 170 + 171 + function calculateTotalBlobSize(directory: Directory): number { 172 + let totalSize = 0; 173 + 174 + function sumBlobSizes(entries: Entry[]) { 175 + for (const entry of entries) { 176 + const node = entry.node; 177 + if ('type' in node && node.type === 'directory' && 'entries' in node) { 178 + sumBlobSizes(node.entries); 179 + } else if ('type' in node && node.type === 'file' && 'blob' in node) { 180 + const fileNode = node as File; 181 + totalSize += (fileNode.blob as any)?.size || 0; 182 + } 183 + } 184 + } 185 + 186 + sumBlobSizes(directory.entries); 187 + return totalSize; 162 188 } 163 189 164 190 function collectFileInfo(entries: Entry[], pathPrefix: string = ''): FileInfo[] {
+30
build-cli.sh
··· 1 + #!/usr/bin/env bash 2 + set -e 3 + 4 + OUTDIR="$(dirname "$0")/binaries" 5 + ENTRY="./cli/index.ts" 6 + 7 + mkdir -p "$OUTDIR" 8 + 9 + echo "Building wispctl binaries..." 10 + 11 + bun build --compile --minify --target=bun-darwin-arm64 "$ENTRY" --outfile "$OUTDIR/wisp-cli-aarch64-darwin" 12 + echo " ✓ aarch64-darwin" 13 + 14 + bun build --compile --minify --target=bun-darwin-x64 "$ENTRY" --outfile "$OUTDIR/wisp-cli-x86_64-darwin" 15 + echo " ✓ x86_64-darwin" 16 + 17 + bun build --compile --minify --target=bun-linux-arm64 "$ENTRY" --outfile "$OUTDIR/wisp-cli-aarch64-linux" 18 + echo " ✓ aarch64-linux" 19 + 20 + bun build --compile --minify --target=bun-linux-x64 "$ENTRY" --outfile "$OUTDIR/wisp-cli-x86_64-linux" 21 + echo " ✓ x86_64-linux" 22 + 23 + lipo -create -output "$OUTDIR/wisp-cli-darwin-universal" \ 24 + "$OUTDIR/wisp-cli-aarch64-darwin" \ 25 + "$OUTDIR/wisp-cli-x86_64-darwin" 26 + echo " ✓ darwin-universal (lipo)" 27 + 28 + echo "" 29 + echo "Done. Binaries written to $OUTDIR:" 30 + ls -lh "$OUTDIR"
+1
packages/@wispplace/constants/src/index.ts
··· 14 14 15 15 // File size limits 16 16 export const MAX_SITE_SIZE = 300 * 1024 * 1024; // 300MB 17 + export const MAX_SITE_SIZE_SUPPORTER = 700 * 1024 * 1024; // 700MB for supporters 17 18 export const MAX_FILE_SIZE = 200 * 1024 * 1024; // 200MB 18 19 export const MAX_FILE_COUNT = 1000; 19 20