| Comment: | add live updates, system to only update when necessary almost works |
|---|---|
| Downloads: | Tarball | ZIP archive | SQL archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
24ec4090837417ad087eff63c16c1336 |
| User & Date: | lexi on 2021-01-01 04:33:10 |
| Other Links: | manifest | tags |
|
2021-01-01
| ||
| 16:24 | move from webp to svg except where necessary check-in: aa17a03321 user: lexi tags: trunk | |
| 04:33 | add live updates, system to only update when necessary almost works check-in: 24ec409083 user: lexi tags: trunk | |
|
2020-12-31
| ||
| 02:18 | start work on user mgmt check-in: db4c5fd644 user: lexi tags: trunk | |
Modified acl.t from [7cc6c4467d] to [db020952e6].
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
id: uint64
}
terra m.eval(expr: lib.str.t, agent: m.agent)
end
terra lib.store.post:save(ctupdate: bool)
-- this post handles the messy details of registering a post's
-- circles and actors, and increments the edit-count if ctupdate
-- is true, which is should be in almost all cases.
if ctupdate then
self.chgcount = self.chgcount + 1
self.edited = lib.osclock.time(nil)
end
-- TODO extract mentions from body, circles from acl
self.source:post_save(self)
end
return m
|
> > > > > > > > | | | | |
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
id: uint64 } terra m.eval(expr: lib.str.t, agent: m.agent) end terra lib.store.post:comp() -- TODO extract mentions from body, circles from acl self.mentions = [lib.mem.ptr(uint64)].null() self.circles = [lib.mem.ptr(uint64)].null() self.convoheaduri = nil end terra lib.store.post:save(ctupdate: bool) -- this post handles the messy details of registering a post's -- circles and actors, and increments the edit-count if ctupdate -- is true, which is should be in almost all cases. if ctupdate then self.chgcount = self.chgcount + 1 self.edited = lib.osclock.time(nil) end self:comp() self.source:post_save(self) end return m |
Modified backend/pgsql.t from [35848d4bf0] to [3e99c3d4ab].
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 ... 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 ... 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 ... 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 ... 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 ... 651 652 653 654 655 656 657 658 659 660 661 662 663 664 .... 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 .... 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 .... 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 .... 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 |
update parsav_actors set
authtime = to_timestamp($2::bigint)
where id = $1::bigint
]];
};
auth_create_pw = {
params = {uint64, lib.mem.ptr(uint8)}, cmd = true, sql = [[
insert into parsav_auth (uid, name, kind, cred) values (
$1::bigint,
(select handle from parsav_actors where id = $1::bigint),
'pw-sha256', $2::bytea
)
]]
};
auth_purge_type = {
params = {rawstring, uint64, rawstring}, cmd = true, sql = [[
delete from parsav_auth where
((uid = 0 and name = $1::text) or uid = $2::bigint) and
kind like $3::text
]]
};
post_save = {
params = {
uint64, uint32, int64;
rawstring, rawstring, rawstring;
}, cmd = true, sql = [[
update parsav_posts set
................................................................................
chgcount = $2::integer,
edited = to_timestamp($3::bigint)
where id = $1::bigint
]]
};
post_create = {
params = {uint64, rawstring, rawstring, rawstring}, sql = [[
insert into parsav_posts (
author, subject, acl, body,
posted, discovered,
circles, mentions
) values (
$1::bigint, case when $2::text = '' then null else $2::text end,
$3::text, $4::text,
now(), now(), array[]::bigint[], array[]::bigint[]
) returning id
]]; -- TODO array handling
};
post_destroy_prepare = {
params = {uint64}, cmd = true, sql = [[
update parsav_posts set
................................................................................
post_fetch = {
params = {uint64}, sql = [[
select a.origin is null,
p.id, p.author, p.subject, p.acl, p.body,
extract(epoch from p.posted )::bigint,
extract(epoch from p.discovered)::bigint,
extract(epoch from p.edited )::bigint,
p.parent, p.convoheaduri, p.chgcount
from parsav_posts as p
inner join parsav_actors as a on p.author = a.id
where p.id = $1::bigint
]];
};
post_enum_author_uid = {
params = {uint64,uint64,uint64,uint64, uint64}, sql = [[
select a.origin is null,
p.id, p.author, p.subject, p.acl, p.body,
extract(epoch from p.posted )::bigint,
extract(epoch from p.discovered)::bigint,
extract(epoch from p.edited )::bigint,
p.parent, p.convoheaduri, p.chgcount
from parsav_posts as p
inner join parsav_actors as a on p.author = a.id
where p.author = $5::bigint and
($1::bigint = 0 or p.posted <= to_timestamp($1::bigint)) and
($2::bigint = 0 or to_timestamp($2::bigint) < p.posted)
order by (p.posted, p.discovered) desc
limit case when $3::bigint = 0 then null
................................................................................
timeline_instance_fetch = {
params = {uint64, uint64, uint64, uint64}, sql = [[
select true,
p.id, p.author, p.subject, p.acl, p.body,
extract(epoch from p.posted )::bigint,
extract(epoch from p.discovered)::bigint,
extract(epoch from p.edited )::bigint,
p.parent, null::text, p.chgcount
from parsav_posts as p
inner join parsav_actors as a on p.author = a.id
where
($1::bigint = 0 or p.posted <= to_timestamp($1::bigint)) and
($2::bigint = 0 or to_timestamp($2::bigint) < p.posted) and
(a.origin is null)
order by (p.posted, p.discovered) desc
limit case when $3::bigint = 0 then null
else $3::bigint end
................................................................................
]];
};
post_attach_ctl_ins = {
params = {uint64, uint64}, cmd=true, sql = [[
update parsav_posts set
artifacts = artifacts || $2::bigint
where id = $1::bigint and not
artifacts @> array[$2::bigint]
]];
};
post_attach_ctl_del = {
params = {uint64, uint64}, cmd=true, sql = [[
update parsav_posts set
artifacts = array_remove(artifacts, $2::bigint)
where id = $1::bigint and
artifacts @> array[$2::bigint]
]];
};
}
local struct pqr {
sz: intptr
res: &lib.pq.PGresult
}
terra pqr:free() if self.sz > 0 then lib.pq.PQclear(self.res) end end
................................................................................
then p.ptr.parent = 0
else p.ptr.parent = r:int(uint64,row,9)
end
if r:null(row,11)
then p.ptr.chgcount = 0
else p.ptr.chgcount = r:int(uint32,row,11)
end
p.ptr.localpost = r:bool(row,0)
return p
end
local terra row_to_actor(r: &pqr, row: intptr): lib.mem.ptr(lib.store.actor)
var a: lib.mem.ptr(lib.store.actor)
var av: rawstring, avlen: intptr
................................................................................
[lib.mem.ptr(lib.store.actor)] { ptr = nil, ct = 0 }
end];
post_create = [terra(
src: &lib.store.source,
post: &lib.store.post
): uint64
var r = queries.post_create.exec(src,post.author,post.subject,post.acl,post.body)
if r.sz == 0 then return 0 end
defer r:free()
var id = r:int(uint64,0,0)
return id
end];
post_destroy = [terra(
src: &lib.store.source,
post: uint64
): {}
................................................................................
lib.dbg('created new actor, establishing powers')
privupdate(src,ac)
lib.dbg('powers established')
return ac.id
end];
auth_create_pw = [terra(
src: &lib.store.source,
uid: uint64,
reset: bool,
pw: lib.mem.ptr(int8)
): {}
var hash: uint8[lib.crypt.algsz.sha256]
if lib.md.mbedtls_md(lib.md.mbedtls_md_info_from_type(lib.crypt.alg.sha256.id),
[&uint8](pw.ptr), pw.ct, &hash[0]) ~= 0 then
lib.bail('cannot hash password')
end
if reset then queries.auth_purge_type.exec(src, nil, uid, 'pw-%') end
queries.auth_create_pw.exec(src, uid, [lib.mem.ptr(uint8)] {ptr = &hash[0], ct = [hash.type.N]})
end];
auth_purge_pw = [terra(src: &lib.store.source, uid: uint64, handle: rawstring): {}
queries.auth_purge_type.exec(src, handle, uid, 'pw-%')
end];
auth_purge_otp = [terra(src: &lib.store.source, uid: uint64, handle: rawstring): {}
................................................................................
src: &lib.store.source,
post: &lib.store.post
): {}
queries.post_save.exec(src,
post.id, post.chgcount, post.edited,
post.subject, post.acl, post.body)
end];
auth_sigtime_user_fetch = [terra(
src: &lib.store.source,
uid: uint64
): lib.store.timepoint
var r = queries.auth_sigtime_user_fetch.exec(src, uid)
if r.sz > 0 then defer r:free()
................................................................................
auth_sigtime_user_alter = [terra(
src: &lib.store.source,
uid: uint64,
time: lib.store.timepoint
): {} queries.auth_sigtime_user_alter.exec(src, uid, time) end];
actor_auth_register_uid = nil; -- TODO better support non-view based auth
}
return b
|
| | | > > > > > > > > > > > > > > | > > | | > | | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | > > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > | < > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 ... 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 ... 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 ... 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 ... 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 ... 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 .... 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 .... 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 .... 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 .... 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 |
update parsav_actors set
authtime = to_timestamp($2::bigint)
where id = $1::bigint
]];
};
auth_create_pw = {
params = {uint64, binblob, pstring}, cmd = true, sql = [[
insert into parsav_auth (uid, name, kind, cred, comment) values (
$1::bigint,
(select handle from parsav_actors where id = $1::bigint),
'pw-sha256', $2::bytea,
$3::text
)
]]
};
auth_purge_type = {
params = {rawstring, uint64, rawstring}, cmd = true, sql = [[
delete from parsav_auth where
((uid = 0 and name = $1::text) or uid = $2::bigint) and
kind like $3::text
]]
};
auth_enum_uid = {
params = {uint64}, sql = [[
select aid, kind, comment, netmask, blacklist from parsav_auth where uid = $1::bigint
]];
};
auth_enum_handle = {
params = {rawstring}, sql = [[
select aid, kind, comment, netmask, blacklist from parsav_auth where name = $1::text
]];
};
post_save = {
params = {
uint64, uint32, int64;
rawstring, rawstring, rawstring;
}, cmd = true, sql = [[
update parsav_posts set
................................................................................
chgcount = $2::integer,
edited = to_timestamp($3::bigint)
where id = $1::bigint
]]
};
post_create = {
params = {
uint64, rawstring, rawstring, rawstring,
uint64, uint64, rawstring
}, sql = [[
insert into parsav_posts (
author, subject, acl, body,
parent, posted, discovered,
circles, mentions, convoheaduri
) values (
$1::bigint, case when $2::text = '' then null else $2::text end,
$3::text, $4::text,
$5::bigint, to_timestamp($6::bigint), now(),
array[]::bigint[], array[]::bigint[], $7::text
) returning id
]]; -- TODO array handling
};
post_destroy_prepare = {
params = {uint64}, cmd = true, sql = [[
update parsav_posts set
................................................................................
post_fetch = {
params = {uint64}, sql = [[
select a.origin is null,
p.id, p.author, p.subject, p.acl, p.body,
extract(epoch from p.posted )::bigint,
extract(epoch from p.discovered)::bigint,
extract(epoch from p.edited )::bigint,
p.parent, p.convoheaduri, p.chgcount,
coalesce(c.value, -1)::smallint
from parsav_posts as p
inner join parsav_actors as a on p.author = a.id
left join parsav_actor_conf_ints as c on c.uid = a.id and c.key = 'ui-accent'
where p.id = $1::bigint
]];
};
post_enum_parent = {
params = {uint64}, sql = [[
select a.origin is null,
p.id, p.author, p.subject, p.acl, p.body,
extract(epoch from p.posted )::bigint,
extract(epoch from p.discovered)::bigint,
extract(epoch from p.edited )::bigint,
p.parent, p.convoheaduri, p.chgcount,
coalesce(c.value, -1)::smallint
from parsav_posts as p
inner join parsav_actors as a on a.id = p.author
left join parsav_actor_conf_ints as c on c.uid = a.id and c.key = 'ui-accent'
where p.parent = $1::bigint
order by p.posted, p.discovered asc
]]
};
thread_latest_arrival_calc = {
params = {uint64}, sql = [[
with recursive posts(id) as (
select id from parsav_posts where parent = $1::bigint
union
select p.id from parsav_posts as p
inner join posts on posts.id = p.parent
),
maxes as (
select unnest(array[max(p.posted), max(p.discovered), max(p.edited)]) as m
from posts
inner join parsav_posts as p
on p.id = posts.id
)
select extract(epoch from max(m))::bigint from maxes
]];
};
post_enum_author_uid = {
params = {uint64,uint64,uint64,uint64, uint64}, sql = [[
select a.origin is null,
p.id, p.author, p.subject, p.acl, p.body,
extract(epoch from p.posted )::bigint,
extract(epoch from p.discovered)::bigint,
extract(epoch from p.edited )::bigint,
p.parent, p.convoheaduri, p.chgcount,
coalesce((select value from parsav_actor_conf_ints as c where
c.uid = $1::bigint and c.key = 'ui-accent'),-1)::smallint
from parsav_posts as p
inner join parsav_actors as a on p.author = a.id
where p.author = $5::bigint and
($1::bigint = 0 or p.posted <= to_timestamp($1::bigint)) and
($2::bigint = 0 or to_timestamp($2::bigint) < p.posted)
order by (p.posted, p.discovered) desc
limit case when $3::bigint = 0 then null
................................................................................
timeline_instance_fetch = {
params = {uint64, uint64, uint64, uint64}, sql = [[
select true,
p.id, p.author, p.subject, p.acl, p.body,
extract(epoch from p.posted )::bigint,
extract(epoch from p.discovered)::bigint,
extract(epoch from p.edited )::bigint,
p.parent, null::text, p.chgcount,
coalesce(c.value, -1)::smallint
from parsav_posts as p
inner join parsav_actors as a on p.author = a.id
left join parsav_actor_conf_ints as c on c.uid = a.id and c.key = 'ui-accent'
where
($1::bigint = 0 or p.posted <= to_timestamp($1::bigint)) and
($2::bigint = 0 or to_timestamp($2::bigint) < p.posted) and
(a.origin is null)
order by (p.posted, p.discovered) desc
limit case when $3::bigint = 0 then null
else $3::bigint end
................................................................................
]];
};
post_attach_ctl_ins = {
params = {uint64, uint64}, cmd=true, sql = [[
update parsav_posts set
artifacts = artifacts || $2::bigint
where id = $1::bigint and not
artifacts @> array[$2::bigint] -- prevent duplication
]];
};
post_attach_ctl_del = {
params = {uint64, uint64}, cmd=true, sql = [[
update parsav_posts set
artifacts = array_remove(artifacts, $2::bigint)
where id = $1::bigint and
artifacts @> array[$2::bigint]
]];
};
actor_conf_str_get = {
params = {uint64, rawstring}, sql = [[
select value from parsav_actor_conf_strs where
uid = $1::bigint and
key = $2::text
limit 1
]];
};
actor_conf_str_set = {
params = {uint64, rawstring, rawstring}, cmd = true, sql = [[
insert into parsav_actor_conf_strs (uid,key,value)
values ($1::bigint, $2::text, $3::text)
on conflict (uid,key) do update set value = $3::text
]];
};
actor_conf_str_enum = {
params = {uint64}, sql = [[
select value from parsav_actor_conf_strs where uid = $1::bigint
]];
};
actor_conf_str_reset = {
params = {uint64, rawstring}, cmd = true, sql = [[
delete from parsav_actor_conf_strs where
uid = $1::bigint and ($2::text is null or key = $2::text)
]]
};
actor_conf_int_get = {
params = {uint64, rawstring}, sql = [[
select value from parsav_actor_conf_ints where
uid = $1::bigint and
key = $2::text
limit 1
]];
};
actor_conf_int_set = {
params = {uint64, rawstring, uint64}, cmd = true, sql = [[
insert into parsav_actor_conf_ints (uid,key,value)
values ($1::bigint, $2::text, $3::bigint)
on conflict (uid,key) do update set value = $3::bigint
]];
};
actor_conf_int_enum = {
params = {uint64}, sql = [[
select value from parsav_actor_conf_ints where uid = $1::bigint
]];
};
actor_conf_int_reset = {
params = {uint64, rawstring}, cmd = true, sql = [[
delete from parsav_actor_conf_ints where
uid = $1::bigint and ($2::text is null or key = $2::text)
]]
};
}
local struct pqr {
sz: intptr
res: &lib.pq.PGresult
}
terra pqr:free() if self.sz > 0 then lib.pq.PQclear(self.res) end end
................................................................................
then p.ptr.parent = 0
else p.ptr.parent = r:int(uint64,row,9)
end
if r:null(row,11)
then p.ptr.chgcount = 0
else p.ptr.chgcount = r:int(uint32,row,11)
end
p.ptr.accent = r:int(int16,row,12)
p.ptr.localpost = r:bool(row,0)
return p
end
local terra row_to_actor(r: &pqr, row: intptr): lib.mem.ptr(lib.store.actor)
var a: lib.mem.ptr(lib.store.actor)
var av: rawstring, avlen: intptr
................................................................................
[lib.mem.ptr(lib.store.actor)] { ptr = nil, ct = 0 }
end];
post_create = [terra(
src: &lib.store.source,
post: &lib.store.post
): uint64
var r = queries.post_create.exec(src,
post.author,post.subject,post.acl,post.body,
post.parent,post.posted,post.convoheaduri
)
if r.sz == 0 then return 0 end
defer r:free()
var id = r:int(uint64,0,0)
post.source = src
return id
end];
post_destroy = [terra(
src: &lib.store.source,
post: uint64
): {}
................................................................................
lib.dbg('created new actor, establishing powers')
privupdate(src,ac)
lib.dbg('powers established')
return ac.id
end];
auth_enum_uid = [terra(
src: &lib.store.source,
uid: uint64
): lib.mem.ptr(lib.mem.ptr(lib.store.auth))
var r = queries.auth_enum_uid.exec(src,uid)
if r.sz == 0 then return [lib.mem.ptr(lib.mem.ptr(lib.store.auth))].null() end
var ret = lib.mem.heapa([lib.mem.ptr(lib.store.auth)], r.sz)
for i=0, r.sz do
var kind = r:_string(i, 1)
var comment = r:_string(i, 2)
var a = [ lib.str.encapsulate(lib.store.auth, {
kind = {`kind.ptr, `kind.ct};
comment = {`comment.ptr, `comment.ct};
}) ]
a.ptr.aid = r:int(uint64, i, 0)
a.ptr.netmask = r:cidr(i, 3)
a.ptr.blacklist = r:bool(i, 4)
ret.ptr[i] = a
end
return ret
end];
auth_attach_pw = [terra(
src: &lib.store.source,
uid: uint64,
reset: bool,
pw: pstring,
comment: pstring
): {}
var hash: uint8[lib.crypt.algsz.sha256]
if lib.md.mbedtls_md(lib.md.mbedtls_md_info_from_type(lib.crypt.alg.sha256.id),
[&uint8](pw.ptr), pw.ct, &hash[0]) ~= 0 then
lib.bail('cannot hash password')
end
if reset then queries.auth_purge_type.exec(src, nil, uid, 'pw-%') end
queries.auth_create_pw.exec(src, uid, binblob {ptr = &hash[0], ct = [hash.type.N]}, comment)
end];
auth_purge_pw = [terra(src: &lib.store.source, uid: uint64, handle: rawstring): {}
queries.auth_purge_type.exec(src, handle, uid, 'pw-%')
end];
auth_purge_otp = [terra(src: &lib.store.source, uid: uint64, handle: rawstring): {}
................................................................................
src: &lib.store.source,
post: &lib.store.post
): {}
queries.post_save.exec(src,
post.id, post.chgcount, post.edited,
post.subject, post.acl, post.body)
end];
post_enum_parent = [terra(
src: &lib.store.source,
post: uint64
): lib.mem.ptr(lib.mem.ptr(lib.store.post))
var r = queries.post_enum_parent.exec(src,post)
if r.sz == 0 then
return [lib.mem.ptr(lib.mem.ptr(lib.store.post))].null()
end
defer r:free()
var lst = lib.mem.heapa([lib.mem.ptr(lib.store.post)], r.sz)
for i=0, r.sz do lst.ptr[i] = row_to_post(&r, i) end
return lst
end];
thread_latest_arrival_calc = [terra(
src: &lib.store.source,
post: uint64
): lib.store.timepoint
var r = queries.thread_latest_arrival_calc.exec(src,post)
if r.sz == 0 or r:null(0,0) then return 0 end
var tp: lib.store.timepoint = r:int(int64,0,0)
r:free()
return tp
end];
auth_sigtime_user_fetch = [terra(
src: &lib.store.source,
uid: uint64
): lib.store.timepoint
var r = queries.auth_sigtime_user_fetch.exec(src, uid)
if r.sz > 0 then defer r:free()
................................................................................
auth_sigtime_user_alter = [terra(
src: &lib.store.source,
uid: uint64,
time: lib.store.timepoint
): {} queries.auth_sigtime_user_alter.exec(src, uid, time) end];
actor_conf_str_enum = nil;
actor_conf_str_get = [terra(src: &lib.store.source, uid: uint64, key: rawstring): pstring
var r = queries.actor_conf_str_get.exec(src, uid, key)
if r.sz > 0 then
var ret = r:String(0,0)
r:free()
return ret
else return pstring.null() end
end];
actor_conf_str_set = [terra(src: &lib.store.source, uid: uint64, key: rawstring, value: rawstring): {}
queries.actor_conf_str_set.exec(src,uid,key,value) end];
actor_conf_str_reset = [terra(src: &lib.store.source, uid: uint64, key: rawstring): {}
queries.actor_conf_str_reset.exec(src,uid,key) end];
actor_conf_int_enum = nil;
actor_conf_int_get = [terra(src: &lib.store.source, uid: uint64, key: rawstring)
var r = queries.actor_conf_int_get.exec(src, uid, key)
if r.sz > 0 then
var ret = r:int(uint64,0,0)
r:free()
return ret, true
end
return 0, false
end];
actor_conf_int_set = [terra(src: &lib.store.source, uid: uint64, key: rawstring, value: uint64): {}
queries.actor_conf_int_set.exec(src,uid,key,value) end];
actor_conf_int_reset = [terra(src: &lib.store.source, uid: uint64, key: rawstring): {}
queries.actor_conf_int_reset.exec(src,uid,key) end];
actor_auth_register_uid = nil; -- TODO better support non-view based auth
}
return b
|
Modified backend/schema/pgsql-drop.sql from [17a37aa5f6] to [fa02548662].
1 2 3 4 5 6 7 8 9 10 11 12 |
-- destroy absolutely everything drop table if exists parsav_config cascade; drop table if exists parsav_servers cascade; drop table if exists parsav_actors cascade; drop table if exists parsav_rights cascade; drop table if exists parsav_posts cascade; drop table if exists parsav_conversations cascade; drop table if exists parsav_rels cascade; drop table if exists parsav_acts cascade; drop table if exists parsav_log cascade; drop table if exists parsav_artifacts cascade; |
> > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
-- destroy absolutely everything drop table if exists parsav_config cascade; drop table if exists parsav_servers cascade; drop table if exists parsav_actors cascade; drop table if exists parsav_actor_conf_strs cascade; drop table if exists parsav_actor_conf_ints cascade; drop table if exists parsav_rights cascade; drop table if exists parsav_posts cascade; drop table if exists parsav_conversations cascade; drop table if exists parsav_rels cascade; drop table if exists parsav_acts cascade; drop table if exists parsav_log cascade; drop table if exists parsav_artifacts cascade; |
Modified backend/schema/pgsql.sql from [135f2b367a] to [b4d8dee98e].
167 168 169 170 171 172 173 174 175 176 177 |
nature smallint not null, -- silence, suspend, disemvowel, censor, noreply, etc victim bigint not null, -- can be user, room, or post expire timestamp, -- auto-expires if set review timestamp, -- brings up for review at given time if set reason text, -- visible to victim if set context text -- admin-only note ); -- create a temporary managed auth table; we can delete this later -- if it ends up being replaced with a view %include pgsql-auth.sql% |
> > > > > > > > > |
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
nature smallint not null, -- silence, suspend, disemvowel, censor, noreply, etc victim bigint not null, -- can be user, room, or post expire timestamp, -- auto-expires if set review timestamp, -- brings up for review at given time if set reason text, -- visible to victim if set context text -- admin-only note ); create table parsav_actor_conf_strs ( uid bigint not null references parsav_actors(id) on delete cascade, key text not null, value text not null, unique (uid,key) ); create table parsav_actor_conf_ints ( uid bigint not null references parsav_actors(id) on delete cascade, key text not null, value bigint not null, unique (uid,key) ); -- create a temporary managed auth table; we can delete this later -- if it ends up being replaced with a view %include pgsql-auth.sql% |
Modified config.lua from [3d0e5432a4] to [cd48dd2db6].
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
embeds = {
-- TODO with gzip compression, svg is dramatically superior to webp
-- we should have a build-time option to serve svg so instances
-- proxied behind nginx can serve svgz, or possibly just straight-up
-- add support for content-encoding headers and pre-compress the
-- damn things before compiling
{'style.css', 'text/css'};
{'default-avatar.webp', 'image/webp'};
{'padlock.webp', 'image/webp'};
{'warn.webp', 'image/webp'};
{'query.webp', 'image/webp'};
};
}
if os.getenv('parsav_let_me_be_an_idiot') == "i know what i'm doing" then
conf.braingeniousmode = true -- SOUND GENERAL QUARTERS
end
if u.ping '.fslckout' or u.ping '_FOSSIL_' then
if u.ping '_FOSSIL_' then default_os = 'windows' end
conf.build.branch = u.exec { 'fossil', 'branch', 'current' }
|
> > |
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
embeds = {
-- TODO with gzip compression, svg is dramatically superior to webp
-- we should have a build-time option to serve svg so instances
-- proxied behind nginx can serve svgz, or possibly just straight-up
-- add support for content-encoding headers and pre-compress the
-- damn things before compiling
{'style.css', 'text/css'};
{'live.js', 'text/javascript'}; -- rrrrrrrr
{'default-avatar.webp', 'image/webp'};
{'padlock.webp', 'image/webp'};
{'warn.webp', 'image/webp'};
{'query.webp', 'image/webp'};
};
default_ui_accent = tonumber(default('parsav_ui_default_accent',323));
}
if os.getenv('parsav_let_me_be_an_idiot') == "i know what i'm doing" then
conf.braingeniousmode = true -- SOUND GENERAL QUARTERS
end
if u.ping '.fslckout' or u.ping '_FOSSIL_' then
if u.ping '_FOSSIL_' then default_os = 'windows' end
conf.build.branch = u.exec { 'fossil', 'branch', 'current' }
|
Modified math.t from [bc01716315] to [1d1d177061].
184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
else dgtct = dgtct + 1 end end else buf = buf - 1 @buf = 0x30 end return buf end terra m.ndigits(n: intptr, base: intptr): intptr var c = base var i = 1 while true do if n < c then return i end c = c * base |
> > > > > > > > > > > > > > > > > > |
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
else dgtct = dgtct + 1 end end else buf = buf - 1 @buf = 0x30 end return buf end terra m.decparse(s: pstring): {intptr, bool} if not s then return 0, false end var val:intptr = 0 var c = s.ptr while @c ~= 0 do if @c >= 0x30 and @c <= 0x39 then val = val * 10 val = val + (@c - 0x30) else return 0, false end c = c + 1 if s.ct ~= 0 and (c - s.ptr > s.ct) then lib.dbg('reached end') return val, true end end return val, true end terra m.ndigits(n: intptr, base: intptr): intptr var c = base var i = 1 while true do if n < c then return i end c = c * base |
Modified mgtool.t from [faf7450a82] to [39281cf1cf].
59 60 61 62 63 64 65 66 67 68 69 70 71 72 ... 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 ... 334 335 336 337 338 339 340 341 342 343 344 345 346 347 |
sid: uint64
iname: rawstring
}
idelegate.metamethods.__methodmissing = macro(function(meth, self, ...)
local expr = {...}
local rt
for _,f in pairs(lib.store.backend.entries) do
local fn = f.field or f[1]
local ft = f.type or f[2]
if fn == meth then rt = ft.type.returntype break end
end
return quote
................................................................................
if tmppw[i] >= 36 then
tmppw[i] = tmppw[i] + (0x61 - 36)
elseif tmppw[i] >= 10 then
tmppw[i] = tmppw[i] + (0x41 - 10)
else tmppw[i] = tmppw[i] + 0x30 end
end
lib.dbg('assigning temporary password')
dlg:auth_create_pw(uid, reset, pstr {
ptr = [rawstring](tmppw), ct = 32
})
end
local terra ipc_report(acks: lib.mem.ptr(lib.ipc.ack), rep: rawstring)
var decbuf: int8[21]
for i=0,acks.ct do
var num = lib.math.decstr(acks(i).clid, &decbuf[20])
if acks(i).success then
................................................................................
if lib.str.cmp(cfmode.arglist(0),'chsec') == 0 then
var sec: int8[65] gensec(&sec[0])
dlg:conf_set('server-secret', &sec[0])
lib.report('server secret reset')
elseif lib.str.cmp(cfmode.arglist(0),'refresh') == 0 then
cfmode.no_notify = false -- duh
else goto cmderr end
elseif cfmode.arglist.ct == 3 and
lib.str.cmp(cfmode.arglist(0),'set') == 0 then
dlg:conf_set(cfmode.arglist(1),cfmode.arglist(2))
lib.report('parameter set')
else goto cmderr end
-- successful commands fall through
|
> | | | > > > > > > |
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 ... 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 ... 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 |
sid: uint64
iname: rawstring
}
idelegate.metamethods.__methodmissing = macro(function(meth, self, ...)
local expr = {...}
local rt
for _,f in pairs(lib.store.backend.entries) do
local fn = f.field or f[1]
local ft = f.type or f[2]
if fn == meth then rt = ft.type.returntype break end
end
return quote
................................................................................
if tmppw[i] >= 36 then
tmppw[i] = tmppw[i] + (0x61 - 36)
elseif tmppw[i] >= 10 then
tmppw[i] = tmppw[i] + (0x41 - 10)
else tmppw[i] = tmppw[i] + 0x30 end
end
lib.dbg('assigning temporary password')
dlg:auth_attach_pw(uid, reset,
pstr { ptr = [rawstring](tmppw), ct = 32 },
lib.str.plit 'temporary password');
end
local terra ipc_report(acks: lib.mem.ptr(lib.ipc.ack), rep: rawstring)
var decbuf: int8[21]
for i=0,acks.ct do
var num = lib.math.decstr(acks(i).clid, &decbuf[20])
if acks(i).success then
................................................................................
if lib.str.cmp(cfmode.arglist(0),'chsec') == 0 then
var sec: int8[65] gensec(&sec[0])
dlg:conf_set('server-secret', &sec[0])
lib.report('server secret reset')
elseif lib.str.cmp(cfmode.arglist(0),'refresh') == 0 then
cfmode.no_notify = false -- duh
else goto cmderr end
elseif cfmode.arglist.ct == 2 and
lib.str.cmp(cfmode.arglist(0),'reset') == 0 or
lib.str.cmp(cfmode.arglist(0),'clear') == 0 or
lib.str.cmp(cfmode.arglist(0),'unset') == 0 then
dlg:conf_reset(cfmode.arglist(1))
lib.report('parameter cleared')
elseif cfmode.arglist.ct == 3 and
lib.str.cmp(cfmode.arglist(0),'set') == 0 then
dlg:conf_set(cfmode.arglist(1),cfmode.arglist(2))
lib.report('parameter set')
else goto cmderr end
-- successful commands fall through
|
Modified parsav.md from [409d9b6b60] to [bfa0a26bd5].
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
## dependencies
* runtime
* mongoose
* json-c
* mbedtls
* **postgresql backend:**
* postgresql-libs
* compile-time
* cmark (commonmark implementation), for transformation of the help files, whose source is in commonmark. online documentation transforms these into html and embeds them in the binary; cmark is also used to to produce the troff source which is used to build the offline documentation. disable with `parsav_online_documentation=no parsav_offline_documentation=no`
* troff implementation (tested with groff but as far as i know we don't need any groff-specific extensions) to produce PDFs and manpages from the cmark-generated intermediate forms. disable with `parsav_offline_documentation=no`
additional preconfigure dependencies are necessary if you are building directly from trunk, rather than from a release tarball that includes certain build artifacts which need to be embedded in the binary:
* inkscape, for rendering out UI graphics
* cwebp (libwebp package), for transforming inkscape PNGs to webp
* sassc, for compiling the SCSS stylesheet into its final CSS
all builds require terra, which, unfortunately, requires installing an older version of llvm, v9 at the latest (which i develop parsav under). with any luck, your distro will be clever enough to package terra and its dependencies properly (it's trivial on nix, tho you'll need to tweak the terra expression to select a more recent llvm package); Arch Linux is one of those distros which is not so clever, and whose (AUR) terra package is totally broken. due to these unfortunate circumstances, terra is distributed not just in source form, but also in the the form of LLVM IR. distributions will also be made in the form of tarballed object code and assembly listings for various common platforms, currently including x86-32/64, arm7hf, aarch64, riscv, mips32/64, and ppc64/64le.
i've noticed that terra (at least with llvm9) seems to get a bit cantankerous and trigger llvm to fail with bizarre errors when you try to cross-compile parsav from x86-64 to any other platform, even x86-32. i don't know if this problem exists on other architectures or in what form, but as a workaround, the current cross-compile process consists of generating LLVM IR (ostensible for x86-64, though this is in reality an architecture-independent language), and then compiling that down to an object file with llc. this is an enormous hassle; hopefully the terra people will fix this eventually.
also note that, while parsav has a flag to build with ASAN, ASAN has proven unusable for most purposes as it routinely reports false positive buffer-heap-overflows. if you figure out how to defuckulate this, i will be overjoyed.
## building
first, either install any missing dependencies as shared libraries, or build them as static libraries with the command `make dep.$LIBRARY`. as a shortcut, `make dep` will build all dependencies as static libraries. note that if the build system finds a static version of a library in the `lib/` folder, it will use that instead of any system library. note that these commands require GNU make (it may be installed as `gmake` on your system), although this is a fairly soft dependency -- if you really need to build it on BSD make, you can probably translate it with a minute or so of work; you'll just have to do some of the various gmake functions' work manually. this may be worthwhile if you're packaging for a BSD.
|
| | |
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
## dependencies * runtime * mongoose * json-c * mbedtls * **postgresql backend:** * postgresql-libs * compile-time * cmark (commonmark implementation), for transformation of the help files, whose source is in commonmark. online documentation transforms these into html and embeds them in the binary; cmark is also used to to produce the troff source which is used to build the offline documentation. disable with `parsav_online_documentation=no parsav_offline_documentation=no` * troff implementation (tested with groff but as far as i know we don't need any groff-specific extensions) to produce PDFs and manpages from the cmark-generated intermediate forms. disable with `parsav_offline_documentation=no` additional preconfigure dependencies are necessary if you are building directly from trunk, rather than from a release tarball that includes certain build artifacts which need to be embedded in the binary: * inkscape, for rendering out UI graphics * cwebp (libwebp package), for transforming inkscape PNGs to webp * sassc, for compiling the SCSS stylesheet into its final CSS all builds require terra, which, unfortunately, requires installing an older version of llvm, v9 at the latest (which i develop parsav under). with any luck, your distro will be clever enough to package terra and its dependencies properly (it's trivial on nix, tho you'll need to tweak the terra expression to select a more recent llvm package); Arch Linux is one of those distros which is not so clever, and whose (AUR) terra package is totally broken. due to these unfortunate circumstances, terra is distributed not just in source form, but also in the the form of LLVM IR. distributions will also be made in the form of tarballed object code and assembly listings for various common platforms, currently including x86-32/64, arm7hf, aarch64, riscv, mips32/64, and ppc64/64le. i've noticed that terra (at least with llvm9) seems to get a bit cantankerous and trigger llvm to fail with bizarre errors when you try to cross-compile parsav from x86-64 to any other platform, even x86-32. i don't know if this problem exists on other architectures or in what form, but as a workaround, the current cross-compile process consists of generating LLVM IR (ostensibly for x86-64, though this is in reality an architecture-independent language), and then compiling that down to an object file with llc. this is an enormous hassle; hopefully the terra (or llvm?) people will fix this eventually. also note that, while parsav has a flag to build with ASAN, ASAN has proven unusable for most purposes as it routinely reports false positive buffer-heap-overflows. if you figure out how to defuckulate this, i will be overjoyed. ## building first, either install any missing dependencies as shared libraries, or build them as static libraries with the command `make dep.$LIBRARY`. as a shortcut, `make dep` will build all dependencies as static libraries. note that if the build system finds a static version of a library in the `lib/` folder, it will use that instead of any system library. note that these commands require GNU make (it may be installed as `gmake` on your system), although this is a fairly soft dependency -- if you really need to build it on BSD make, you can probably translate it with a minute or so of work; you'll just have to do some of the various gmake functions' work manually. this may be worthwhile if you're packaging for a BSD. |
Modified parsav.t from [90c24eca6f] to [4b4b2876e4].
602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 |
print(util.dump(config))
os.exit(0)
end
local holler = print
local suffix = config.exe and '' or ('.'..config.outform)
local out = 'parsavd' .. suffix
local linkargs = {'-O4'}
local target = config.tgttrip and terralib.newtarget {
Triple = config.tgttrip;
CPU = config.tgtcpu;
FloatABIHard = config.tgthf;
} or nil
if bflag('quiet','q') then holler = function() end end
|
| |
602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 |
print(util.dump(config))
os.exit(0)
end
local holler = print
local suffix = config.exe and '' or ('.'..config.outform)
local out = 'parsavd' .. suffix
local linkargs = {}
local target = config.tgttrip and terralib.newtarget {
Triple = config.tgttrip;
CPU = config.tgtcpu;
FloatABIHard = config.tgthf;
} or nil
if bflag('quiet','q') then holler = function() end end
|
Modified render/conf.t from [1b75d5dd6d] to [fa178f73b5].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
-- vim: ft=terra
local pstr = lib.mem.ptr(int8)
local pref = lib.mem.ref(int8)
local mappings = {
{url = 'profile', title = 'account profile', render = 'profile'};
{url = 'avi', title = 'avatar', render = 'avatar'};
{url = 'sec', title = 'security', render = 'sec'};
{url = 'rel', title = 'relationships', render = 'rel'};
{url = 'qnt', title = 'quarantine', render = 'quarantine'};
{url = 'acl', title = 'access control shortcuts', render = 'acl'};
{url = 'rooms', title = 'chatrooms', render = 'rooms'};
{url = 'circles', title = 'circles', render = 'circles'};
|
> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
-- vim: ft=terra
local pstr = lib.mem.ptr(int8)
local pref = lib.mem.ref(int8)
local mappings = {
{url = 'profile', title = 'account profile', render = 'profile'};
{url = 'avi', title = 'avatar', render = 'avatar'};
{url = 'ui', title = 'user interface', render = 'ui'};
{url = 'sec', title = 'security', render = 'sec'};
{url = 'rel', title = 'relationships', render = 'rel'};
{url = 'qnt', title = 'quarantine', render = 'quarantine'};
{url = 'acl', title = 'access control shortcuts', render = 'acl'};
{url = 'rooms', title = 'chatrooms', render = 'rooms'};
{url = 'circles', title = 'circles', render = 'circles'};
|
Modified render/conf/profile.t from [7f970c2f4a] to [864a63a85e].
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
local terra cs(s: rawstring)
return pstr { ptr = s, ct = lib.str.sz(s) }
end
local terra
render_conf_profile(co: &lib.srv.convo, path: lib.mem.ptr(pref)): pstr
var c = data.view.conf_profile {
handle = cs(co.who.handle);
nym = cs(lib.coalesce(co.who.nym,''));
bio = cs(lib.coalesce(co.who.bio,''));
}
return c:tostr()
end
return render_conf_profile
|
> > |
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
local terra cs(s: rawstring)
return pstr { ptr = s, ct = lib.str.sz(s) }
end
local terra
render_conf_profile(co: &lib.srv.convo, path: lib.mem.ptr(pref)): pstr
var hue: int8[21]
var c = data.view.conf_profile {
handle = cs(co.who.handle);
nym = cs(lib.coalesce(co.who.nym,''));
bio = cs(lib.coalesce(co.who.bio,''));
hue = lib.math.decstr(co.ui_hue, &hue[20]);
}
return c:tostr()
end
return render_conf_profile
|
Modified render/timeline.t from [2afba48373] to [ab5808172b].
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
to_idx = 64;
})
elseif mode == modes.fediglobal then
elseif mode == modes.circle then
end
var acc: lib.str.acc acc:init(1024)
for i = 0, posts.sz do
lib.render.tweet(co, posts(i).ptr, &acc)
posts(i):free()
end
posts:free()
var doc = [lib.srv.convo.page] {
title = lib.str.plit'timeline';
body = acc:finalize();
class = lib.str.plit'timeline';
cache = false;
}
co:stdpage(doc)
doc.body:free()
end
return render_timeline
|
> > > > > | |
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
to_idx = 64; }) elseif mode == modes.fediglobal then elseif mode == modes.circle then end var acc: lib.str.acc acc:init(1024) acc:lpush('<div id="tl" data-live="10">') var newest: lib.store.timepoint = 0 for i = 0, posts.sz do lib.render.tweet(co, posts(i).ptr, &acc) var t = lib.math.biggest(lib.math.biggest(posts(i).ptr.posted, posts(i).ptr.discovered),posts(i).ptr.edited) if t > newest then newest = t end posts(i):free() end posts:free() acc:lpush('</div>') var doc = [lib.srv.convo.page] { title = lib.str.plit'timeline'; body = acc:finalize(); class = lib.str.plit'timeline'; cache = false; } co:livepage(doc,newest) doc.body:free() end return render_timeline |
Modified render/tweet-page.t from [ad592d16bd] to [005ba03599].
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
..
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
-- vim: ft=terra
local pstr = lib.mem.ptr(int8)
local pref = lib.mem.ref(int8)
local terra cs(s: rawstring)
return pstr { ptr = s, ct = lib.str.sz(s) }
end
local terra
render_tweet_page(
co: &lib.srv.convo,
path: lib.mem.ptr(pref),
p: &lib.store.post
): {}
var pg: lib.str.acc pg:init(256)
lib.render.tweet(co, p, &pg)
pg:lpush('<form class="action-bar" method="post">')
if co.aid ~= 0 then
var liked = false -- FIXME
var rtd = false
if not liked
then pg:lpush('<button class="pos" name="act" value="like">like</button>')
else pg:lpush('<button class="neg" name="act" value="dislike">dislike</button>')
end
if not rtd
................................................................................
end
if p.author == co.who.id then
pg:lpush('<a class="button" href="/post/'):rpush(path(1)):lpush('/edit">edit</a><a class="neg button" href="/post/'):rpush(path(1)):lpush('/del">delete</a>')
end
-- TODO list user's chosen reaction emoji
pg:lpush('</form>')
if co.who.rights.powers.post() then
lib.render.compose(co, nil, &pg)
end
end
var ppg = pg:finalize() defer ppg:free()
co:stdpage([lib.srv.convo.page] {
title = lib.str.plit 'post'; cache = false;
class = lib.str.plit 'post'; body = ppg;
})
-- TODO display conversation
-- perhaps display descendant nodes here, and have a link to the top of the whole tree?
end
return render_tweet_page
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
<
>
>
>
>
>
>
|
|
<
|
|
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
..
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
-- vim: ft=terra
local pstr = lib.mem.ptr(int8)
local pref = lib.mem.ref(int8)
local terra cs(s: rawstring)
return pstr { ptr = s, ct = lib.str.sz(s) }
end
local terra
render_tweet_replies(
co: &lib.srv.convo,
acc: &lib.str.acc,
id: uint64
): {}
var replies = co.srv:post_enum_parent(id)
if replies.ct == 0 then return end
acc:lpush('<div class="thread">')
for i=0, replies.ct do
var post = replies(i).ptr
lib.render.tweet(co, post, acc)
render_tweet_replies(co, acc, post.id)
end
acc:lpush('</div>')
end
local terra
render_tweet_page(
co: &lib.srv.convo,
path: lib.mem.ptr(pref),
p: &lib.store.post
): {}
var livetime = co.srv:thread_latest_arrival_calc(p.id)
var pg: lib.str.acc pg:init(256)
lib.render.tweet(co, p, &pg)
if co.aid ~= 0 then
pg:lpush('<form class="action-bar" method="post">')
var liked = false -- FIXME
var rtd = false
if not liked
then pg:lpush('<button class="pos" name="act" value="like">like</button>')
else pg:lpush('<button class="neg" name="act" value="dislike">dislike</button>')
end
if not rtd
................................................................................
end
if p.author == co.who.id then
pg:lpush('<a class="button" href="/post/'):rpush(path(1)):lpush('/edit">edit</a><a class="neg button" href="/post/'):rpush(path(1)):lpush('/del">delete</a>')
end
-- TODO list user's chosen reaction emoji
pg:lpush('</form>')
end
pg:lpush('<div id="convo" data-live="10">')
render_tweet_replies(co, &pg, p.id)
pg:lpush('</div>')
if co.aid ~= 0 and co.who.rights.powers.post() then
lib.render.compose(co, nil, &pg)
end
var ppg = pg:finalize() defer ppg:free()
co:livepage([lib.srv.convo.page] {
title = lib.str.plit 'post'; cache = false;
class = lib.str.plit 'post'; body = ppg;
}, livetime)
-- TODO display conversation
-- perhaps display descendant nodes here, and have a link to the top of the whole tree?
end
return render_tweet_page
|
Modified render/tweet.t from [ac0f8e680f] to [77ab77b2db].
32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
subject = cs(lib.coalesce(p.subject,''));
nym = fullname;
when = cs(×tr[0]);
avatar = cs(lib.trn(author.origin == 0, avistr.buf,
lib.coalesce(author.avatar, '/s/default-avatar.webp')));
acctlink = cs(author.xid);
permalink = permalink:finalize();
}
defer tpl.permalink:free()
if acc ~= nil then tpl:append(acc) return [lib.mem.ptr(int8)]{ptr=nil,ct=0} end
var txt = tpl:tostr()
return txt
end
return render_tweet
|
> > > > > > > > > > > > > |
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
subject = cs(lib.coalesce(p.subject,'')); nym = fullname; when = cs(×tr[0]); avatar = cs(lib.trn(author.origin == 0, avistr.buf, lib.coalesce(author.avatar, '/s/default-avatar.webp'))); acctlink = cs(author.xid); permalink = permalink:finalize(); attr = '' } var attrbuf: int8[32] if p.accent ~= -1 and p.accent ~= co.ui_hue then var hdecbuf: int8[21] var hdec = lib.math.decstr(p.accent, &hdecbuf[20]) lib.str.cpy(&attrbuf[0], ' style="--hue:') lib.str.cpy(&attrbuf[14], hdec) var len = &hdecbuf[20] - hdec lib.str.cpy(&attrbuf[14] + len, '"') tpl.attr = &attrbuf[0] end defer tpl.permalink:free() if acc ~= nil then tpl:append(acc) return [lib.mem.ptr(int8)]{ptr=nil,ct=0} end var txt = tpl:tostr() return txt end return render_tweet |
Modified render/user-page.t from [8e478d7a95] to [08cdf2fd9f].
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
var stoptime = lib.osclock.time(nil)
var posts = co.srv:post_enum_author_uid(actor.id, lib.store.range {
mode = 1; -- T->I
from_time = stoptime;
to_idx = 64;
})
for i = 0, posts.sz do
lib.render.tweet(co, posts(i).ptr, &acc)
posts(i):free()
end
posts:free()
var bdf = acc:finalize()
co:stdpage([lib.srv.convo.page] {
title = tiptr; body = bdf;
class = lib.str.plit 'profile';
cache = false;
})
tiptr:free()
bdf:free()
end
return render_userpage
|
> > > > > | | |
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
var stoptime = lib.osclock.time(nil)
var posts = co.srv:post_enum_author_uid(actor.id, lib.store.range {
mode = 1; -- T->I
from_time = stoptime;
to_idx = 64;
})
acc:lpush('<div id="feed" data-live="10">')
var newest: lib.store.timepoint = 0
for i = 0, posts.sz do
lib.render.tweet(co, posts(i).ptr, &acc)
var t = lib.math.biggest(lib.math.biggest(posts(i).ptr.posted, posts(i).ptr.discovered),posts(i).ptr.edited)
if t > newest then newest = t end
posts(i):free()
end
posts:free()
acc:lpush('</div>')
var bdf = acc:finalize()
co:livepage([lib.srv.convo.page] {
title = tiptr; body = bdf;
class = lib.str.plit 'profile';
cache = false;
}, newest)
tiptr:free()
bdf:free()
end
return render_userpage
|
Modified route.t from [a9eb70a00e] to [f59188addc].
2 3 4 5 6 7 8 9 10 11 12 13 14 15 .. 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 ... 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 ... 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 ... 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 ... 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 ... 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 ... 240 241 242 243 244 245 246 247 248 249 250 251 252 253 ... 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 |
local r = lib.srv.route
local method = lib.http.method
local pstring = lib.mem.ptr(int8)
local rstring = lib.mem.ref(int8)
local hpath = lib.mem.ptr(rstring)
local http = {}
terra http.actor_profile_xid(co: &lib.srv.convo, uri: lib.mem.ptr(int8), meth: method.t)
var handle = [lib.mem.ptr(int8)] { ptr = &uri.ptr[2], ct = 0 }
for i=2,uri.ct do
if uri.ptr[i] == @'/' or uri.ptr[i] == 0 then handle.ct = i - 2 break end
end
if handle.ct == 0 then
handle.ct = uri.ct - 2
................................................................................
end
defer actor:free()
lib.render.user_page(co, actor.ptr)
end
terra http.login_form(co: &lib.srv.convo, meth: method.t)
if meth == method.get then
-- request a username
lib.render.login(co, nil, nil, lib.str.plit(nil))
elseif meth == method.post then
var usn, usnl = co:postv('user')
var am, aml = co:postv('authmethod')
var chrs, chrsl = co:postv('response')
var cs, authok = co.srv:actor_auth_how(co.peer, usn)
................................................................................
end
terra http.post_compose(co: &lib.srv.convo, meth: method.t)
if not co:assertpow('post') then return end
--if co.who.rights.powers.post() == false then
--co:complain(403,'insufficient privileges','you lack the <strong>post</strong> power and cannot perform this action')
if meth == method.get then
lib.render.compose(co, nil, nil)
elseif meth == method.post then
var text, textlen = co:postv("post")
var acl, acllen = co:postv("acl")
var subj, subjlen = co:postv("subject")
if text == nil or acl == nil then
co:complain(405, 'invalid post', 'every post must have at least body text and an ACL')
................................................................................
end
if subj == nil then subj = '' end
var p = lib.store.post {
author = co.who.id, acl = acl;
body = text, subject = subj;
}
var newid = co.srv:post_create(&p)
var idbuf: int8[lib.math.shorthand.maxlen]
var idlen = lib.math.shorthand.gen(newid, idbuf)
var redirto: lib.str.acc redirto:compose('/post/',{idbuf,idlen}) defer redirto:free()
co:reroute(redirto.buf)
end
end
................................................................................
if path.ct == 3 then
var lnk: lib.str.acc lnk:compose('/post/', path(1))
var lnkp = lnk:finalize() defer lnkp:free()
if post(0).author ~= co.who.id then
co:complain(403, 'forbidden', 'you cannot alter other people\'s posts')
return
elseif path(2):cmp(lib.str.lit 'edit') then
if meth == method.get then
lib.render.compose(co, post.ptr, nil)
return
elseif meth == method.post then
var newbody = co:postv('post')._0
var newacl = co:postv('acl')._0
var newsubj = co:postv('subject')._0
if newbody ~= nil then post(0).body = newbody end
................................................................................
if newacl ~= nil then post(0).acl = newacl end
if newsubj ~= nil then post(0).subject = newsubj end
post(0):save(true)
co:reroute(lnkp.ptr)
end
return
elseif path(2):cmp(lib.str.lit 'del') then
if meth == method.get then
var conf = data.view.confirm {
title = lib.str.plit 'delete post';
query = lib.str.plit 'are you sure you want to delete this post?';
cancel = lnkp
}
var body = conf:tostr() defer body:free()
co:stdpage([lib.srv.convo.page] {
................................................................................
co:reroute('/') -- TODO maybe return to parent or conversation if possible
return
else goto badop end
end
else goto badurl end
end
if meth == method.post then goto badop end
lib.render.tweet_page(co, path, post.ptr)
do return end
::badurl:: do co:complain(404, 'invalid URL', 'this URL does not reference extant content or functionality') return end
::badop :: do co:complain(405, 'invalid operation', 'the operation you have attempted on this post is not meaningful') return end
end
................................................................................
if path(1):cmp(lib.str.lit 'profile') then
lib.dbg('updating profile')
co.who.bio = co:postv('bio')._0
co.who.nym = co:postv('nym')._0
if co.who.bio ~= nil and @co.who.bio == 0 then co.who.bio = nil end
if co.who.nym ~= nil and @co.who.nym == 0 then co.who.nym = nil end
co.who.source:actor_save(co.who)
msg = lib.str.plit 'profile changes saved'
--user_refresh = true -- not really necessary here, actually
elseif path(1):cmp(lib.str.lit 'srv') then
if not co.who.rights.powers.config() then goto nopriv end
elseif path(1):cmp(lib.str.lit 'brand') then
if not co.who.rights.powers.rebrand() then goto nopriv end
elseif path(1):cmp(lib.str.lit 'users') then
................................................................................
-- entry points
terra r.dispatch_http(co: &lib.srv.convo, uri: lib.mem.ptr(int8), meth: method.t)
lib.dbg('handling URI of form ', {uri.ptr,uri.ct})
co.navbar = lib.render.nav(co)
-- some routes are non-hierarchical, and can be resolved with a simple strcmp
-- we run through those first before giving up and parsing the URI
if uri.ptr[0] ~= @'/' then
co:complain(404, 'what the hell', 'how did you do that')
return
elseif uri.ct == 1 then -- root
if (co.srv.cfg.pol_sec == lib.srv.secmode.private or
co.srv.cfg.pol_sec == lib.srv.secmode.lockdown) and co.aid == 0 then
http.login_form(co, meth)
else
-- FIXME display home screen
http.timeline(co, hpath {ptr=nil})
goto notfound
end
return
elseif uri.ptr[1] == @'@' then
http.actor_profile_xid(co, uri, meth)
return
elseif uri.ptr[1] == @'s' and uri.ptr[2] == @'/' and uri.ct > 3 then
if meth ~= method.get then goto wrongmeth end
if not http.static_content(co, uri.ptr + 3, uri.ct - 3) then goto notfound end
return
elseif lib.str.ncmp('/avi/', uri.ptr, 5) == 0 then
http.local_avatar(co, [lib.mem.ptr(int8)] {ptr = uri.ptr + 5, ct = uri.ct - 5})
return
elseif lib.str.ncmp('/compose', uri.ptr, lib.math.biggest(uri.ct,8)) == 0 then
if co.aid == 0 then co:reroute('/login') return end
http.post_compose(co,meth)
return
elseif lib.str.ncmp('/login', uri.ptr, lib.math.biggest(uri.ct,6)) == 0 then
if co.aid == 0
then http.login_form(co, meth)
else co:reroute('/')
end
return
elseif lib.str.ncmp('/logout', uri.ptr, lib.math.biggest(uri.ct,7)) == 0 then
if co.aid == 0
then goto notfound
else co:reroute_cookie('/','auth=; Path=/')
end
return
else -- hierarchical routes
var path = lib.http.hier(uri) defer path:free()
if path.ct > 1 and path(0):cmp(lib.str.lit('user')) then
http.actor_profile_uid(co, path, meth)
elseif path.ct > 1 and path(0):cmp(lib.str.lit('post')) then
http.tweet_page(co, path, meth)
elseif path(0):cmp(lib.str.lit('tl')) then
http.timeline(co, path)
elseif path(0):cmp(lib.str.lit('doc')) then
if meth ~= method.get and meth ~= method.head then goto wrongmeth end
http.documentation(co, path)
elseif path(0):cmp(lib.str.lit('conf')) then
if co.aid == 0 then goto unauth end
http.configure(co,path,meth)
else goto notfound end
return
end
::wrongmeth:: co:complain(405, 'method not allowed', 'that method is not meaningful for this endpoint') do return end
::notfound:: co:complain(404, 'not found', 'no such resource available') do return end
::unauth:: co:complain(401, 'unauthorized', 'this content is not available at your clearance level') do return end
end
|
> | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < | < < < < < > < < < < < | < > |
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 .. 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 ... 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 ... 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 ... 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 ... 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 ... 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 ... 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 ... 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 |
local r = lib.srv.route
local method = lib.http.method
local pstring = lib.mem.ptr(int8)
local rstring = lib.mem.ref(int8)
local hpath = lib.mem.ptr(rstring)
local http = {}
terra meth_get(meth: method.t) return (meth == method.get) or (meth == method.head) end
terra http.actor_profile_xid(co: &lib.srv.convo, uri: lib.mem.ptr(int8), meth: method.t)
var handle = [lib.mem.ptr(int8)] { ptr = &uri.ptr[2], ct = 0 }
for i=2,uri.ct do
if uri.ptr[i] == @'/' or uri.ptr[i] == 0 then handle.ct = i - 2 break end
end
if handle.ct == 0 then
handle.ct = uri.ct - 2
................................................................................
end
defer actor:free()
lib.render.user_page(co, actor.ptr)
end
terra http.login_form(co: &lib.srv.convo, meth: method.t)
if meth_get(meth) then
-- request a username
lib.render.login(co, nil, nil, lib.str.plit(nil))
elseif meth == method.post then
var usn, usnl = co:postv('user')
var am, aml = co:postv('authmethod')
var chrs, chrsl = co:postv('response')
var cs, authok = co.srv:actor_auth_how(co.peer, usn)
................................................................................
end
terra http.post_compose(co: &lib.srv.convo, meth: method.t)
if not co:assertpow('post') then return end
--if co.who.rights.powers.post() == false then
--co:complain(403,'insufficient privileges','you lack the <strong>post</strong> power and cannot perform this action')
if meth_get(meth) then
lib.render.compose(co, nil, nil)
elseif meth == method.post then
var text, textlen = co:postv("post")
var acl, acllen = co:postv("acl")
var subj, subjlen = co:postv("subject")
if text == nil or acl == nil then
co:complain(405, 'invalid post', 'every post must have at least body text and an ACL')
................................................................................
end
if subj == nil then subj = '' end
var p = lib.store.post {
author = co.who.id, acl = acl;
body = text, subject = subj;
}
var newid = p:publish(co.srv)
var idbuf: int8[lib.math.shorthand.maxlen]
var idlen = lib.math.shorthand.gen(newid, idbuf)
var redirto: lib.str.acc redirto:compose('/post/',{idbuf,idlen}) defer redirto:free()
co:reroute(redirto.buf)
end
end
................................................................................
if path.ct == 3 then
var lnk: lib.str.acc lnk:compose('/post/', path(1))
var lnkp = lnk:finalize() defer lnkp:free()
if post(0).author ~= co.who.id then
co:complain(403, 'forbidden', 'you cannot alter other people\'s posts')
return
elseif path(2):cmp(lib.str.lit 'edit') then
if meth_get(meth) then
lib.render.compose(co, post.ptr, nil)
return
elseif meth == method.post then
var newbody = co:postv('post')._0
var newacl = co:postv('acl')._0
var newsubj = co:postv('subject')._0
if newbody ~= nil then post(0).body = newbody end
................................................................................
if newacl ~= nil then post(0).acl = newacl end
if newsubj ~= nil then post(0).subject = newsubj end
post(0):save(true)
co:reroute(lnkp.ptr)
end
return
elseif path(2):cmp(lib.str.lit 'del') then
if meth_get(meth) then
var conf = data.view.confirm {
title = lib.str.plit 'delete post';
query = lib.str.plit 'are you sure you want to delete this post?';
cancel = lnkp
}
var body = conf:tostr() defer body:free()
co:stdpage([lib.srv.convo.page] {
................................................................................
co:reroute('/') -- TODO maybe return to parent or conversation if possible
return
else goto badop end
end
else goto badurl end
end
if meth == method.post then
var replytext = co:ppostv('post')
var acl = co:ppostv('acl')
var subj = co:ppostv('subject')
if not acl then acl = lib.str.plit 'all' end
if not replytext then goto badop end
var reply = lib.store.post {
author = co.who.id, parent = pid;
subject = subj.ptr, acl = acl.ptr, body = replytext.ptr;
}
reply:publish(co.srv)
end
lib.render.tweet_page(co, path, post.ptr)
do return end
::badurl:: do co:complain(404, 'invalid URL', 'this URL does not reference extant content or functionality') return end
::badop :: do co:complain(405, 'invalid operation', 'the operation you have attempted on this post is not meaningful') return end
end
................................................................................
if path(1):cmp(lib.str.lit 'profile') then
lib.dbg('updating profile')
co.who.bio = co:postv('bio')._0
co.who.nym = co:postv('nym')._0
if co.who.bio ~= nil and @co.who.bio == 0 then co.who.bio = nil end
if co.who.nym ~= nil and @co.who.nym == 0 then co.who.nym = nil end
co.who.source:actor_save(co.who)
var act = co:ppostv('act')
var resethue = false
if act:ref() then
resethue = act:cmp(lib.str.plit 'reset-hue')
end
if not resethue then
var shue = co:ppostv('hue')
var nhue, okhue = lib.math.decparse(shue)
if okhue and nhue ~= co.ui_hue then
if nhue == co.srv.cfg.ui_hue
then resethue = true
else co.srv:actor_conf_int_set(co.who.id, 'ui-accent', nhue)
end
co.ui_hue = nhue
end
end
if resethue then
co.srv:actor_conf_int_reset(co.who.id, 'ui-accent')
co.ui_hue = co.srv.cfg.ui_hue
end
msg = lib.str.plit 'profile changes saved'
--user_refresh = true -- not really necessary here, actually
elseif path(1):cmp(lib.str.lit 'srv') then
if not co.who.rights.powers.config() then goto nopriv end
elseif path(1):cmp(lib.str.lit 'brand') then
if not co.who.rights.powers.rebrand() then goto nopriv end
elseif path(1):cmp(lib.str.lit 'users') then
................................................................................
-- entry points
terra r.dispatch_http(co: &lib.srv.convo, uri: lib.mem.ptr(int8), meth: method.t)
lib.dbg('handling URI of form ', {uri.ptr,uri.ct})
co.navbar = lib.render.nav(co)
-- some routes are non-hierarchical, and can be resolved with a simple strcmp
-- we run through those first before giving up and parsing the URI
if uri.ptr == nil or uri.ptr[0] ~= @'/' then
co:complain(404, 'what the hell', 'how did you do that')
elseif uri.ct == 1 then -- root
if (co.srv.cfg.pol_sec == lib.srv.secmode.private or
co.srv.cfg.pol_sec == lib.srv.secmode.lockdown) and co.aid == 0 then
http.login_form(co, meth)
else http.timeline(co, hpath {ptr=nil}) end
elseif uri.ptr[1] == @'@' then
http.actor_profile_xid(co, uri, meth)
elseif uri.ptr[1] == @'s' and uri.ptr[2] == @'/' and uri.ct > 3 then
if not meth_get(meth) then goto wrongmeth end
if not http.static_content(co, uri.ptr + 3, uri.ct - 3) then goto notfound end
elseif lib.str.ncmp('/avi/', uri.ptr, 5) == 0 then
http.local_avatar(co, [lib.mem.ptr(int8)] {ptr = uri.ptr + 5, ct = uri.ct - 5})
elseif lib.str.ncmp('/compose', uri.ptr, lib.math.biggest(uri.ct,8)) == 0 then
if co.aid == 0 then co:reroute('/login') return end
http.post_compose(co,meth)
elseif lib.str.ncmp('/login', uri.ptr, lib.math.biggest(uri.ct,6)) == 0 then
if co.aid == 0
then http.login_form(co, meth)
else co:reroute('/')
end
elseif lib.str.ncmp('/logout', uri.ptr, lib.math.biggest(uri.ct,7)) == 0 then
if co.aid == 0
then goto notfound
else co:reroute_cookie('/','auth=; Path=/')
end
else -- hierarchical routes
var path = lib.http.hier(uri) defer path:free()
if path.ct > 1 and path(0):cmp(lib.str.lit('user')) then
http.actor_profile_uid(co, path, meth)
elseif path.ct > 1 and path(0):cmp(lib.str.lit('post')) then
http.tweet_page(co, path, meth)
elseif path(0):cmp(lib.str.lit('tl')) then
http.timeline(co, path)
elseif path(0):cmp(lib.str.lit('doc')) then
if not meth_get(meth) then goto wrongmeth end
http.documentation(co, path)
elseif path(0):cmp(lib.str.lit('conf')) then
if co.aid == 0 then goto unauth end
http.configure(co,path,meth)
else goto notfound end
end
do return end
::wrongmeth:: co:complain(405, 'method not allowed', 'that method is not meaningful for this endpoint') do return end
::notfound:: co:complain(404, 'not found', 'no such resource available') do return end
::unauth:: co:complain(401, 'unauthorized', 'this content is not available at your clearance level') do return end
end
|
Modified srv.t from [9e0d1b7489] to [675eda18a7].
7 8 9 10 11 12 13 14 15 16 17 18 19 20 ... 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 ... 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 ... 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 ... 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 ... 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 ... 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 ... 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 ... 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 ... 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 ... 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 |
secret: lib.mem.ptr(int8)
pol_sec: secmode.t
pol_reg: bool
credmgd: bool
maxupsz: intptr
instance: lib.mem.ptr(int8)
overlord: &srv
}
local struct srv {
sources: lib.mem.ptr(lib.store.source)
webmgr: lib.net.mg_mgr
webcon: &lib.net.mg_connection
cfg: cfgcache
id: rawstring
................................................................................
if [ok] then break
else r = empty end
end
end
in r end
end
end)
local struct convo {
srv: &srv
con: &lib.net.mg_connection
msg: &lib.net.mg_http_message
aid: uint64 -- 0 if logged out
aid_issue: lib.store.timepoint
who: &lib.store.actor -- who we're logged in as, if aid ~= 0
peer: lib.store.inet
reqtype: lib.http.mime.t -- negotiated content type
-- cache
navbar: lib.mem.ptr(int8)
actorcache: lib.mem.cache(lib.mem.ptr(lib.store.actor),32) -- naive cache to avoid unnecessary queries
-- private
varbuf: lib.mem.ptr(int8)
vbofs: &int8
}
-- this is unfortunately necessary to work around a terra bug
-- it can't seem to handle forward-declarations of structs in C
local getpeer
do local struct strucheader {
next: &lib.net.mg_connection
................................................................................
peer: lib.net.mg_addr
}
terra getpeer(con: &lib.net.mg_connection)
return [&strucheader](con).peer
end
end
terra convo:reroute_cookie(dest: rawstring, cookie: rawstring)
var hdrs = array(
lib.http.header { key = 'Content-Type', value = 'text/html; charset=UTF-8' },
lib.http.header { key = 'Location', value = dest },
lib.http.header { key = 'Set-Cookie', value = cookie }
)
var body = data.view.docskel {
instance = self.srv.cfg.instance.ptr;
title = 'rerouting';
body = 'you are being redirected';
class = 'error';
navlinks = '';
}
body:send(self.con, 303, [lib.mem.ptr(lib.http.header)] {
ptr = &hdrs[0], ct = [hdrs.type.N] - lib.trn(cookie == nil,1,0)
})
end
................................................................................
lib.dbg('sending cookie ',{&sesskey[0],15})
p = lib.str.ncpy(p, '; Path=/', 9)
end
self:reroute_cookie(dest, &sesskey[0])
end
terra convo:complain(code: uint16, title: rawstring, msg: rawstring)
var hdrs = array(
lib.http.header { key = 'Content-Type', value = 'text/html; charset=UTF-8' },
lib.http.header { key = 'Cache-Control', value = 'no-store' }
)
var ti: lib.str.acc ti:compose('error :: ', title)
var bo: lib.str.acc bo:compose('<div class="message"><img class="icon" src="/s/warn.webp"><h1>',title,'</h1><p>',msg,'</p></div>')
var body = data.view.docskel {
instance = self.srv.cfg.instance;
title = ti:finalize();
body = bo:finalize();
class = lib.str.plit 'error';
navlinks = lib.coalesce(self.navbar, [lib.mem.ptr(int8)]{ptr='',ct=0});
}
if body.body.ptr == nil then
body.body = lib.str.plit"i'm sorry, dave. i can't let you do that"
end
body:send(self.con, code, [lib.mem.ptr(lib.http.header)] {
ptr = &hdrs[0], ct = [hdrs.type.N]
})
body.title:free()
body.body:free()
end
convo.methods.assertpow = macro(function(self, pow)
return quote
................................................................................
if self.aid == 0 or self.who.rights.powers.[pow:asvalue()]() == false then
ok = false
self:complain(403,'insufficient privileges',['you lack the <strong>'..pow:asvalue()..'</strong> power and cannot perform this action'])
end
in ok end
end)
struct convo.page {
title: pstring
body: pstring
class: pstring
cache: bool
}
terra convo:stdpage(pg: convo.page)
var doc = data.view.docskel {
instance = self.srv.cfg.instance;
title = pg.title;
body = pg.body;
class = pg.class;
navlinks = self.navbar;
}
var hdrs = array(
lib.http.header { key = 'Content-Type', value = 'text/html; charset=UTF-8' },
lib.http.header { key = 'Cache-Control', value = 'no-store' }
)
doc:send(self.con,200,[lib.mem.ptr(lib.http.header)] {ct = [hdrs.type.N] - lib.trn(pg.cache,1,0), ptr = &hdrs[0]})
end
-- CALL ONLY ONCE PER VAR
terra convo:postv(name: rawstring)
if self.varbuf.ptr == nil then
self.varbuf = lib.mem.heapa(int8, self.msg.body.len + self.msg.query.len)
self.vbofs = self.varbuf.ptr
end
var o = lib.net.mg_http_get_var(&self.msg.body, name, self.vbofs, self.varbuf.ct - (self.vbofs - self.varbuf.ptr))
................................................................................
if lib.str.ncmp(mimevar.ptr, mime, lib.math.biggest(mimevar.ct, [#mime])) == 0 then
ret = [lib.http.mime[name]]
else ret = [mimeneg] end
in ret end
end
local handle = {
http = terra(con: &lib.net.mg_connection, event: int, p: &opaque, ext: &opaque)
var server = [&srv](ext)
var mgpeer = getpeer(con)
var peer = lib.store.inet { port = mgpeer.port; }
if mgpeer.is_ip6 then peer.pv = 6 else peer.pv = 4 end
if peer.pv == 6 then
for i = 0, 16 do peer.v6[i] = mgpeer.ip6[i] end
else -- v4
@[&uint32](&peer.v4) = mgpeer.ip
................................................................................
-- for now i'm leaving it as is, but note that netmask restrictions
-- WILL NOT WORK until upstream gets its shit together. FIXME
-- needs to check for an X-Forwarded-For header from nginx and
-- use that instead of the peer iff peer is ::1/127.1 FIXME
-- maybe also haproxy support?
switch event do
case lib.net.MG_EV_HTTP_MSG then
lib.dbg('routing HTTP request')
var msg = [&lib.net.mg_http_message](p)
var co = convo {
con = con, srv = server, msg = msg;
aid = 0, aid_issue = 0, who = nil;
reqtype = lib.http.mime.none;
peer = peer;
} co.varbuf.ptr = nil
co.navbar.ptr = nil
co.actorcache.top = 0
co.actorcache.cur = 0
-- first, check for an accept header. if it's there, we need to
-- iterate over the values and pick the highest-priority one
do var acc = lib.http.findheader(msg, 'Accept')
-- TODO handle q-value
if acc.ptr ~= nil then
var [mimevar] = [lib.mem.ref(int8)] { ptr = acc.ptr }
var i = 0 while i < acc.ct do
if acc.ptr[i] == @',' or acc.ptr[i] == @';' then
mimevar.ct = (acc.ptr+i) - mimevar.ptr
var t = [mimeneg]
if t ~= lib.http.mime.none then
co.reqtype = t
................................................................................
else co.reqtype = lib.http.mime.html end
::foundtype::end
-- we need to check if there's any cookies sent with the request,
-- and if so, whether they contain any credentials. this will be
-- used to set the auth parameters in the http conversation
var cookies_p = lib.http.findheader(msg, 'Cookie')
if cookies_p ~= nil then
var cookies = cookies_p.ptr
var key = [lib.mem.ref(int8)] {ptr = cookies, ct = 0}
var val = [lib.mem.ref(int8)] {ptr = nil, ct = 0}
var i = 0 while i < cookies_p.ct and
cookies[i] ~= 0 and
cookies[i] ~= @'\r' and
cookies[i] ~= @'\n' do -- cover all the bases
................................................................................
end
if co.aid ~= 0 then
var sess, usr = co.srv:actor_session_fetch(co.aid, peer, co.aid_issue)
if sess.ok == false then co.aid = 0 co.aid_issue = 0 else
co.who = usr.ptr
co.who.rights.powers = server:actor_powers_fetch(co.who.id)
end
end
var uridec = lib.mem.heapa(int8, msg.uri.len) defer uridec:free()
var urideclen = lib.net.mg_url_decode(msg.uri.ptr, msg.uri.len, uridec.ptr, uridec.ct, 1)
var uri = uridec
if urideclen == -1 then
for i = 0,msg.uri.len do
................................................................................
end
end
uri.ct = msg.uri.len
else uri.ct = urideclen end
lib.dbg('routing URI ', {uri.ptr, uri.ct})
if lib.str.ncmp('GET', msg.method.ptr, msg.method.len) == 0 then
route.dispatch_http(&co, uri, [lib.http.method.get])
elseif lib.str.ncmp('POST', msg.method.ptr, msg.method.len) == 0 then
route.dispatch_http(&co, uri, [lib.http.method.post])
elseif lib.str.ncmp('HEAD', msg.method.ptr, msg.method.len) == 0 then
route.dispatch_http(&co, uri, [lib.http.method.head])
elseif lib.str.ncmp('OPTIONS', msg.method.ptr, msg.method.len) == 0 then
route.dispatch_http(&co, uri, [lib.http.method.options])
else
co:complain(400,'unknown method','you have submitted an invalid http request')
end
if co.aid ~= 0 then lib.mem.heapf(co.who) end
if co.varbuf.ptr ~= nil then co.varbuf:free() end
................................................................................
do self.pol_reg = false
var sreg = self.overlord:conf_get('policy-self-register')
if sreg:ref() then
if lib.str.cmp(sreg.ptr, 'on') == 0
then self.pol_reg = true
else self.pol_reg = false
end
end
sreg:free() end
do self.credmgd = false
var sreg = self.overlord:conf_get('credential-store')
if sreg:ref() then
if lib.str.cmp(sreg.ptr, 'managed') == 0
then self.credmgd = true
else self.credmgd = false
end
end
sreg:free() end
do self.maxupsz = [1024 * 100] -- 100 kilobyte default
var sreg = self.overlord:conf_get('maximum-artifact-size')
if sreg:ref() then
var sz, ok = lib.math.fsz_parse(sreg)
if ok then self.maxupsz = sz else
lib.warn('invalid configuration value for maximum-artifact-size; keeping default 100K upload limit')
end
end
sreg:free() end
self.pol_sec = secmode.lockdown
var smode = self.overlord:conf_get('policy-security')
if smode.ptr ~= nil then
if lib.str.cmp(smode.ptr, 'public') == 0 then
self.pol_sec = secmode.public
elseif lib.str.cmp(smode.ptr, 'private') == 0 then
self.pol_sec = secmode.private
elseif lib.str.cmp(smode.ptr, 'lockdown') == 0 then
self.pol_sec = secmode.lockdown
elseif lib.str.cmp(smode.ptr, 'isolate') == 0 then
self.pol_sec = secmode.isolate
end
end
smode:free()
end
return {
overlord = srv;
convo = convo;
route = route;
secmode = secmode;
}
|
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < | < | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | | > | | > > > > > > > > > > > > > < | > < | > < | > > > > > > > > | > |
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ... 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 ... 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 ... 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 ... 282 283 284 285 286 287 288 289 290 291 292 293 294 295 ... 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 ... 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 ... 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 ... 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 ... 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 ... 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 |
secret: lib.mem.ptr(int8) pol_sec: secmode.t pol_reg: bool credmgd: bool maxupsz: intptr instance: lib.mem.ptr(int8) overlord: &srv ui_hue: uint16 } local struct srv { sources: lib.mem.ptr(lib.store.source) webmgr: lib.net.mg_mgr webcon: &lib.net.mg_connection cfg: cfgcache id: rawstring ................................................................................ if [ok] then break else r = empty end end end in r end end end) terra lib.store.post:publish(s: &srv) self:comp() self.posted = lib.osclock.time(nil) self.discovered = self.posted self.chgcount = 0 self.edited = 0 self.id = s:post_create(self) return self.id end local struct convo { srv: &srv con: &lib.net.mg_connection msg: &lib.net.mg_http_message aid: uint64 -- 0 if logged out aid_issue: lib.store.timepoint who: &lib.store.actor -- who we're logged in as, if aid ~= 0 peer: lib.store.inet reqtype: lib.http.mime.t -- negotiated content type method: lib.http.method.t live_last: lib.store.timepoint -- cache ui_hue: uint16 navbar: lib.mem.ptr(int8) actorcache: lib.mem.cache(lib.mem.ptr(lib.store.actor),32) -- naive cache to avoid unnecessary queries -- private varbuf: lib.mem.ptr(int8) vbofs: &int8 } struct convo.page { title: pstring body: pstring class: pstring cache: bool } -- this is unfortunately necessary to work around a terra bug -- it can't seem to handle forward-declarations of structs in C local getpeer do local struct strucheader { next: &lib.net.mg_connection ................................................................................ peer: lib.net.mg_addr } terra getpeer(con: &lib.net.mg_connection) return [&strucheader](con).peer end end terra convo:rawpage(code: uint16, pg: convo.page, hdrs: lib.mem.ptr(lib.http.header)) var doc = data.view.docskel { instance = self.srv.cfg.instance; title = pg.title; body = pg.body; class = pg.class; navlinks = self.navbar; attr = ''; } var attrbuf: int8[32] if self.aid ~= 0 and self.ui_hue ~= 323 then var hdecbuf: int8[21] var hdec = lib.math.decstr(self.ui_hue, &hdecbuf[20]) lib.str.cpy(&attrbuf[0], ' style="--hue:') lib.str.cpy(&attrbuf[14], hdec) var len = &hdecbuf[20] - hdec lib.str.cpy(&attrbuf[14] + len, '"') doc.attr = &attrbuf[0] end if self.method == [lib.http.method.head] then doc:head(self.con,code,hdrs) else doc:send(self.con,code,hdrs) end end terra convo:statpage(code: uint16, pg: convo.page) var hdrs = array( lib.http.header { key = 'Content-Type', value = 'text/html; charset=UTF-8' }, lib.http.header { key = 'Cache-Control', value = 'no-store' } ) self:rawpage(code,pg, [lib.mem.ptr(lib.http.header)] { ptr = &hdrs[0]; ct = [hdrs.type.N] - lib.trn(pg.cache,1,0); }) end terra convo:livepage(pg: convo.page, lastup: lib.store.timepoint) var nbuf: int8[21] var hdrs = array( lib.http.header { key = 'Content-Type', value = 'text/html; charset=UTF-8' }, lib.http.header { key = 'Cache-Control', value = 'no-store' }, lib.http.header { key = 'X-Live-Newest-Artifact'; value = lib.math.decstr(lastup, &nbuf[20]); }, lib.http.header { key = 'Content-Length', value = '0' } ) if self.live_last ~= 0 and self.live_last <= lastup then lib.net.mg_printf(self.con, 'HTTP/1.1 %s', lib.http.codestr(200)) for i = 0, [hdrs.type.N] do lib.net.mg_printf(self.con, '%s: %s\r\n', hdrs[i].key, hdrs[i].value) end lib.net.mg_printf(self.con, '\r\n') else self:rawpage(200, pg, [lib.mem.ptr(lib.http.header)] { ptr = &hdrs[0], ct = 3 }) end end terra convo:stdpage(pg: convo.page) self:statpage(200, pg) end terra convo:reroute_cookie(dest: rawstring, cookie: rawstring) var hdrs = array( lib.http.header { key = 'Content-Type', value = 'text/html; charset=UTF-8' }, lib.http.header { key = 'Location', value = dest }, lib.http.header { key = 'Set-Cookie', value = cookie } ) var body = data.view.docskel { instance = self.srv.cfg.instance.ptr; title = 'rerouting'; body = 'you are being redirected'; class = 'error'; navlinks = ''; attr = ''; } body:send(self.con, 303, [lib.mem.ptr(lib.http.header)] { ptr = &hdrs[0], ct = [hdrs.type.N] - lib.trn(cookie == nil,1,0) }) end ................................................................................ lib.dbg('sending cookie ',{&sesskey[0],15}) p = lib.str.ncpy(p, '; Path=/', 9) end self:reroute_cookie(dest, &sesskey[0]) end terra convo:complain(code: uint16, title: rawstring, msg: rawstring) if msg == nil then msg = "i'm sorry, dave. i can't let you do that" end var ti: lib.str.acc ti:compose('error :: ', title) var bo: lib.str.acc bo:compose('<div class="message"><img class="icon" src="/s/warn.webp"><h1>',title,'</h1><p>',msg,'</p></div>') var body = [convo.page] { title = ti:finalize(); body = bo:finalize(); class = lib.str.plit 'error'; cache = false; } self:statpage(code, body) body.title:free() body.body:free() end convo.methods.assertpow = macro(function(self, pow) return quote ................................................................................ if self.aid == 0 or self.who.rights.powers.[pow:asvalue()]() == false then ok = false self:complain(403,'insufficient privileges',['you lack the <strong>'..pow:asvalue()..'</strong> power and cannot perform this action']) end in ok end end) -- CALL ONLY ONCE PER VAR terra convo:postv(name: rawstring) if self.varbuf.ptr == nil then self.varbuf = lib.mem.heapa(int8, self.msg.body.len + self.msg.query.len) self.vbofs = self.varbuf.ptr end var o = lib.net.mg_http_get_var(&self.msg.body, name, self.vbofs, self.varbuf.ct - (self.vbofs - self.varbuf.ptr)) ................................................................................ if lib.str.ncmp(mimevar.ptr, mime, lib.math.biggest(mimevar.ct, [#mime])) == 0 then ret = [lib.http.mime[name]] else ret = [mimeneg] end in ret end end local handle = { http = terra(con: &lib.net.mg_connection, event_kind: int, event: &opaque, userdata: &opaque) var server = [&srv](userdata) var mgpeer = getpeer(con) var peer = lib.store.inet { port = mgpeer.port; } if mgpeer.is_ip6 then peer.pv = 6 else peer.pv = 4 end if peer.pv == 6 then for i = 0, 16 do peer.v6[i] = mgpeer.ip6[i] end else -- v4 @[&uint32](&peer.v4) = mgpeer.ip ................................................................................ -- for now i'm leaving it as is, but note that netmask restrictions -- WILL NOT WORK until upstream gets its shit together. FIXME -- needs to check for an X-Forwarded-For header from nginx and -- use that instead of the peer iff peer is ::1/127.1 FIXME -- maybe also haproxy support? switch event_kind do case lib.net.MG_EV_HTTP_MSG then lib.dbg('routing HTTP request') var msg = [&lib.net.mg_http_message](event) var co = convo { con = con, srv = server, msg = msg; aid = 0, aid_issue = 0, who = nil; reqtype = lib.http.mime.none; peer = peer, live_last = 0; } co.varbuf.ptr = nil co.navbar.ptr = nil co.actorcache.top = 0 co.actorcache.cur = 0 co.ui_hue = server.cfg.ui_hue -- first, check for an accept header. if it's there, we need to -- iterate over the values and pick the highest-priority one do var acc = lib.http.findheader(msg, 'Accept') -- TODO handle q-value if acc ~= nil and acc.ptr ~= nil then var [mimevar] = [lib.mem.ref(int8)] { ptr = acc.ptr } var i = 0 while i < acc.ct do if acc.ptr[i] == @',' or acc.ptr[i] == @';' then mimevar.ct = (acc.ptr+i) - mimevar.ptr var t = [mimeneg] if t ~= lib.http.mime.none then co.reqtype = t ................................................................................ else co.reqtype = lib.http.mime.html end ::foundtype::end -- we need to check if there's any cookies sent with the request, -- and if so, whether they contain any credentials. this will be -- used to set the auth parameters in the http conversation var cookies_p = lib.http.findheader(msg, 'Cookie') if cookies_p ~= nil and cookies_p.ptr ~= nil then var cookies = cookies_p.ptr var key = [lib.mem.ref(int8)] {ptr = cookies, ct = 0} var val = [lib.mem.ref(int8)] {ptr = nil, ct = 0} var i = 0 while i < cookies_p.ct and cookies[i] ~= 0 and cookies[i] ~= @'\r' and cookies[i] ~= @'\n' do -- cover all the bases ................................................................................ end if co.aid ~= 0 then var sess, usr = co.srv:actor_session_fetch(co.aid, peer, co.aid_issue) if sess.ok == false then co.aid = 0 co.aid_issue = 0 else co.who = usr.ptr co.who.rights.powers = server:actor_powers_fetch(co.who.id) var userhue, hueok = server:actor_conf_int_get(co.who.id, 'ui-accent') if hueok then co.ui_hue = userhue end end end var livelast_p = lib.http.findheader(msg, 'X-Live-Last-Arrival') if livelast_p ~= nil and livelast_p.ptr ~= nil then var ll, ok = lib.math.decparse(pstring{ptr = livelast_p.ptr, ct = livelast_p.ct - 1}) if ok then co.live_last = ll end end var uridec = lib.mem.heapa(int8, msg.uri.len) defer uridec:free() var urideclen = lib.net.mg_url_decode(msg.uri.ptr, msg.uri.len, uridec.ptr, uridec.ct, 1) var uri = uridec if urideclen == -1 then for i = 0,msg.uri.len do ................................................................................ end end uri.ct = msg.uri.len else uri.ct = urideclen end lib.dbg('routing URI ', {uri.ptr, uri.ct}) if lib.str.ncmp('GET', msg.method.ptr, msg.method.len) == 0 then co.method = [lib.http.method.get] route.dispatch_http(&co, uri, [lib.http.method.get]) elseif lib.str.ncmp('POST', msg.method.ptr, msg.method.len) == 0 then co.method = [lib.http.method.get] route.dispatch_http(&co, uri, [lib.http.method.post]) elseif lib.str.ncmp('HEAD', msg.method.ptr, msg.method.len) == 0 then co.method = [lib.http.method.head] route.dispatch_http(&co, uri, [lib.http.method.head]) elseif lib.str.ncmp('OPTIONS', msg.method.ptr, msg.method.len) == 0 then co.method = [lib.http.method.options] route.dispatch_http(&co, uri, [lib.http.method.options]) else co:complain(400,'unknown method','you have submitted an invalid http request') end if co.aid ~= 0 then lib.mem.heapf(co.who) end if co.varbuf.ptr ~= nil then co.varbuf:free() end ................................................................................ do self.pol_reg = false var sreg = self.overlord:conf_get('policy-self-register') if sreg:ref() then if lib.str.cmp(sreg.ptr, 'on') == 0 then self.pol_reg = true else self.pol_reg = false end sreg:free() end end do self.credmgd = false var sreg = self.overlord:conf_get('credential-store') if sreg:ref() then if lib.str.cmp(sreg.ptr, 'managed') == 0 then self.credmgd = true else self.credmgd = false end sreg:free() end end do self.maxupsz = [1024 * 100] -- 100 kilobyte default var sreg = self.overlord:conf_get('maximum-artifact-size') if sreg:ref() then var sz, ok = lib.math.fsz_parse(sreg) if ok then self.maxupsz = sz else lib.warn('invalid configuration value for maximum-artifact-size; keeping default 100K upload limit') end sreg:free() end end self.pol_sec = secmode.lockdown var smode = self.overlord:conf_get('policy-security') if smode.ptr ~= nil then if lib.str.cmp(smode.ptr, 'public') == 0 then self.pol_sec = secmode.public elseif lib.str.cmp(smode.ptr, 'private') == 0 then self.pol_sec = secmode.private elseif lib.str.cmp(smode.ptr, 'lockdown') == 0 then self.pol_sec = secmode.lockdown elseif lib.str.cmp(smode.ptr, 'isolate') == 0 then self.pol_sec = secmode.isolate end smode:free() end self.ui_hue = config.default_ui_accent var shue = self.overlord:conf_get('ui-accent') if shue.ptr ~= nil then var hue,ok = lib.math.decparse(shue) if ok then self.ui_hue = hue end shue:free() end end return { overlord = srv; convo = convo; route = route; secmode = secmode; } |
Added static/live.js version [6fb4c9ec70].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
/* first things first, we need to scan over the document and see * if there are any UI elements unfortunate enough to need * interactivity beyond what native HTML+CSS can provide. if so, * we attach the appropriate listeners to them. */ window.addEventListener('load', function() { /* update hue-picker background when slider is adjusted */ document.querySelectorAll('.color-picker').forEach(function(box) { let slider = box.querySelector('[data-color-pick]'); box.style.setProperty('--hue', slider.value); slider.addEventListener('input', function(e) { box.style.setProperty('--hue', e.target.value); }); }); /* the main purpose of this script -- by marking itself with the * data-live property, an html element registers itself for live * updates from the server. this is pretty straightforward: we * retrieve this url from the server as a get request, create a * tree from its html, find the element in question, ferret out * any deltas, and apply them. */ document.querySelectorAll('*[data-live]').forEach(function(container) { let interv = parseFloat(container.attributes.getNamedItem('data-live').nodeValue) * 1000; container._liveLastArrival = '0'; /* TODO include header for this */ window.setInterval(function() { var req = new Request(window.location, { method: 'GET', headers: { 'X-Live-Last-Arrival': container._liveLastArrival } }) fetch(req).then(function(resp) { if (!resp.ok) return; let newest = resp.headers.get('X-Live-Newest-Artifact'); if (newest <= container._liveLastArrival) { resp.body.cancel(); return; } container._liveLastArrival = newest resp.text().then(function(htmlbody) { var parser = new DOMParser(); var newdoc = parser.parseFromString(htmlbody,'text/html') // console.log(newdoc.getElementById(container.id).innerHTML) container.innerHTML = newdoc.getElementById(container.id).innerHTML }) }) }, interv) }); }); |
Modified static/style.scss from [ada3763759] to [0e6b10a9e2].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 .. 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 .. 69 70 71 72 73 74 75 76 77 78 79 80 81 82 ... 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 ... 341 342 343 344 345 346 347 348 349 350 351 352 353 354 ... 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 ... 494 495 496 497 498 499 500 501 502 503 504 505 506 507 ... 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 ... 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 ... 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 |
$color: hsl(323,100%,65%);
%sans { font-family: "Alegreya Sans", "Open Sans", sans-serif; }
%serif { font-family: Alegreya, GaramondNo8, "Garamond Premier Pro", "Adobe Garamond", Garamond, Junicode, serif; }
%teletype { font-family: "Inconsolata LGC", Inconsolata, monospace; font-size: 12pt !important; }
@function tone($pct, $alpha: 0) { @return adjust-color($color, $lightness: $pct, $alpha: $alpha) }
body {
@extend %sans;
background-color: tone(-55%);
color: tone(25%);
font-size: 14pt;
margin: 0;
padding: 0;
................................................................................
::placeholder {
color: tone(0,-0.3);
font-style: italic;
}
a[href] {
color: tone(10%);
text-decoration-color: tone(10%,-0.5);
&:hover {
color: white;
text-shadow: 0 0 15px tone(20%);
text-decoration-color: tone(10%,-0.1);
}
&.button { @extend %button; }
}
a[href^="//"],
a[href^="http://"],
a[href^="https://"] { // external link
&:hover::after {
................................................................................
border: 1px solid black;
color: tone(25%);
text-shadow: 1px 1px black;
text-decoration: none;
text-align: center;
cursor: default;
user-select: none;
background: linear-gradient(to bottom,
tone(-47%),
tone(-50%) 15%,
tone(-50%) 75%,
tone(-53%)
);
&:hover, &:focus {
................................................................................
> a[href] {
display: block;
padding: 0.25in 0.10in;
//padding: calc((25% - 1em)/2) 0.15in;
&, &::after { transition: 0.3s; }
text-shadow: 1px 1px 1px black;
&:hover{
transform: scale(120%);
}
}
}
}
}
main {
................................................................................
@extend %box;
display: block;
width: 4in;
margin:auto;
padding: 0.5in;
text-align: center;
menu:first-of-type { margin-top: 0.3in; }
}
div.login {
@extend %box;
width: 4in;
padding: 0.4in;
> .msg {
................................................................................
font-size: 1.5ex !important;
letter-spacing: 1.3px;
padding-bottom: 3px;
border-radius: 2px;
vertical-align: baseline;
box-shadow: 1px 1px 1px black;
}
div.post {
@extend %box;
display: grid;
grid-template-columns: 1in 1fr max-content;
grid-template-rows: min-content max-content;
margin-bottom: 0.1in;
>.avatar {
grid-column: 1/2; grid-row: 1/2;
img { display: block; width: 1in; margin:0; }
background: linear-gradient(to bottom, tone(-53%), tone(-57%));
}
>a[href].username {
display: block;
grid-column: 1/3;
grid-row: 2/3;
text-align: left;
................................................................................
}
>.content {
grid-column: 2/4; grid-row: 1/2;
padding: 0.2in;
@extend %serif;
font-size: 110%;
text-align: justify;
}
> a[href].permalink {
display: block;
grid-column: 3/4; grid-row: 2/3;
font-size: 80%;
text-align: right;
padding: 0.1in;
................................................................................
h1, h2, h3, h4, h5, h6 {
background: linear-gradient(to right, tone(-50%), transparent);
margin-left: -0.4in;
padding-left: 0.2in;
text-shadow: 0 2px 0 black;
}
}
menu { all: unset; display: block; }
body.conf main {
display: grid;
grid-template-columns: 2in 1fr;
grid-template-rows: max-content 1fr;
> menu {
margin-left: -0.25in;
grid-column: 1/2; grid-row: 1/2;
background: linear-gradient(to bottom, tone(-45%),tone(-55%));
border: 1px solid black;
padding: 0.1in;
> a[href] {
@extend %button;
display: block;
text-align: left;
}
> a[href] + a[href] {
border-top: none;
}
hr {
border: none;
}
}
> .panel {
grid-column: 2/3; grid-row: 1/3;
padding-left: 0.15in;
> h1 {
padding-bottom: 0.1in;
margin-bottom: 0.1in;
margin-left: -0.15in;
................................................................................
&.vertical-float {
flex-flow: column;
float: right;
width: 40%;
margin-left: 0.1in;
}
> %button {
flex-basis: 0;
flex-grow: 1;
display: block; margin: 2px;
}
}
.check-panel {
display: flex;
................................................................................
100% { opacity: 0; transform: scale(0.9) translateY(-0.12in); display: none; }
}
.flashmsg {
display: block;
position: fixed;
top: 1.3in;
max-width: 3in;
padding: 0.5in 0.2in;
left: 0; right: 0;
text-align: center;
text-shadow: 0 0 15px tone(10%);
margin: auto;
background: linear-gradient(to bottom, tone(-49%), tone(-43%,-0.1));
border: 1px solid tone(0%);
border-radius: 3px;
box-shadow: 0 0 50px tone(-55%);
color: white;
animation: ease forwards flashup;
//cubic-bezier(0.4, 0.63, 0.6, 0.31)
animation-duration: 3s;
}
form.action-bar {
display: flex;
> * {
flex-grow: 1;
flex-basis: 0;
margin-left: 0.1in;
}
> *:first-child {
margin-left: 0;
}
}
|
| | > > | > > | > > > | > > > > > > | > > > > > > > > > > > > > > > > > > > > | < < < < < < < < < < < < < < < < < | | | > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 .. 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 .. 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 ... 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 ... 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 ... 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 ... 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 ... 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 ... 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 ... 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 |
$default-color: hsl(323,100%,65%); %sans { font-family: "Alegreya Sans", "Open Sans", sans-serif; } %serif { font-family: Alegreya, GaramondNo8, "Garamond Premier Pro", "Adobe Garamond", Garamond, Junicode, serif; } %teletype { font-family: "Inconsolata LGC", Inconsolata, monospace; font-size: 12pt !important; } // @function tone($pct, $alpha: 0) { @return adjust-color($color, $lightness: $pct, $alpha: $alpha) } @function tone($pct, $alpha: 0) { @return hsla(var(--hue), 100%, 65% + $pct, 1 + $alpha) } :root { --hue: 323; } body { @extend %sans; background-color: tone(-55%); color: tone(25%); font-size: 14pt; margin: 0; padding: 0; ................................................................................ ::placeholder { color: tone(0,-0.3); font-style: italic; } a[href] { color: tone(10%); text-decoration-color: tone(10%,-0.5); &:hover, &:focus { color: white; text-shadow: 0 0 15px tone(20%); text-decoration-color: tone(10%,-0.1); outline: none; } &.button { @extend %button; } } a[href^="//"], a[href^="http://"], a[href^="https://"] { // external link &:hover::after { ................................................................................ border: 1px solid black; color: tone(25%); text-shadow: 1px 1px black; text-decoration: none; text-align: center; cursor: default; user-select: none; -webkit-user-drag: none; -webkit-app-region: no-drag; background: linear-gradient(to bottom, tone(-47%), tone(-50%) 15%, tone(-50%) 75%, tone(-53%) ); &:hover, &:focus { ................................................................................ > a[href] { display: block; padding: 0.25in 0.10in; //padding: calc((25% - 1em)/2) 0.15in; &, &::after { transition: 0.3s; } text-shadow: 1px 1px 1px black; &:hover{ transform: scale(1.2); } } } } } main { ................................................................................ @extend %box; display: block; width: 4in; margin:auto; padding: 0.5in; text-align: center; menu:first-of-type { margin-top: 0.3in; } img.icon { width: 1.875in; height: 1.875in; } } div.login { @extend %box; width: 4in; padding: 0.4in; > .msg { ................................................................................ font-size: 1.5ex !important; letter-spacing: 1.3px; padding-bottom: 3px; border-radius: 2px; vertical-align: baseline; box-shadow: 1px 1px 1px black; } div.thread { margin-left: 0.3in; & + div.post { margin-top: 0.3in; } } div.post { @extend %box; display: grid; grid-template-columns: 1in 1fr max-content; grid-template-rows: min-content max-content; margin-bottom: 0.1in; >.avatar { grid-column: 1/2; grid-row: 1/2; img { display: block; width: 1in; height: 1in; margin:0; } background: linear-gradient(to bottom, tone(-53%), tone(-57%)); } >a[href].username { display: block; grid-column: 1/3; grid-row: 2/3; text-align: left; ................................................................................ } >.content { grid-column: 2/4; grid-row: 1/2; padding: 0.2in; @extend %serif; font-size: 110%; text-align: justify; color: tone(25%); } > a[href].permalink { display: block; grid-column: 3/4; grid-row: 2/3; font-size: 80%; text-align: right; padding: 0.1in; ................................................................................ h1, h2, h3, h4, h5, h6 { background: linear-gradient(to right, tone(-50%), transparent); margin-left: -0.4in; padding-left: 0.2in; text-shadow: 0 2px 0 black; } } %navmenu, body.profile main > menu { margin-left: -0.25in; grid-column: 1/2; grid-row: 1/2; background: linear-gradient(to bottom, tone(-45%),tone(-55%)); border: 1px solid black; padding: 0.1in; > a[href] { @extend %button; display: block; text-align: left; } > a[href] + a[href] { border-top: none; } hr { border: none; } } menu { all: unset; display: block; } body.conf main { display: grid; grid-template-columns: 2in 1fr; grid-template-rows: max-content 1fr; > menu { @extend %navmenu; } > .panel { grid-column: 2/3; grid-row: 1/3; padding-left: 0.15in; > h1 { padding-bottom: 0.1in; margin-bottom: 0.1in; margin-left: -0.15in; ................................................................................ &.vertical-float { flex-flow: column; float: right; width: 40%; margin-left: 0.1in; } > %button { flex-basis: min-content; flex-grow: 1; display: block; margin: 2px; } } .check-panel { display: flex; ................................................................................ 100% { opacity: 0; transform: scale(0.9) translateY(-0.12in); display: none; } } .flashmsg { display: block; position: fixed; top: 1.3in; max-width: 3in; padding: 0.4in 0.2in; left: 0; right: 0; text-align: center; text-shadow: 0 0 15px tone(10%); margin: auto; background: linear-gradient(to bottom, tone(-49%), tone(-43%,-0.1)); border: 1px solid tone(0%); border-radius: 3px; box-shadow: 0 0 50px tone(-55%); color: white; animation: ease forwards flashup; //cubic-bezier(0.4, 0.63, 0.6, 0.31) animation-duration: 2.5s; } form.action-bar { display: flex; > * { flex-grow: 1; flex-basis: 0; margin-left: 0.1in; } > *:first-child { margin-left: 0; } } .color-picker { /* implemented using javascript, alas */ @extend %box; label { text-shadow: 1px 1px black; } padding: 0.1in; } |
Modified store.t from [d79d41c9fe] to [e7b33e5534].
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 ... 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 ... 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 ... 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 ... 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 |
chgcount: uint
mentions: lib.mem.ptr(uint64)
circles: lib.mem.ptr(uint64) --only meaningful if scope is set to circle
convoheaduri: str
parent: uint64
-- ephemera
localpost: bool
source: &m.source
-- save :: bool -> {} (defined in acl.t due to dep. hell)
}
local cnf = terralib.memoize(function(ty,rty)
rty = rty or ty
return struct {
enum: {&opaque, uint64, rawstring} -> intptr
get: {&opaque, uint64, rawstring} -> rty
set: {&opaque, uint64, rawstring, ty} -> {}
reset: {&opaque, uint64, rawstring} -> {}
}
end)
struct m.notif {
kind: m.notiftype.t
when: uint64
union {
post: uint64
reaction: int8[8]
}
}
struct m.inet {
pv: uint8 -- 0 = null, 4 = ipv4, 6 = ipv6
union {
v4: uint8[4]
................................................................................
context: str
}
struct m.auth {
-- a credential record
aid: uint64
uid: uint64
aname: str
netmask: m.inet
privs: m.privset
blacklist: bool
}
struct m.relationship {
agent: uint64
................................................................................
-- cookie issue time: m.timepoint
actor_auth_register_uid: {&m.source, uint64, uint64} -> {}
-- notifies the backend module of the UID that has been assigned for
-- an authentication ID
-- aid: uint64
-- uid: uint64
actor_conf_str: cnf(rawstring, lib.mem.ptr(int8))
actor_conf_int: cnf(intptr, lib.stat(intptr))
auth_create_pw: {&m.source, uint64, bool, lib.mem.ptr(int8)} -> {}
-- uid: uint64
-- reset: bool (delete other passwords?)
-- pw: pstring
auth_purge_pw: {&m.source, uint64, rawstring} -> {}
auth_purge_otp: {&m.source, uint64, rawstring} -> {}
auth_purge_trust: {&m.source, uint64, rawstring} -> {}
auth_sigtime_user_fetch: {&m.source, uint64} -> m.timepoint
-- authentication tokens and accounts have a property that controls
-- whether auth cookies dated to a certain point are valid. cookies
-- that are generated before the timepoint are considered invalid.
................................................................................
-- timestamp: timepoint
post_save: {&m.source, &m.post} -> {}
post_create: {&m.source, &m.post} -> uint64
post_destroy: {&m.source, uint64} -> {}
post_fetch: {&m.source, uint64} -> lib.mem.ptr(m.post)
post_enum_author_uid: {&m.source, uint64, m.range} -> lib.mem.ptr(lib.mem.ptr(m.post))
post_attach_ctl: {&m.source, uint64, uint64, bool} -> {}
-- attaches or detaches an existing database artifact
-- post id: uint64
-- artifact id: uint64
-- detach: bool
artifact_instantiate: {&m.source, lib.mem.ptr(uint8), lib.mem.ptr(int8)} -> uint64
-- instantiate an artifact in the database, either installing a new
-- artifact or returning the id of an existing artifact with the same hash
-- artifact: bytea
-- mime: pstring
artifact_quicksearch: {&m.source, lib.mem.ptr(uint8)} -> {uint64,bool}
-- checks whether a hash is already in the database without uploading
................................................................................
-- proto: kompromat (null for all records, or a prototype describing the records to return)
nkvd_sanction_issue: {&m.source, &m.sanction} -> uint64
nkvd_sanction_vacate: {&m.source, uint64} -> {}
nkvd_sanction_enum_target: {&m.source, uint64} -> {}
nkvd_sanction_enum_issuer: {&m.source, uint64} -> {}
nkvd_sanction_review: {&m.source, m.timepoint} -> {}
convo_fetch_xid: {&m.source,rawstring} -> lib.mem.ptr(m.post)
convo_fetch_cid: {&m.source,uint64} -> lib.mem.ptr(m.post)
timeline_actor_fetch_uid: {&m.source, uint64, m.range} -> lib.mem.ptr(lib.mem.ptr(m.post))
timeline_instance_fetch: {&m.source, m.range} -> lib.mem.ptr(lib.mem.ptr(m.post))
}
struct m.source {
backend: &m.backend
id: lib.mem.ptr(int8)
handle: &opaque
string: lib.mem.ptr(int8)
}
|
> > | | > | | > > > > | | > > > | | > | > > | | < | > > > > > < < < > > > |
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 ... 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 ... 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 ... 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 ... 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 |
chgcount: uint mentions: lib.mem.ptr(uint64) circles: lib.mem.ptr(uint64) --only meaningful if scope is set to circle convoheaduri: str parent: uint64 -- ephemera localpost: bool accent: int16 depth: uint16 -- used in conversations to indicate tree depth source: &m.source -- save :: bool -> {} (defined in acl.t due to dep. hell) } m.user_conf_funcs = function(be,n,ty,rty,rty2) rty = rty or ty local gt if not rty2 -- what the fuck? then gt = {&m.source, uint64, rawstring} -> rty; else gt = {&m.source, uint64, rawstring} -> {rty, rty2}; end for k, t in pairs { enum = {&m.source, uint64, rawstring} -> lib.mem.ptr(rty); get = gt; set = {&m.source, uint64, rawstring, ty} -> {}; reset = {&m.source, uint64, rawstring} -> {}; } do be.entries[#be.entries+1] = { field = 'actor_conf_'..n..'_'..k, type = t } end end struct m.notif { kind: m.notiftype.t when: uint64 union { post: uint64 reaction: int8[16] } } struct m.inet { pv: uint8 -- 0 = null, 4 = ipv4, 6 = ipv6 union { v4: uint8[4] ................................................................................ context: str } struct m.auth { -- a credential record aid: uint64 uid: uint64 kind: str aname: str comment: str netmask: m.inet privs: m.privset blacklist: bool } struct m.relationship { agent: uint64 ................................................................................ -- cookie issue time: m.timepoint actor_auth_register_uid: {&m.source, uint64, uint64} -> {} -- notifies the backend module of the UID that has been assigned for -- an authentication ID -- aid: uint64 -- uid: uint64 auth_enum_uid: {&m.source, uint64} -> lib.mem.ptr(lib.mem.ptr(m.auth)) auth_enum_handle: {&m.source, rawstring} -> lib.mem.ptr(lib.mem.ptr(m.auth)) auth_attach_pw: {&m.source, uint64, bool, pstr, pstr} -> {} -- uid: uint64 -- reset: bool (delete other passwords?) -- pw: pstring -- comment: pstring auth_purge_pw: {&m.source, uint64, rawstring} -> {} auth_purge_otp: {&m.source, uint64, rawstring} -> {} auth_purge_trust: {&m.source, uint64, rawstring} -> {} auth_sigtime_user_fetch: {&m.source, uint64} -> m.timepoint -- authentication tokens and accounts have a property that controls -- whether auth cookies dated to a certain point are valid. cookies -- that are generated before the timepoint are considered invalid. ................................................................................ -- timestamp: timepoint post_save: {&m.source, &m.post} -> {} post_create: {&m.source, &m.post} -> uint64 post_destroy: {&m.source, uint64} -> {} post_fetch: {&m.source, uint64} -> lib.mem.ptr(m.post) post_enum_author_uid: {&m.source, uint64, m.range} -> lib.mem.ptr(lib.mem.ptr(m.post)) post_enum_parent: {&m.source, uint64} -> lib.mem.ptr(lib.mem.ptr(m.post)) post_attach_ctl: {&m.source, uint64, uint64, bool} -> {} -- attaches or detaches an existing database artifact -- post id: uint64 -- artifact id: uint64 -- detach: bool thread_latest_arrival_calc: {&m.source, uint64} -> m.timepoint artifact_instantiate: {&m.source, lib.mem.ptr(uint8), lib.mem.ptr(int8)} -> uint64 -- instantiate an artifact in the database, either installing a new -- artifact or returning the id of an existing artifact with the same hash -- artifact: bytea -- mime: pstring artifact_quicksearch: {&m.source, lib.mem.ptr(uint8)} -> {uint64,bool} -- checks whether a hash is already in the database without uploading ................................................................................ -- proto: kompromat (null for all records, or a prototype describing the records to return) nkvd_sanction_issue: {&m.source, &m.sanction} -> uint64 nkvd_sanction_vacate: {&m.source, uint64} -> {} nkvd_sanction_enum_target: {&m.source, uint64} -> {} nkvd_sanction_enum_issuer: {&m.source, uint64} -> {} nkvd_sanction_review: {&m.source, m.timepoint} -> {} timeline_actor_fetch_uid: {&m.source, uint64, m.range} -> lib.mem.ptr(lib.mem.ptr(m.post)) timeline_instance_fetch: {&m.source, m.range} -> lib.mem.ptr(lib.mem.ptr(m.post)) } m.user_conf_funcs(m.backend, 'str', rawstring, lib.mem.ptr(int8)) m.user_conf_funcs(m.backend, 'int', intptr, intptr, bool) struct m.source { backend: &m.backend id: lib.mem.ptr(int8) handle: &opaque string: lib.mem.ptr(int8) } |
Modified tpl.t from [682e534236] to [9f68e11c52].
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
rec.methods.append = terra([symself], [accumulator])
lib.dbg(['appending template ' .. tid])
[tallyup]
accumulator:cue([runningtally])
[appenders]
return accumulator
end
rec.methods.send = terra([symself], [destcon], code: uint16, hd: lib.mem.ptr(lib.http.header))
lib.dbg(['transmitting template ' .. tid])
[tallyup]
lib.net.mg_printf([destcon], 'HTTP/1.1 %s', lib.http.codestr(code))
for i = 0, hd.ct do
lib.net.mg_printf([destcon], '%s: %s\r\n', hd.ptr[i].key, hd.ptr[i].value)
end
lib.net.mg_printf([destcon],'Content-Length: %llu\r\n\r\n', [runningtally] + 1)
[senders]
lib.net.mg_send([destcon],'\r\n',2)
end
return rec
end
return m
|
| | > > > > > > > > > > > |
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
rec.methods.append = terra([symself], [accumulator]) lib.dbg(['appending template ' .. tid]) [tallyup] accumulator:cue([runningtally]) [appenders] return accumulator end rec.methods.head = terra([symself], [destcon], code: uint16, hd: lib.mem.ptr(lib.http.header)) lib.dbg(['transmitting template headers ' .. tid]) [tallyup] lib.net.mg_printf([destcon], 'HTTP/1.1 %s', lib.http.codestr(code)) for i = 0, hd.ct do lib.net.mg_printf([destcon], '%s: %s\r\n', hd.ptr[i].key, hd.ptr[i].value) end lib.net.mg_printf([destcon],'Content-Length: %llu\r\n\r\n', [runningtally] + 1) end rec.methods.send = terra([symself], [destcon], code: uint16, hd: lib.mem.ptr(lib.http.header)) lib.dbg(['transmitting template ' .. tid]) symself:head(destcon,code,hd) [senders] lib.net.mg_send([destcon],'\r\n',2) end rec.methods.sz = terra([symself]) lib.dbg(['tallying template ' .. tid]) [tallyup] return [runningtally] + 1 end return rec end return m |
Modified view/conf-profile.tpl from [d384fd3b9f] to [48e88ad45a].
1 2 3 4 5 6 |
<form method="post"> <div class="elem"><label>handle</label> <div class="txtbox">@!handle</div></div> <div class="elem"><label for="nym">display name</label> <input type="text" name="nym" id="nym" placeholder="j. random poster" value="@:nym"></div> <div class="elem"><label for="bio">bio</label><textarea name="bio" id="bio" placeholder="tall, dark, and mysterious">@!bio</textarea></div> <button>commit</button> </form> |
> > | > > |
1 2 3 4 5 6 7 8 9 10 |
<form method="post"> <div class="elem"><label>handle</label> <div class="txtbox">@!handle</div></div> <div class="elem"><label for="nym">display name</label> <input type="text" name="nym" id="nym" placeholder="j. random poster" value="@:nym"></div> <div class="elem"><label for="bio">bio</label><textarea name="bio" id="bio" placeholder="tall, dark, and mysterious">@!bio</textarea></div> <div class="elem color-picker"><label for="hue">accent</label><input type="range" min="0" max="360" value="@hue" name="hue" id="hue" data-color-pick></div> <menu class="choice vertical"> <button>commit all</button> <button name="act" value="reset-hue">use server colors</button> </menu> </form> |
Modified view/conf.tpl from [09b447ff32] to [6af7c57c63].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<menu> <a href="/conf/profile">profile</a> <a href="/conf/avi">avatar</a> <a href="/conf/sec">security</a> <a href="/conf/rel">relationships</a> <a href="/conf/qnt">quarantine</a> <a href="/conf/acl">ACL shortcuts</a> <a href="/conf/rooms">chatrooms</a> <a href="/conf/circles">circles</a> @menu </menu> <div class="panel"> @panel </div> |
> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<menu>
<a href="/conf/profile">profile</a>
<a href="/conf/avi">avatar</a>
<a href="/conf/ui">interface</a>
<a href="/conf/sec">security</a>
<a href="/conf/rel">relationships</a>
<a href="/conf/qnt">quarantine</a>
<a href="/conf/acl">ACL shortcuts</a>
<a href="/conf/rooms">chatrooms</a>
<a href="/conf/circles">circles</a>
@menu
</menu>
<div class="panel">
@panel
</div>
|
Modified view/docskel.tpl from [cb4a31dcc6] to [3229efd171].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<!doctype html> <html> <head> <title>@instance :: @title</title> <link rel="stylesheet" href="/s/style.css"> </head> <body class="@class"> <header><div> <h1>@title</h1> <nav> <a href="/instance">instance</a> @navlinks </nav> </div></header> <main> @body </main> </body> </html> |
| > | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<!doctype html> <html> <head> <title>@instance :: @title</title> <link rel="stylesheet" type="text/css" href="/s/style.css"> <script type="text/javascript" src="/s/live.js" async></script> </head> <body class="@class"@attr> <header><div> <h1>@title</h1> <nav> <a href="/instance">instance</a> @navlinks </nav> </div></header> <main> @body </main> </body> </html> |
Added view/media.tpl version [5a68c18a8e].
> > > > > > > > > > > > |
1 2 3 4 5 6 7 8 9 10 11 12 |
<menu> <a href="/user/@:xid/media">new uploads</a> @folders </menu> <div name="gallery"> @images </div> <div name="files"> @files </div> |
Modified view/profile.tpl from [cfeb837b05] to [694fa2eec6].
11 12 13 14 15 16 17 18 19 20 21 22 23 |
<tr><th>following</th> <td>@nfollows</td></tr> <tr><th>followers</th> <td>@nfollowers</td></tr> <tr><th>mutuals</th> <td>@nmutuals</td></tr> <tr><th>@timephrase</th> <td>@tweetday</td></tr> </table> <form class="actions"> <a class="button" href="/@:xid">posts</a> <a class="button" href="/@:xid/media">media</a> <a class="button" href="/@:xid/social">associates</a> <hr> @auxbtn </form> </div> |
> |
11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<tr><th>following</th> <td>@nfollows</td></tr>
<tr><th>followers</th> <td>@nfollowers</td></tr>
<tr><th>mutuals</th> <td>@nmutuals</td></tr>
<tr><th>@timephrase</th> <td>@tweetday</td></tr>
</table>
<form class="actions">
<a class="button" href="/@:xid">posts</a>
<a class="button" href="/@:xid/arc">archive</a>
<a class="button" href="/@:xid/media">media</a>
<a class="button" href="/@:xid/social">associates</a>
<hr>
@auxbtn
</form>
</div>
|
Added view/tweet-mini.tpl version [4c5364412a].
> > > > |
1 2 3 4 |
<div class="post-mini"> <span class="permalink">[<a href="@permalink">@when</a>]</span> <span class="username">‹<a href="/@:acctlink">@nym</a>›</span> <span class="content">@text</span> </div> |
Modified view/tweet.tpl from [806c88c01c] to [e117899db4].
1 2 3 4 5 6 7 8 9 |
<div class="post"> <div class="avatar"><img src="@:avatar"></div> <a class="username" href="/@:acctlink">@nym</a> <div class="content"> <div class="subject">@!subject</div> <div class="text">@text</div> </div> <a class="permalink" href="@permalink">@when</a> </div> |
| |
1 2 3 4 5 6 7 8 9 |
<div class="post"@attr>
<div class="avatar"><img src="@:avatar"></div>
<a class="username" href="/@:acctlink">@nym</a>
<div class="content">
<div class="subject">@!subject</div>
<div class="text">@text</div>
</div>
<a class="permalink" href="@permalink">@when</a>
</div>
|