Overview
| Comment: | look ma, im tweetin |
|---|---|
| Downloads: | Tarball | ZIP archive | SQL archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
8f954221a11a00d23ad86812516851d3 |
| User & Date: | lexi on 2020-12-27 04:08:20 |
| Other Links: | manifest | tags |
Context
|
2020-12-28
| ||
| 23:42 | vastly improve the setup process check-in: d228cd7fcb user: lexi tags: trunk | |
|
2020-12-27
| ||
| 04:08 | look ma, im tweetin check-in: 8f954221a1 user: lexi tags: trunk | |
| 02:31 | permissions work now check-in: bbfea467bf user: lexi tags: trunk | |
Changes
Modified backend/pgsql.t from [36b1398523] to [2c2a215381].
205 205 $1::bigint, case when $2::text = '' then null else $2::text end, 206 206 $3::text, $4::text, 207 207 now(), now(), array[]::bigint[], array[]::bigint[] 208 208 ) returning id 209 209 ]]; -- TODO array handling 210 210 }; 211 211 212 - instance_timeline_fetch = { 212 + post_enum_author_uid = { 213 + params = {uint64,uint64,uint64,uint64, uint64}, sql = [[ 214 + select a.origin is null, 215 + p.id, p.author, p.subject, p.acl, p.body, 216 + extract(epoch from p.posted )::bigint, 217 + extract(epoch from p.discovered)::bigint, 218 + p.parent, p.convoheaduri 219 + from parsav_posts as p 220 + inner join parsav_actors as a on p.author = a.id 221 + where p.author = $5::bigint and 222 + ($1::bigint = 0 or p.posted <= to_timestamp($1::bigint)) and 223 + ($2::bigint = 0 or to_timestamp($2::bigint) < p.posted) 224 + order by (p.posted, p.discovered) desc 225 + limit case when $3::bigint = 0 then null 226 + else $3::bigint end 227 + offset $4::bigint 228 + ]] 229 + }; 230 + 231 + -- maybe there's some way to unify these two, idk, im tired 232 + 233 + timeline_instance_fetch = { 213 234 params = {uint64, uint64, uint64, uint64}, sql = [[ 214 235 select true, 215 236 p.id, p.author, p.subject, p.acl, p.body, 216 237 extract(epoch from p.posted )::bigint, 217 238 extract(epoch from p.discovered)::bigint, 218 239 p.parent, null::text 219 240 from parsav_posts as p ................................................................................ 394 415 else 395 416 return pqr {ct, res} 396 417 end 397 418 end 398 419 end 399 420 400 421 local terra row_to_post(r: &pqr, row: intptr): lib.mem.ptr(lib.store.post) 401 - --lib.io.fmt("body ptr %p len %llu\n", r:string(row,5), r:len(row,5)) 402 - --lib.io.fmt("acl ptr %p len %llu\n", r:string(row,4), r:len(row,4)) 403 422 var subj: rawstring, sblen: intptr 423 + var cvhu: rawstring, cvhlen: intptr 404 424 if r:null(row,3) 405 425 then subj = nil sblen = 0 406 426 else subj = r:string(row,3) sblen = r:len(row,3)+1 407 427 end 428 + if r:null(row,9) 429 + then cvhu = nil cvhlen = 0 430 + else cvhu = r:string(row,9) cvhlen = r:len(row,9)+1 431 + end 408 432 var p = [ lib.str.encapsulate(lib.store.post, { 409 433 subject = { `subj, `sblen }; 410 434 acl = {`r:string(row,4), `r:len(row,4)+1}; 411 435 body = {`r:string(row,5), `r:len(row,5)+1}; 412 - --convoheaduri = { `nil, `0 }; --FIXME 436 + convoheaduri = { `cvhu, `cvhlen }; --FIXME 413 437 }) ] 414 438 p.ptr.id = r:int(uint64,row,1) 415 439 p.ptr.author = r:int(uint64,row,2) 416 440 p.ptr.posted = r:int(uint64,row,6) 417 441 p.ptr.discovered = r:int(uint64,row,7) 418 442 if r:null(row,8) 419 443 then p.ptr.parent = 0 ................................................................................ 682 706 var r = queries.post_create.exec(src,post.author,post.subject,post.acl,post.body) 683 707 if r.sz == 0 then return 0 end 684 708 defer r:free() 685 709 var id = r:int(uint64,0,0) 686 710 return id 687 711 end]; 688 712 689 - instance_timeline_fetch = [terra(src: &lib.store.source, rg: lib.store.range) 713 + timeline_instance_fetch = [terra(src: &lib.store.source, rg: lib.store.range) 714 + var r = pqr { sz = 0 } 715 + var A,B,C,D = rg:matrix() -- :/ 716 + r = queries.timeline_instance_fetch.exec(src,A,B,C,D) 717 + 718 + var ret: lib.mem.ptr(lib.mem.ptr(lib.store.post)) ret:init(r.sz) 719 + for i=0,r.sz do ret.ptr[i] = row_to_post(&r, i) end -- MUST FREE ALL 720 + 721 + return ret 722 + end]; 723 + 724 + post_enum_author_uid = [terra( 725 + src: &lib.store.source, 726 + uid: uint64, 727 + rg: lib.store.range 728 + ): lib.mem.ptr(lib.mem.ptr(lib.store.post)) 690 729 var r = pqr { sz = 0 } 691 - if rg.mode == 0 then 692 - r = queries.instance_timeline_fetch.exec(src,rg.from_time,rg.to_time,0,0) 693 - elseif rg.mode == 1 then 694 - r = queries.instance_timeline_fetch.exec(src,rg.from_time,0,rg.to_idx,0) 695 - elseif rg.mode == 2 then 696 - r = queries.instance_timeline_fetch.exec(src,0,rg.to_time,0,rg.from_idx) 697 - elseif rg.mode == 3 then 698 - r = queries.instance_timeline_fetch.exec(src,0,0,rg.to_idx,rg.from_idx) 699 - end 730 + var A,B,C,D = rg:matrix() -- :/ 731 + r = queries.post_enum_author_uid.exec(src,A,B,C,D,uid) 700 732 701 733 var ret: lib.mem.ptr(lib.mem.ptr(lib.store.post)) ret:init(r.sz) 702 734 for i=0,r.sz do ret.ptr[i] = row_to_post(&r, i) end -- MUST FREE ALL 703 735 704 736 return ret 705 737 end]; 706 738
Modified doc/acl.md from [ea4e893c12] to [53956f184b].
7 7 * **mutuals**: matches users you follow who also follow you 8 8 * **followed**: matches users you follow 9 9 * **followers**: matches users who follow you 10 10 * **groupies**: matches users who follow you, but whom you do not follow 11 11 * **mentioned**: matches users who are mentioned in the post 12 12 * **staff**: matches instance staff (equivalent to `~%0`) 13 13 * **admin**: matches the individual named as the instance administrator, if any 14 -* **@**`handle`: matches the user `handle` 15 -* **+**`circle`: matches users you have categorized under `circle` 16 -* **#**`room`: matches users who are members of `room` 17 -* **%**`rank`: matches users of `rank` or higher (e.g. `%3` matches users of rank 3, 2, and 1). as a special case, `%0` matches ordinary users 18 -* **#**`room`**%**`rank`: matches users who hold `rank` in `room` 19 -* **<**`title`**>**: matches peers of the net who have been created `title` by the sovereign 20 -* **#**`room`**<**`title`**>**: matches peers of the chat who have been created `title` by `room` staff 14 +* **@**_handle_: matches the user *handle* 15 +* **+**_circle_: matches users you have categorized under *circle* 16 +* **#**_room_: matches users who are members of *room* 17 +* **%**_rank_: matches users of *rank* or higher (e.g. `%3` matches users of rank 3, 2, and 1). as a special case, `%0` matches ordinary users 18 +* **#**_room_**%**_rank_: matches users who hold *rank* in *room* 19 +* **<**_title_**>**: matches peers of the net who have been created *title* by the sovereign 20 +* **#**_room_**<**_title_**>**: matches peers of the chat who have been created *title* by *room* staff 21 21 22 22 to evaluate an ACL expression, `parsav` reads each term from start to finish. for each term, it considers whether it describes the user who is attempting to access the content. if the term matches, its policy is applied and the expression completes. if the term doesn't match, the server proceeds on to the next term and the process repeats until it finds a matching term or runs out of terms, applying the fallback policy. 23 23 24 24 **policy** is whether a term grants or denies access. the default term policy is **allow**, but you can control the policy with the keywords `allow` and `deny`. if a term finishes evaluating without any match being found, a fallback policy is applied; this fallback is the opposite of whatever the current policy is. this sounds confusing but makes ACL expressions much more intuitive; `allow @bob` and `deny trent` do exactly what you'd expect — the former allows bob and only bob in; the latter denies access only to trent, but grants access to the rest of the world. 25 25 26 26 expressions must contain at least one term to be valid. if they consist only of policy keywords, they will be rejected. 27 27
Modified parsav.t from [8c471232dd] to [1c10e6f8f8].
369 369 end 370 370 371 371 lib.load { 372 372 'srv'; 373 373 'render:nav'; 374 374 'render:login'; 375 375 'render:profile'; 376 - 'render:userpage'; 377 376 'render:compose'; 378 377 'render:tweet'; 378 + 'render:userpage'; 379 379 'render:timeline'; 380 380 'render:docpage'; 381 381 'route'; 382 382 } 383 383 384 384 do 385 385 local p = string.format('parsav: %s\nbuilt on %s\n', config.build.str, config.build.when)
Modified render/profile.t from [d063b74f92] to [efe49adad0].
10 10 var auxp: pstr 11 11 if co.aid ~= 0 and co.who.id == actor.id then 12 12 auxp = lib.str.plit '<a href="/conf/profile">alter</a>' 13 13 elseif co.aid ~= 0 then 14 14 aux:compose('<a href="/', actor.xid, '/follow">follow</a><a href="/', 15 15 actor.xid, '/chat">chat</a>') 16 16 if co.who.rights.powers:affect_users() then 17 - aux:push('<a href="/',11):push(actor.xid,0):push('/ctl">control</a>',17) 17 + aux:lpush('<a href="/'):push(actor.xid,0):lpush('/ctl">control</a>') 18 18 end 19 19 auxp = aux:finalize() 20 20 else 21 21 aux:compose('<a href="/', actor.xid, '/follow">remote follow</a>') 22 22 auxp = aux:finalize() 23 23 end 24 24 var avistr: lib.str.acc if actor.origin == 0 then
Modified render/timeline.t from [e2a4558814] to [873aeea861].
13 13 var stoptime = lib.osclock.time(nil) 14 14 15 15 var posts = [lib.mem.vec(lib.mem.ptr(lib.store.post))] { 16 16 sz = 0, run = 0 17 17 } 18 18 if mode == modes.follow then 19 19 elseif mode == modes.srvlocal then 20 - posts = co.srv:instance_timeline_fetch(lib.store.range { 20 + posts = co.srv:timeline_instance_fetch(lib.store.range { 21 21 mode = 1; -- T->I 22 22 from_time = stoptime; 23 23 to_idx = 64; 24 24 }) 25 25 elseif mode == modes.fediglobal then 26 26 elseif mode == modes.circle then 27 27 end
Modified render/tweet.t from [71eb9101d9] to [00c7b6fd89].
4 4 return pstr { ptr = s, ct = lib.str.sz(s) } 5 5 end 6 6 7 7 local terra 8 8 render_tweet(co: &lib.srv.convo, p: &lib.store.post, acc: &lib.str.acc) 9 9 var author: &lib.store.actor 10 10 for j = 0, co.actorcache.top do 11 - lib.io.fmt('scanning cache for author %llu (%llu/%llu)\n', p.author, j, co.actorcache.top) 12 11 if p.author == co.actorcache(j).ptr.id then 13 12 author = co.actorcache(j).ptr 14 - lib.io.fmt('cache hit on idx %llu, skipping db lookup\n', j) 15 13 goto foundauth 16 14 end 17 15 end 18 - lib.io.fmt('cache miss, checking db for id %llu\n', p.author) 19 16 author = co.actorcache:insert(co.srv:actor_fetch_uid(p.author)).ptr 20 - lib.io.fmt('got author %s\n', author.handle) 21 17 22 18 ::foundauth:: 23 19 var avistr: lib.str.acc if author.origin == 0 then 24 20 avistr:compose('/avi/',author.handle) 25 21 end 26 22 var timestr: int8[26] lib.osclock.ctime_r(&p.posted, ×tr[0]) 27 - lib.io.fmt('got body %s\n', author.handle) 28 23 29 24 var bhtml = lib.smackdown.html([lib.mem.ptr(int8)] {ptr=p.body,ct=0}) defer bhtml:free() 30 25 31 26 var idbuf: int8[lib.math.shorthand.maxlen] 32 27 var idlen = lib.math.shorthand.gen(p.id, idbuf) 33 28 var permalink: lib.str.acc permalink:compose('/post/',{idbuf,idlen}) 34 29
Modified render/userpage.t from [9774faf032] to [edce0da550].
3 3 render_userpage(co: &lib.srv.convo, actor: &lib.store.actor) 4 4 var ti: lib.str.acc 5 5 if co.aid ~= 0 and co.who.id == actor.id then 6 6 ti:compose('my profile') 7 7 else 8 8 ti:compose('profile :: ', actor.handle) 9 9 end 10 - var pftxt = lib.render.profile(co,actor) defer pftxt:free() 11 10 var tiptr = ti:finalize() 11 + 12 + var acc: lib.str.acc acc:init(1024) 13 + var pftxt = lib.render.profile(co,actor) defer pftxt:free() 14 + acc:ppush(pftxt) 15 + 16 + var stoptime = lib.osclock.time(nil) 17 + var posts = co.srv:post_enum_author_uid(actor.id, lib.store.range { 18 + mode = 1; -- T->I 19 + from_time = stoptime; 20 + to_idx = 64; 21 + }) 22 + 23 + for i = 0, posts.sz do 24 + lib.render.tweet(co, posts(i).ptr, &acc) 25 + posts(i):free() 26 + end 27 + posts:free() 28 + 29 + var bdf = acc:finalize() 12 30 co:stdpage([lib.srv.convo.page] { 13 - title = tiptr; body = pftxt; 31 + title = tiptr; body = bdf; 14 32 class = lib.str.plit 'profile'; 15 33 }) 16 34 17 35 tiptr:free() 36 + bdf:free() 18 37 end 19 38 20 39 return render_userpage
Modified srv.t from [8c264568c9] to [8808dbd7a5].
18 18 } 19 19 20 20 terra cfgcache:free() -- :/ 21 21 self.secret:free() 22 22 self.instance:free() 23 23 end 24 24 25 -terra srv:instance_timeline_fetch(r: lib.store.range): lib.mem.vec(lib.mem.ptr(lib.store.post)) 25 +terra srv:post_enum_author_uid(uid: uint64, r: lib.store.range): lib.mem.vec(lib.mem.ptr(lib.store.post)) 26 + var all: lib.mem.vec(lib.mem.ptr(lib.store.post)) all:init(64) 27 + for i=0,self.sources.ct do var src = self.sources.ptr + i 28 + if src.handle ~= nil and src.backend.timeline_instance_fetch ~= nil then 29 + var lst = src:post_enum_author_uid(uid,r) 30 + all:assure(all.sz + lst.ct) 31 + for j=0, lst.ct do all:push(lst.ptr[j]) end 32 + lst:free() 33 + end 34 + end 35 + return all 36 +end 37 + 38 +terra srv:timeline_instance_fetch(r: lib.store.range): lib.mem.vec(lib.mem.ptr(lib.store.post)) 26 39 var all: lib.mem.vec(lib.mem.ptr(lib.store.post)) all:init(64) 27 40 for i=0,self.sources.ct do var src = self.sources.ptr + i 28 - if src.handle ~= nil and src.backend.instance_timeline_fetch ~= nil then 29 - var lst = src:instance_timeline_fetch(r) 41 + if src.handle ~= nil and src.backend.timeline_instance_fetch ~= nil then 42 + var lst = src:timeline_instance_fetch(r) 30 43 all:assure(all.sz + lst.ct) 31 44 for j=0, lst.ct do all:push(lst.ptr[j]) end 32 45 lst:free() 33 46 end 34 47 end 35 48 return all 36 49 end
Modified static/style.scss from [9520b92c84] to [b0a8082b0c].
157 157 background-color: adjust-color($color, $lightness: -53%, $alpha: -0.7); 158 158 } 159 159 @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) { 160 160 background-color: adjust-color($color, $lightness: -53%, $alpha: -0.1); 161 161 } 162 162 } 163 163 164 +h1 { margin-top: 0 } 165 + 164 166 header { 165 167 position: fixed; 166 168 height: min-content; 167 169 width: 100vw; 168 170 margin: 0; 169 171 padding: 0; 170 172 border-bottom: 1px solid black; ................................................................................ 191 193 all: unset; 192 194 display: flex; 193 195 justify-content: flex-end; 194 196 align-items: center; 195 197 grid-column: 2/3; grid-row: 1/2; 196 198 > a[href] { 197 199 display: block; 198 - padding: 0.25in 0.15in; 200 + padding: 0.25in 0.10in; 199 201 //padding: calc((25% - 1em)/2) 0.15in; 200 202 &, &::after { transition: 0.3s; } 201 203 text-shadow: 1px 1px 1px black; 202 204 &:hover{ 203 205 transform: scale(120%); 204 206 } 205 207 } ................................................................................ 221 223 border: { 222 224 left: 1px solid black; 223 225 right: 1px solid black; 224 226 } 225 227 } 226 228 227 229 div.profile { 228 - @extend %box; 229 230 padding: 0.1in; 230 231 position: relative; 231 232 display: grid; 233 + margin-bottom: 0.4in; 232 234 grid-template-columns: 2fr 1fr; 233 - grid-template-rows: 1fr 1fr; 235 + grid-template-rows: max-content 1fr; 234 236 width: 100%; 235 237 > .banner { 236 238 grid-column: 1 / 3; 237 239 grid-row: 1 / 2; 238 240 display: grid; 239 241 grid-template-columns: 1.1in 1fr; 240 - grid-template-rows: 0.3in 1fr; 242 + grid-template-rows: max-content 1fr; 241 243 > .avatar { 242 244 display: block; 243 245 width: 1in; height: 1in; 244 246 grid-column: 1 / 2; 245 247 grid-row: 1 / 3; 246 248 border: 1px solid black; 247 249 } ................................................................................ 265 267 } 266 268 } 267 269 > .stats { 268 270 grid-column: 3 / 4; 269 271 grid-row: 1 / 3; 270 272 } 271 273 > .menu { 272 - grid-column: 1 / 3; 273 - grid-row: 2 / 3; 274 + grid-column: 1 / 3; grid-row: 2 / 3; 275 + padding-top: 0.075in; 276 + flex-wrap: wrap; 274 277 display: flex; 275 278 justify-content: center; 276 279 align-items: center; 277 280 > a[href] { 278 281 @extend %button; 279 282 display: block; 280 - margin: 0 0.05in; 283 + margin: 0.025in 0.05in; 281 284 } 282 285 > hr { 283 286 all: unset; 284 287 display: block; 285 288 height: 0.3in; 286 289 width: 1px; 287 290 border-left: 1px solid rgba(0,0,0,0.6); ................................................................................ 413 416 padding: 0.1in; 414 417 &:hover { font-weight: bold; } 415 418 } 416 419 } 417 420 418 421 code { 419 422 @extend %teletype; 420 - background: tone(-50%); 421 - border: 1px solid tone(-20%); 423 + background: tone(-55%); 424 + border: 1px inset tone(-20%); 422 425 padding: 2px 6px; 423 - text-shadow: 2px 2px black; 426 + font-size: 1.5ex !important; 427 + letter-spacing: 1.3px; 428 + padding-bottom: 3px; 429 + border-radius: 2px; 430 + vertical-align: baseline; 431 + box-shadow: 1px 1px 1px black; 424 432 } 425 433 426 434 div.post { 427 435 @extend %box; 428 436 display: grid; 429 437 grid-template-columns: 1in 1fr max-content; 430 - grid-template-rows: 1fr max-content; 438 + grid-template-rows: min-content max-content; 431 439 margin-bottom: 0.1in; 432 440 >.avatar { 433 441 grid-column: 1/2; grid-row: 1/2; 434 - width: 1in; 442 + img { display: block; width: 1in; margin:0; } 443 + background: linear-gradient(to bottom, tone(-53%), tone(-57%)); 435 444 } 436 445 >a[href].username { 437 446 display: block; 438 447 grid-column: 1/3; 439 448 grid-row: 2/3; 440 449 text-align: left; 441 450 text-decoration: none; ................................................................................ 469 478 @extend %teletype; 470 479 } 471 480 472 481 body.doc main { 473 482 @extend %serif; 474 483 li { margin-top: 0.05in; } 475 484 li:first-child { margin-top: 0; } 485 + h1, h2, h3, h4, h5, h6 { 486 + background: linear-gradient(to right, tone(-50%), transparent); 487 + margin-left: -0.4in; 488 + padding-left: 0.2in; 489 + text-shadow: 0 2px 0 black; 490 + } 476 491 }
Modified store.t from [5b8778f17f] to [4959208545].
91 91 from_idx: uint64 92 92 } 93 93 union { 94 94 to_time: m.timepoint 95 95 to_idx: uint64 96 96 } 97 97 } 98 + 99 +terra m.range:matrix() 100 + if self.mode == 0 then 101 + return self.from_time,self.to_time,0,0 102 + elseif self.mode == 1 then 103 + return self.from_time,0,self.to_idx,0 104 + elseif self.mode == 2 then 105 + return 0,self.to_time,0,self.from_idx 106 + elseif self.mode == 3 then 107 + return 0,0,self.to_idx,self.from_idx 108 + else lib.bail('invalid mode on timeline range!') end 109 +end 98 110 99 111 struct m.post { 100 112 id: uint64 101 113 author: uint64 102 114 subject: str 103 115 body: str 104 116 acl: str 105 117 posted: m.timepoint 106 118 discovered: m.timepoint 107 119 mentions: lib.mem.ptr(uint64) 108 120 circles: lib.mem.ptr(uint64) --only meaningful if scope is set to circle 109 - convo: uint64 121 + convoheaduri: str 110 122 parent: uint64 111 123 -- ephemera 112 124 localpost: bool 113 125 source: &m.source 114 126 } 115 127 116 128 local cnf = terralib.memoize(function(ty,rty) ................................................................................ 222 234 -- uid: uint64 223 235 224 236 actor_conf_str: cnf(rawstring, lib.mem.ptr(int8)) 225 237 actor_conf_int: cnf(intptr, lib.stat(intptr)) 226 238 227 239 post_save: {&m.source, &m.post} -> {} 228 240 post_create: {&m.source, &m.post} -> uint64 229 - actor_post_fetch_uid: {&m.source, uint64, m.range} -> lib.mem.ptr(m.post) 241 + post_enum_author_uid: {&m.source, uint64, m.range} -> lib.mem.ptr(lib.mem.ptr(m.post)) 230 242 convo_fetch_xid: {&m.source,rawstring} -> lib.mem.ptr(m.post) 231 243 convo_fetch_uid: {&m.source,uint64} -> lib.mem.ptr(m.post) 232 244 233 - actor_timeline_fetch_uid: {&m.source, uint64, m.range} -> lib.mem.ptr(lib.mem.ptr(m.post)) 234 - instance_timeline_fetch: {&m.source, m.range} -> lib.mem.ptr(lib.mem.ptr(m.post)) 245 + timeline_actor_fetch_uid: {&m.source, uint64, m.range} -> lib.mem.ptr(lib.mem.ptr(m.post)) 246 + timeline_instance_fetch: {&m.source, m.range} -> lib.mem.ptr(lib.mem.ptr(m.post)) 235 247 } 236 248 237 249 struct m.source { 238 250 backend: &m.backend 239 251 id: lib.mem.ptr(int8) 240 252 handle: &opaque 241 253 string: lib.mem.ptr(int8)
Modified view/tweet.tpl from [e1b2184d52] to [43ed0b36e9].
1 1 <div class="post"> 2 - <img class="avatar" src="@avatar"> 2 + <div class="avatar"><img src="@avatar"></div> 3 3 <a class="username" href="/@acctlink"> 4 4 <span class="nym">@nym</span> [<span class="handle">@xid</span>] 5 5 </a> 6 6 <div class="content"> 7 7 <div class="subject">@subject</div> 8 8 <div class="text">@text</div> 9 9 </div> 10 10 <a class="permalink" href="@permalink">@when</a> 11 11 </div>