nx_archive_site_viewer.nx source
↩ module page · 224 lines · 18221 B
1// nx_archive_site_viewer.nx -- DOMAIN-GENERAL time-machine viewer with LOCAL MEDIA (no broken images). Reads the
2// target host from web_assets/archive/_target.txt (default page3.com), fetches its Wayback CDX, picks the EARLIEST
3// status-200 capture, reconstructs it from the capture's OWN original URL, then FETCHES every referenced image from
4// the archive, stores them under web_assets/archive/<host>/media/, and rewrites the page's <img src> to those local
5// copies (transparent-pixel placeholder for any image the archive doesn't have -> never a broken icon). Output:
6// web_assets/archive/<host>/{index.html,page.html,media/*}. Served by nx_archive_server. Fetch stack, cache-first,
7// status-aware retry. license_tier: ORIGINAL
8import "nx_syscalls.nx"
9import "nx_x509_trust_store.nx"
10import "nx_trust_store_load_from_certdata.nx"
11import "nx_https_fetch_follow.nx"
12import "nx_cdx_parse.nx"
13const K_MAGIC_4194304: i64 = 4194304
14const K_MAGIC_1024: i64 = 1024
15const K_MAGIC_4096: i64 = 4096
16const K_MAGIC_2048: i64 = 2048
17const K_MAGIC_65536: i64 = 65536
18
19func gw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
20func gn(v: i64) -> i64 { if v==0 { sys_write(1,"0" as *u8,1); return 0 } var m: i64=v; if m<0 { sys_write(1,"-" as *u8,1); m=0-m } let t: *u8=sys_mmap(24); var k: i64=0; while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } let o: *u8=sys_mmap(24); var w: i64=0; var q: i64=k-1; while q>=0 { o[w]=t[q]; w=w+1; q=q-1 } sys_write(1,o,w); return 0 }
21func ap(buf: *u8, off: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8) { buf[off+i]=s[i]; i=i+1 } return off+i }
22func apn(buf: *u8, off: i64, v: i64) -> i64 { if v==0 { buf[off]=0x30 as u8; return off+1 } var m: i64=v; if m<0 { buf[off]=0x2d as u8; off=off+1; m=0-m } let t: *u8=sys_mmap(24); var k: i64=0; while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } var o: i64=off; var q: i64=k-1; while q>=0 { buf[o]=t[q]; o=o+1; q=q-1 } return o }
23func apslice(dst: *u8, off: i64, src: *u8, so: i64, sl: i64) -> i64 { var i: i64=0; while i<sl { dst[off+i]=src[so+i]; i=i+1 } return off+sl }
24func dstarts(buf: *u8, off: i64, n: i64, lit: *u8, litlen: i64) -> i64 { if off+litlen>n { return 0 } var i: i64=0; while i<litlen { var a: i64=buf[off+i] as i64; if a>=0x41 { if a<=0x5a { a=a+0x20 } } if a != (lit[i] as i64) { return 0 } i=i+1 } return 1 }
25func esc_html(dst: *u8, doff: i64, src: *u8, n: i64) -> i64 { var o: i64=doff; var i: i64=0; while i<n { let c: i64=src[i] as i64; if c==0x3c { o=ap(dst,o,"<" as *u8) } else { if c==0x3e { o=ap(dst,o,">" as *u8) } else { if c==0x26 { o=ap(dst,o,"&" as *u8) } else { dst[o]=c as u8; o=o+1 } } } i=i+1 } return o }
26func save_file(path: *u8, buf: *u8, n: i64) -> i64 { let fd: i64=sys_openat_wr(path, 0x1a4); if fd<0 { return 0 } var off: i64=0; while off<n { let w: i64=sys_write(fd, ((buf as i64)+off) as *u8, n-off); if w<=0 { sys_close(fd); return 0 } off=off+w } sys_close(fd); return 1 }
27func read_file(path: *u8, buf: *u8, cap: i64, lenbox: *i64) -> i64 { let fd: i64=sys_openat_rd(path); if fd<0 { return 0 } let n: i64=sys_read(fd, buf, cap); sys_close(fd); if n<=0 { return 0 } lenbox[0]=n; return n }
28func is_image(buf: *u8, n: i64) -> i64 { if n<4 { return 0 } let a: i64=buf[0] as i64; let b: i64=buf[1] as i64; let c: i64=buf[2] as i64; let d: i64=buf[3] as i64; if a==0x47 { if b==0x49 { if c==0x46 { return 1 } } } if a==0xff { if b==0xd8 { if c==0xff { return 1 } } } if a==0x89 { if b==0x50 { if c==0x4e { if d==0x47 { return 1 } } } } if a==0x42 { if b==0x4d { return 1 } } return 0 }
29func fetch_retry(url: *u8, store: *TrustStore, out: *u8, cap: i64, st: *i64, tries: i64) -> i64 { var t: i64=0; var n: i64=0-1; while t<tries { n=nx_https_fetch_follow(url, store, out, cap, 6, st); if n>0 { if st[0]==200 { t=tries } else { t=t+1 } } else { t=t+1 } } return n }
30func mkpath(dst: *u8, a: *u8, b: *u8) -> i64 { var o: i64=ap(dst,0,a); o=ap(dst,o,b); dst[o]=0 as u8; return o }
31func next_imgsrc(buf: *u8, n: i64, from: i64, out2: *i64) -> i64 { var i: i64=from; while i<n { if dstarts(buf, i, n, "src=" as *u8, 4)==1 { if i+4<n { if (buf[i+4] as i64)==0x22 { let us: i64=i+5; var ue: i64=us; var go: i64=1; while go==1 { if ue<n { if (buf[ue] as i64)==0x22 { go=0 } else { ue=ue+1 } } else { go=0 } } out2[0]=us; out2[1]=ue-us; return ue+1 } } } i=i+1 } return 0-1 }
32// lowercase extension (<=4 alnum) of url[off,off+len) into extbuf; returns length (0 if none).
33func ext_of(buf: *u8, off: i64, len: i64, extbuf: *u8) -> i64 {
34 var dot: i64=0-1; var i: i64=off
35 while i<off+len { if (buf[i] as i64)==0x2e { dot=i } i=i+1 }
36 if dot<0 { return 0 }
37 var o: i64=0; var j: i64=dot+1
38 while j<off+len { if o<4 { var c: i64=buf[j] as i64; var ok: i64=0; if c>=0x30 { if c<=0x39 { ok=1 } } if c>=0x41 { if c<=0x5a { c=c+0x20; ok=1 } } if c>=0x61 { if c<=0x7a { ok=1 } } if ok==1 { extbuf[o]=c as u8; o=o+1 } else { j=off+len } } j=j+1 }
39 return o
40}
41
42func main() -> i64 {
43 gw("=== nx_archive_site_viewer (local-media): time machine for any domain ===\n" as *u8)
44 sys_mkdir("web_assets" as *u8, 0x1ed)
45 sys_mkdir("web_assets/archive" as *u8, 0x1ed)
46 let cap: i64=K_MAGIC_4194304
47 let lb: *i64=sys_mmap(8) as *i64
48 let st: *i64=sys_mmap(8) as *i64
49
50 let dbuf: *u8=sys_mmap(256)
51 var domain_len: i64=0
52 if read_file("web_assets/archive/_target.txt" as *u8, dbuf, 250, lb)>0 { var di: i64=0; var go: i64=1; while go==1 { if di<lb[0] { let c: i64=dbuf[di] as i64; if c<=0x20 { go=0 } else { di=di+1 } } else { go=0 } } domain_len=di }
53 if domain_len==0 { domain_len=ap(dbuf, 0, "page3.com" as *u8) }
54 dbuf[domain_len]=0 as u8
55 gw("host = " as *u8); gw(dbuf); gw("\n" as *u8)
56
57 let ddir: *u8=sys_mmap(512)
58 var dl: i64=ap(ddir, 0, "web_assets/archive/" as *u8); dl=ap(ddir, dl, dbuf); ddir[dl]=0 as u8
59 sys_mkdir(ddir, 0x1ed)
60 let mdir: *u8=sys_mmap(512); mkpath(mdir, ddir, "/media" as *u8); sys_mkdir(mdir, 0x1ed)
61
62 let r: i64=nx_trust_store_load_from_certdata("data/mozilla_certdata.txt" as *u8, 512, K_MAGIC_4194304)
63 if r<=0 { gw("trust store load failed\n" as *u8); return 1 }
64 let store: *TrustStore=r as *TrustStore
65 let cdxbuf: *u8=sys_mmap(cap)
66 let pth: *u8=sys_mmap(K_MAGIC_1024)
67
68 mkpath(pth, ddir, "/_cache_cdx.txt" as *u8)
69 var ncdx: i64=read_file(pth, cdxbuf, cap, lb)
70 if ncdx>0 { gw("[1] CDX cache (" as *u8); gn(ncdx); gw(" B)\n" as *u8) } else {
71 let cu: *u8=sys_mmap(K_MAGIC_1024); var co: i64=ap(cu, 0, "https://web.archive.org/cdx/search/cdx?url=" as *u8); co=ap(cu, co, dbuf); co=ap(cu, co, "&limit=150" as *u8); cu[co]=0 as u8
72 gw("[1] fetch CDX " as *u8); gw(cu); gw("\n" as *u8)
73 ncdx=fetch_retry(cu, store, cdxbuf, cap, st, 6)
74 if ncdx<=0 { gw(" CDX fail\n" as *u8); return 1 }
75 if st[0]!=200 { gw(" rate-limited; retry shortly\n" as *u8); return 1 }
76 save_file(pth, cdxbuf, ncdx)
77 }
78
79 let RS: *i64=sys_mmap(K_MAGIC_4096) as *i64
80 let RE: *i64=sys_mmap(K_MAGIC_4096) as *i64
81 let nrows: i64=cdx_rows(cdxbuf, ncdx, RS, RE, 480)
82 let dates: *i64=sys_mmap(K_MAGIC_4096) as *i64
83 let tso: *i64=sys_mmap(K_MAGIC_4096) as *i64
84 let tsl: *i64=sys_mmap(K_MAGIC_4096) as *i64
85 let oro: *i64=sys_mmap(K_MAGIC_4096) as *i64
86 let orl: *i64=sys_mmap(K_MAGIC_4096) as *i64
87 let b2: *i64=sys_mmap(16) as *i64
88 var i: i64=0
89 var m: i64=0
90 while i<nrows {
91 cdx_field(cdxbuf, RS[i], RE[i], 4, b2)
92 var ok2: i64=0
93 if b2[1]==3 { if (cdxbuf[b2[0]] as i64)==0x32 { if (cdxbuf[b2[0]+1] as i64)==0x30 { if (cdxbuf[b2[0]+2] as i64)==0x30 { ok2=1 } } } }
94 if ok2==1 { cdx_field(cdxbuf, RS[i], RE[i], 1, b2); dates[m]=cdx_ts8(cdxbuf, b2[0], b2[1]); tso[m]=b2[0]; tsl[m]=b2[1]; cdx_field(cdxbuf, RS[i], RE[i], 2, b2); oro[m]=b2[0]; orl[m]=b2[1]; m=m+1 }
95 i=i+1
96 }
97 if m<=0 { gw(" no captures\n" as *u8); return 1 }
98 var q: i64=0; var qd: i64=dates[0]; var jj: i64=1
99 while jj<m { if dates[jj]<qd { qd=dates[jj]; q=jj } jj=jj+1 }
100 gw("[2] earliest capture " as *u8); gn(dates[q]); gw("\n" as *u8)
101
102 let cont: *u8=sys_mmap(cap)
103 mkpath(pth, ddir, "/_cache_page.txt" as *u8)
104 var ncont: i64=read_file(pth, cont, cap, lb)
105 if ncont>0 { gw("[3] page cache (" as *u8); gn(ncont); gw(" B)\n" as *u8) } else {
106 let url: *u8=sys_mmap(K_MAGIC_2048); var uo: i64=ap(url, 0, "https://web.archive.org/web/" as *u8); uo=apslice(url, uo, cdxbuf, tso[q], tsl[q]); uo=ap(url, uo, "id_/" as *u8); uo=apslice(url, uo, cdxbuf, oro[q], orl[q]); url[uo]=0 as u8
107 gw("[3] fetch page\n" as *u8)
108 ncont=fetch_retry(url, store, cont, cap, st, 6)
109 if ncont<=0 { gw(" page fail\n" as *u8); return 1 }
110 if st[0]!=200 { gw(" rate-limited; retry shortly\n" as *u8); return 1 }
111 save_file(pth, cont, ncont)
112 }
113
114 // -- LOCAL MEDIA: fetch each <img src>, store under <host>/media/, record local name (or 0 if absent) --
115 let names: *i64=sys_mmap(64*8) as *i64
116 let namel: *i64=sys_mmap(64*8) as *i64
117 let uoffs_a: *i64=sys_mmap(64*8) as *i64
118 let ulens_a: *i64=sys_mmap(64*8) as *i64
119 let imgbuf: *u8=sys_mmap(cap)
120 let o2: *i64=sys_mmap(16) as *i64
121 let ext: *u8=sys_mmap(16)
122 var from: i64=0
123 var idx: i64=0
124 // base of the capture's original URL (up to last '/') for resolving relative <img src>
125 var obase_off: i64=oro[q]
126 var obls: i64=oro[q]+orl[q]
127 var ox: i64=oro[q]
128 while ox<oro[q]+orl[q] { if (cdxbuf[ox] as i64)==0x2f { obls=ox } ox=ox+1 }
129 var obase_len: i64=obls-oro[q]+1
130 gw("[4] localizing media ...\n" as *u8)
131 var more: i64=1
132 while more==1 {
133 let p: i64=next_imgsrc(cont, ncont, from, o2)
134 if p<0 { more=0 } else {
135 from=p
136 if idx<24 {
137 let uoff: i64=o2[0]; let ulen: i64=o2[1]
138 names[idx]=0; namel[idx]=0
139 var dup: i64=0-1; var dj: i64=0
140 while dj<idx { if ulens_a[dj]==ulen { var eq: i64=1; var dk: i64=0; while dk<ulen { if (cont[uoffs_a[dj]+dk] as i64) != (cont[uoff+dk] as i64) { eq=0; dk=ulen } else { dk=dk+1 } } if eq==1 { dup=dj; dj=idx } } dj=dj+1 }
141 uoffs_a[idx]=uoff; ulens_a[idx]=ulen
142 if dup>=0 { names[idx]=names[dup]; namel[idx]=namel[dup] } else {
143 let el: i64=ext_of(cont, uoff, ulen, ext); ext[el]=0 as u8
144 let lname: *u8=sys_mmap(64)
145 var lo: i64=ap(lname, 0, "media/img" as *u8); lo=apn(lname, lo, idx)
146 if el>0 { lname[lo]=0x2e as u8; lo=lo+1; lo=ap(lname, lo, ext) }
147 lname[lo]=0 as u8
148 let sp: *u8=sys_mmap(512); var spo: i64=ap(sp, 0, ddir); sp[spo]=0x2f as u8; spo=spo+1; spo=ap(sp, spo, lname); sp[spo]=0 as u8
149 var fn: i64=read_file(sp, imgbuf, cap, lb)
150 if fn>0 { fn=lb[0] } else {
151 // resolve the absolute image URL
152 let rimg: *u8=sys_mmap(K_MAGIC_2048); var ro: i64=0
153 if dstarts(cont, uoff, uoff+ulen, "http" as *u8, 4)==1 { ro=apslice(rimg, 0, cont, uoff, ulen) } else { ro=apslice(rimg, 0, cdxbuf, obase_off, obase_len); var us2: i64=uoff; if ulen>=2 { if (cont[uoff] as i64)==0x2e { if (cont[uoff+1] as i64)==0x2f { us2=uoff+2 } } } if us2==uoff { if (cont[uoff] as i64)==0x2f { us2=uoff+1 } } ro=apslice(rimg, ro, cont, us2, ulen-(us2-uoff)) }
154 rimg[ro]=0 as u8
155 // primary: the image AT this capture's timestamp
156 let iu: *u8=sys_mmap(K_MAGIC_2048); var io: i64=ap(iu, 0, "https://web.archive.org/web/" as *u8); io=apslice(iu, io, cdxbuf, tso[q], tsl[q]); io=ap(iu, io, "id_/" as *u8); io=ap(iu, io, rimg); iu[io]=0 as u8
157 fn=fetch_retry(iu, store, imgbuf, cap, st, 3)
158 if fn>0 { if st[0]==200 { if is_image(imgbuf, fn)==1 { } else { fn=0 } } else { fn=0 } } else { fn=0 }
159 // CROSS-TEMPORAL RECOVERY: gap at this date -> find the image at ANY date it was captured
160 if fn==0 {
161 let cq: *u8=sys_mmap(K_MAGIC_2048); var qo: i64=ap(cq, 0, "https://web.archive.org/cdx/search/cdx?url=" as *u8); qo=ap(cq, qo, rimg); qo=ap(cq, qo, "&limit=1&filter=statuscode:200" as *u8); cq[qo]=0 as u8
162 let cr: *u8=sys_mmap(K_MAGIC_65536)
163 let cn: i64=fetch_retry(cq, store, cr, K_MAGIC_65536, st, 2)
164 if cn>10 { if st[0]==200 {
165 let R1: *i64=sys_mmap(64) as *i64; let R2: *i64=sys_mmap(64) as *i64
166 let nr: i64=cdx_rows(cr, cn, R1, R2, 4)
167 if nr>0 {
168 let tb: *i64=sys_mmap(16) as *i64
169 cdx_field(cr, R1[0], R2[0], 1, tb)
170 let ru: *u8=sys_mmap(K_MAGIC_2048); var rio: i64=ap(ru, 0, "https://web.archive.org/web/" as *u8); rio=apslice(ru, rio, cr, tb[0], tb[1]); rio=ap(ru, rio, "id_/" as *u8); rio=ap(ru, rio, rimg); ru[rio]=0 as u8
171 fn=fetch_retry(ru, store, imgbuf, cap, st, 2)
172 if fn>0 { if st[0]==200 { if is_image(imgbuf, fn)==1 { gw(" img" as *u8); gn(idx); gw(" RECOVERED from another date\n" as *u8) } else { fn=0 } } else { fn=0 } } else { fn=0 }
173 }
174 } }
175 }
176 if fn>0 { save_file(sp, imgbuf, fn) }
177 }
178 if fn>0 { names[idx]=lname as i64; namel[idx]=lo; gw(" img" as *u8); gn(idx); gw(" ok " as *u8); gn(fn); gw("B\n" as *u8) } else { gw(" img" as *u8); gn(idx); gw(" miss\n" as *u8) }
179 }
180 idx=idx+1
181 } else { more=0 }
182 }
183 }
184 let nmedia: i64=idx
185
186 // -- page.html: rewrite each <img src> to its local copy (or transparent placeholder) --
187 let pbuf: *u8=sys_mmap(cap)
188 var po: i64=ap(pbuf, 0, "<!DOCTYPE html><meta charset=utf-8><style>html,body{margin:0;padding:8px;background:#fff;color:#000;font-family:Times,Georgia,serif}img{max-width:100%;height:auto}table{max-width:100%}</style>" as *u8)
189 var ri: i64=0
190 var rj: i64=0
191 while rj<ncont {
192 var handled: i64=0
193 if dstarts(cont, rj, ncont, "src=" as *u8, 4)==1 { if rj+4<ncont { if (cont[rj+4] as i64)==0x22 {
194 po=ap(pbuf, po, "src=" as *u8); pbuf[po]=0x22 as u8; po=po+1
195 var nm: i64=0
196 if ri<nmedia { nm=names[ri] }
197 if nm != 0 { po=apslice(pbuf, po, nm as *u8, 0, namel[ri]) } else { po=ap(pbuf, po, "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" as *u8) }
198 ri=ri+1
199 rj=rj+5
200 var go: i64=1
201 while go==1 { if rj<ncont { if (cont[rj] as i64)==0x22 { go=0 } else { rj=rj+1 } } else { go=0 } }
202 handled=1
203 } } }
204 if handled==0 { pbuf[po]=cont[rj] as u8; po=po+1; rj=rj+1 }
205 }
206 mkpath(pth, ddir, "/page.html" as *u8)
207 if save_file(pth, pbuf, po)==0 { gw("page.html fail\n" as *u8); return 1 }
208
209 // -- index.html dashboard --
210 let ob: *u8=sys_mmap(cap)
211 var o: i64=0
212 o=ap(ob,o,"<!DOCTYPE html><html><head><meta charset=utf-8><title>Nishi Time Machine - " as *u8); o=ap(ob,o,dbuf); o=ap(ob,o,"</title><style>body{font-family:system-ui,Arial,sans-serif;margin:0;background:#0e0f13;color:#e7e7ea}.wrap{max-width:1040px;margin:0 auto;padding:28px}h1{font-size:26px;margin:0 0 4px}.sub{color:#8a8f9a;margin:0 0 18px}.prov{background:#171a21;border:1px solid #2a2d36;border-radius:10px;padding:16px 18px;line-height:1.6}.prov b{color:#7bd88f}h3{color:#cdd}.tl{display:flex;flex-wrap:wrap;gap:6px;margin:10px 0;max-height:140px;overflow:auto;background:#0b0c10;border:1px solid #2a2d36;border-radius:8px;padding:10px}.tl a{background:#23262f;border:1px solid #343846;border-radius:5px;padding:3px 8px;font-size:12px;color:#7bd8f0;text-decoration:none}.tl a:hover{background:#2f5d40;color:#fff}iframe{width:100%;height:540px;border:1px solid #2a2d36;border-radius:10px;background:#fff}pre{white-space:pre-wrap;word-break:break-all;background:#0b0c10;border:1px solid #2a2d36;border-radius:10px;padding:14px;font-size:12px;color:#9fe6c8;max-height:320px;overflow:auto}</style></head><body><div class=wrap>" as *u8)
213 o=ap(ob,o,"<h1>🕐 Nishi Time Machine</h1><p class=sub>" as *u8); o=ap(ob,o,dbuf); o=ap(ob,o," — reconstructed with LOCAL media (served by our own stack; no broken images)</p>" as *u8)
214 o=ap(ob,o,"<div class=prov><b>What we pulled</b><br>CDX index " as *u8); o=apn(ob,o,ncdx); o=ap(ob,o," B · <b>" as *u8); o=apn(ob,o,m); o=ap(ob,o,"</b> captures · earliest <b>" as *u8); o=apn(ob,o,dates[q]); o=ap(ob,o,"</b> · reconstructed page " as *u8); o=apn(ob,o,ncont); o=ap(ob,o," B · <b>" as *u8); o=apn(ob,o,nmedia); o=ap(ob,o,"</b> media localized</div>" as *u8)
215 o=ap(ob,o,"<h3>Scroll the history — click any date to load that snapshot</h3><div class=tl>" as *u8)
216 var d: i64=0; while d<m { if d<130 { o=ap(ob,o,"<a target=tm href='https://web.archive.org/web/" as *u8); o=apslice(ob,o,cdxbuf,tso[d],tsl[d]); o=ap(ob,o,"/" as *u8); o=apslice(ob,o,cdxbuf,oro[d],orl[d]); o=ap(ob,o,"'>" as *u8); o=apn(ob,o,dates[d]); o=ap(ob,o,"</a>" as *u8) } d=d+1 }
217 o=ap(ob,o,"</div><p class=sub>Default frame below = OUR sovereign reconstruction (local media). Click a date to load that snapshot from the archive.</p><iframe name=tm src=page.html></iframe>" as *u8)
218 o=ap(ob,o,"<h3>Raw archived HTML</h3><pre>" as *u8); o=esc_html(ob, o, cont, ncont); o=ap(ob,o,"</pre><div class=prov>Source: Internet Archive Wayback Machine · fetched, decoded & served sovereignly by Nishi</div></div></body></html>" as *u8)
219 mkpath(pth, ddir, "/index.html" as *u8)
220 if save_file(pth, ob, o)==0 { gw("index.html fail\n" as *u8); return 1 }
221
222 gw("\nSITE-VIEWER DONE: " as *u8); gn(nmedia); gw(" media localized -> web_assets/archive/" as *u8); gw(dbuf); gw("/index.html\n" as *u8)
223 return 0
224}