spec.js
1 import express from 'express' 2 const router = express.Router() 3 4 import state from './state.js' 5 import utils from './utils.js' 6 import validators from './validators.js' 7 import calculations from '../calculations.js' 8 import events from './events.js' 9 //import connector from './connector.js' 10 import lightning from './lightning.js' 11 12 router.post('/events', (req, res, next) => { 13 state.serverState.sessions.forEach(s => { 14 if (s.token === req.headers.authorization){ 15 req.body.blame = s.ownerId 16 } 17 }) 18 next() 19 }) 20 21 import fs from 'fs' 22 import chalk from 'chalk' 23 import crypto from 'crypto' 24 import { config } from './configParser.js' 25 const priv = fs.readFileSync(config.aodir + '/key') 26 27 router.post('/events', (req, res, next)=>{ 28 let errRes = [] 29 switch (req.body.type){ 30 case "completed-toggled": 31 if ( 32 validators.isTaskId(req.body.taskId, errRes) 33 ) { 34 events.completedToggled( 35 req.body.taskId, 36 req.body.blame, 37 utils.buildResCallback(res) 38 ) 39 } else { 40 res.status(400).send(errRes); 41 } 42 break 43 case "member-purged": 44 let isActive = state.serverState.members.some(m => { 45 if (m.memberId === req.body.memberId && (m.boost > 0 || m.active)){ 46 return true 47 } 48 }) 49 if ( 50 validators.isMemberId(req.body.memberId, errRes) && 51 !isActive 52 ) { 53 events.memberPurged( 54 req.body.memberId, 55 req.body.blame, 56 utils.buildResCallback(res) 57 ) 58 } else { 59 res.status(400).send(errRes); 60 } 61 break 62 case "task-colored": 63 if ( 64 validators.isTaskId(req.body.taskId, errRes) && 65 validators.isTaskId(req.body.inId, errRes) && 66 validators.isNotes(req.body.color, errRes) 67 ) { 68 events.taskColored( 69 req.body.taskId, 70 req.body.inId, 71 req.body.color, 72 req.body.blame, 73 utils.buildResCallback(res) 74 ) 75 } else { 76 res.status(400).send(errRes); 77 } 78 break 79 case "task-locked": 80 state.serverState.members.some( m => { 81 if (m.memberId === req.body.blame){ 82 console.log(chalk.bold( crypto.privateDecrypt(priv, Buffer.from(req.body.name, 'hex') ).toString('latin1'))) 83 console.log( chalk.bold.magenta('-', m.name)) 84 events.taskCreated( 85 "__lock:" + req.body.name, 86 req.body.color, 87 req.body.deck, 88 req.body.inId, 89 req.body.blame, 90 utils.buildResCallback(res) 91 ) 92 } 93 }) 94 break 95 case "address-updated": 96 if (validators.isTaskId(req.body.taskId, errRes)){ 97 lightning.newAddress() 98 .then(result => { 99 events.addressUpdated( 100 req.body.taskId, 101 result['bech32'], 102 req.body.blame, 103 utils.buildResCallback(res) 104 ) 105 }) 106 .catch(console.log) 107 } 108 break 109 case "task-seized": 110 if ( 111 validators.isTaskId(req.body.taskId, errRes) && 112 validators.isTaskId(req.body.inId, errRes) 113 ) { 114 events.taskSeized( 115 req.body.taskId, 116 req.body.inId, 117 utils.buildResCallback(res) 118 ); 119 } else { 120 res.status(400).send(errRes); 121 } 122 break 123 case "task-touched": 124 if ( 125 validators.isTaskId(req.body.taskId, errRes) && 126 validators.isNotes(req.body.stack, errRes) && 127 validators.yesSir(req.body.position, errRes) && 128 validators.yesSir(req.body.value, errRes) 129 ) { 130 events.taskTouched( 131 req.body.taskId, 132 req.body.stack, 133 req.body.position, 134 req.body.blame, 135 utils.buildResCallback(res) 136 ); 137 } else { 138 res.status(400).send(errRes); 139 } 140 break 141 case "task-valued": 142 if ( 143 validators.isTaskId(req.body.taskId, errRes) && 144 validators.isAmount(req.body.value, errRes) 145 ) { 146 req.body.value = req.body.value.toString() 147 events.taskValued( 148 req.body.taskId, 149 req.body.value, 150 req.body.blame, 151 utils.buildResCallback(res) 152 ); 153 lightning 154 .createInvoice(req.body.value, "ao <3: " + crypto.randomUUID(), '~ ao ~', 3600) 155 .then(result => { 156 console.log("invoice result?>>>" , result) 157 events.invoiceCreated(req.body.taskId, result.bolt11, result.payment_hash, req.body.blame) 158 }) 159 .catch(err => { 160 console.log(err) 161 }); 162 } else { 163 res.status(400).send(errRes); 164 } 165 break 166 case "task-popped": 167 if ( 168 validators.isTaskId(req.body.taskId, errRes) && 169 validators.isTaskId(req.body.inId, errRes) 170 ) { 171 events.taskPopped( 172 req.body.taskId, 173 req.body.inId, 174 req.body.blame, 175 utils.buildResCallback(res) 176 ); 177 } else { 178 res.status(400).send(errRes); 179 } 180 break 181 case "pile-prioritized": 182 if ( 183 validators.isTaskId(req.body.inId, errRes) && 184 req.body.tasks.every(tId => { 185 console.log("validating tId") 186 validators.isTaskId(tId, errRes) 187 }) 188 ) { 189 events.pilePrioritized(req.body.inId, req.body.tasks, req.body.blame, utils.buildResCallback(res)); 190 } else { 191 res.status(400).send(errRes); 192 } 193 break 194 case "pile-sub-tasked": 195 if ( 196 validators.isTaskId(req.body.inId, errRes) && 197 req.body.tasks.every(tId => validators.isTaskId(tId, errRes)) 198 ) { 199 events.pileSubTasked(req.body.inId, req.body.tasks, req.body.blame, utils.buildResCallback(res)); 200 } else { 201 res.status(400).send(errRes); 202 } 203 break 204 case "pile-de-sub-tasked": 205 if ( 206 validators.isTaskId(req.body.inId, errRes) && 207 req.body.tasks.every(tId => validators.isTaskId(tId, errRes)) 208 ) { 209 events.pileDeSubTasked(req.body.inId, req.body.tasks, req.body.blame, utils.buildResCallback(res)); 210 } else { 211 res.status(400).send(errRes); 212 } 213 break 214 case 'highlighted': 215 if ( 216 validators.isTaskId(req.body.taskId, errRes) && 217 validators.isMemberId(req.body.memberId, errRes) && 218 validators.isBool(req.body.valence, errRes) 219 ){ 220 events.highlighted(req.body.taskId, req.body.memberId, req.body.valence, req.body.blame, utils.buildResCallback(res)) 221 } else { 222 res.status(400).send(errRes) 223 } 224 break 225 case 'ao-disconnected': 226 if (validators.isAddress(req.body.address, errRes)){ 227 events.aoDisconnected( 228 req.body.address, 229 utils.buildResCallback(res) 230 ) 231 } else { 232 res.status(400).send(errRes) 233 } 234 break 235 case 'rent-set': 236 if ( 237 validators.isAmount(req.body.amount, errRes) 238 ){ 239 events.rentSet( 240 req.body.amount, 241 utils.buildResCallback(res) 242 ) 243 } else { 244 res.status(400).send(errRes) 245 } 246 break 247 case 'cap-set': 248 if ( 249 validators.isAmount(req.body.amount, errRes) 250 ){ 251 events.capSet( 252 req.body.amount, 253 utils.buildResCallback(res) 254 ) 255 } else { 256 res.status(400).send(errRes) 257 } 258 break 259 // Inter-AO functions are disabled until tor or o7s can be reintegrated. 260 //case 'ao-outbound-connected': 261 // connector.postEvent(req.body.address, req.body.secret, { 262 // type: 'ao-inbound-connected', 263 // alias: state.serverState.cash.alias, 264 // address: state.serverState.cash.address.trim(), 265 // secret: req.body.secret, // 266 // }, (err, subscriptionResponse) => { 267 // if (err || !subscriptionResponse || !subscriptionResponse.lastInsertRowid){ 268 // return res.status(200).send(['ao-connect failed']) 269 // } 270 // events.aoOutboundConnected( 271 // req.body.address, 272 // req.body.secret, 273 // utils.buildResCallback(res) 274 // ) 275 // }) 276 // break 277 //case 'ao-inbound-connected': 278 // if ( 279 // validators.isNotes(req.body.address, errRes) && 280 // validators.isNotes(req.body.secret, errRes) 281 // ){ 282 // events.aoInboundConnected( 283 // req.body.address.trim(), 284 // req.body.alias || 'ao', 285 // req.body.secret, 286 // utils.buildResCallback(res) 287 // ) 288 // } else { 289 // res.status(400).send(errRes) 290 // } 291 // break 292 //case 'ao-relay': 293 // let secret 294 // state.serverState.ao.forEach(a => { 295 // if (a.address == req.body.address){ 296 // secret = a.outboundSecret 297 // } 298 // }) 299 // if (secret){ 300 // connector.postEvent(req.body.address, secret, req.body.ev, (err, connectorRes) => { 301 // if (err){ 302 // res.status(400).send(err) 303 // } else { 304 // res.status(201).send(connectorRes) 305 // } 306 // }) 307 // } else { 308 // console.log("no connection for ", req.body.address) 309 // next() 310 // } 311 // break 312 case 'invoice-created': 313 if ( 314 validators.isTaskId(req.body.taskId, errRes) && 315 validators.isAmount(req.body.amount, errRes) 316 ) { 317 lightning.createInvoice(req.body.amount, "<3" + crypto.randomUUID(), '~', 3600) 318 .then(result => { 319 events.invoiceCreated(req.body.taskId, result.bolt11, result.payment_hash, req.body.blame, utils.buildResCallback(res)) 320 }) 321 .catch(err => { 322 console.log({err}) 323 res.status(200).send("attempt failed") 324 }); 325 } else { 326 res.status(400).send(errRes) 327 } 328 break 329 case 'member-created': 330 if ( 331 validators.isValidName(req.body.name, errRes) && 332 validators.isNotes(req.body.fob, errRes) && 333 validators.isNotes(req.body.secret) 334 ){ 335 events.memberCreated( 336 req.body.name, 337 req.body.fob, 338 req.body.secret, 339 utils.buildResCallback(res) 340 ) 341 } else { 342 res.status(400).send(errRes) 343 } 344 break 345 case 'member-activated': 346 if ( 347 validators.isMemberId(req.body.memberId, errRes) 348 ){ 349 events.memberActivated( 350 req.body.memberId, 351 utils.buildResCallback(res) 352 ) 353 } else { 354 res.status(400).send(errRes) 355 } 356 break 357 case 'member-deactivated': 358 if ( 359 validators.isMemberId(req.body.memberId, errRes) 360 ){ 361 events.memberDeactivated( 362 req.body.memberId, 363 utils.buildResCallback(res) 364 ) 365 } else { 366 res.status(400).send(errRes) 367 } 368 break 369 case 'member-field-updated': 370 if ( 371 validators.isMemberId(req.body.memberId, errRes) && 372 validators.isField(req.body.field, errRes) && 373 validators.yesSir(req.body.newfield, errRes) 374 ){ 375 if (req.body.field === 'name' && !validators.isValidName(req.body.newfield, errRes)) { 376 return res.status(400).send(errRes) 377 } 378 events.memberFieldUpdated( 379 req.body.memberId, 380 req.body.field, 381 req.body.newfield, 382 utils.buildResCallback(res) 383 ) 384 } else { 385 res.status(400).send(errRes) 386 } 387 break 388 case 'resource-created': 389 console.log('trying',req.body.resourceId,req.body.name,req.body.charged,req.body.secret,req.body.trackStock) 390 if ( 391 validators.isNewResourceId(req.body.resourceId, errRes) && 392 validators.isNotes(req.body.name, errRes) && 393 validators.isAmount(req.body.charged, errRes) && 394 validators.isNotes(req.body.secret, errRes) && 395 validators.isBool(req.body.trackStock, errRes) 396 ){ 397 events.resourceCreated( 398 req.body.resourceId, 399 req.body.name, 400 req.body.charged, 401 req.body.secret, 402 req.body.trackStock, 403 utils.buildResCallback(res) 404 ) 405 } else { 406 console.log(errRes) 407 res.status(400).send(errRes) 408 } 409 break 410 case 'resource-used': 411 let member = validators.isActiveMemberId(req.body.memberId, errRes) 412 let resource = validators.isResourceId(req.body.resourceId, errRes) 413 let memberTask = validators.isTaskId(member.memberId, errRes) 414 let canAccess = calculations.access(member.active, memberTask.boost, resource.charged) 415 if ( 416 member && 417 resource && 418 validators.isAmount(req.body.charged, errRes) && 419 validators.isNotes(req.body.notes, errRes) && 420 canAccess 421 ){ 422 events.resourceUsed( 423 req.body.resourceId, 424 req.body.memberId, 425 req.body.charged, 426 req.body.notes, 427 utils.buildResCallback(res) 428 ) 429 } else { 430 res.status(400).send(errRes) 431 } 432 break 433 case 'resource-stocked': 434 if ( 435 validators.isResourceId(req.body.resourceId, errRes) && 436 validators.isMemberId(req.body.memberId, errRes) && 437 validators.isAmount(req.body.amount, errRes) && 438 validators.isAmount(req.body.paid, errRes) && 439 validators.yesSir(req.body.notes, errRes) 440 ){ 441 events.resourceStocked( 442 req.body.resourceId, 443 req.body.memberId, 444 req.body.amount, 445 req.body.paid, 446 req.body.notes, 447 utils.buildResCallback(res) 448 ) 449 } else { 450 res.status(400).send(errRes) 451 } 452 break 453 case 'resource-booked': 454 if ( 455 validators.isTaskId(req.body.resourceId, errRes) && 456 validators.isMemberId(req.body.blame, errRes) && 457 validators.yesSir(req.body.startTs, errRes) && 458 validators.yesSir(req.body.endTs, errRes) && 459 validators.yesSir(req.body.charge, errRes) && 460 validators.yesSir(req.body.notes, errRes) 461 ){ 462 events.resourceBooked( 463 req.body.resourceId, 464 req.body.blame, 465 req.body.startTs, 466 req.body.endTs, 467 req.body.charge, 468 req.body.notes, 469 utils.buildResCallback(res) 470 ) 471 } else { 472 res.status(400).send(errRes) 473 } 474 break 475 case 'session-killed': 476 if ( 477 validators.isSession(req.body.session, errRes) 478 ){ 479 events.sessionKilled( 480 req.body.session, 481 utils.buildResCallback(res) 482 ) 483 } else { 484 res.status(400).send(errRes) 485 } 486 break 487 case 'task-created': 488 if ( 489 validators.isNotes(req.body.name, errRes) && 490 validators.isNotes(req.body.color, errRes) && 491 validators.isNotes(req.body.deck, errRes) && 492 validators.isTaskId(req.body.inId, errRes) 493 ){ 494 events.taskCreated( 495 req.body.name, 496 req.body.color, 497 req.body.deck, 498 req.body.inId, 499 req.body.blame, 500 utils.buildResCallback(res) 501 ) 502 } else { 503 res.status(400).send(errRes) 504 } 505 break 506 case 'task-guilded': 507 if ( 508 validators.isTaskId(req.body.taskId, errRes) && 509 validators.yesSir(req.body.guild, errRes) 510 ){ 511 events.taskGuilded( 512 req.body.taskId, 513 req.body.guild, 514 req.body.blame, 515 utils.buildResCallback(res) 516 ) 517 } else { 518 res.status(400).send(errRes) 519 } 520 break 521 case 'task-sub-tasked': 522 if ( 523 validators.isTaskId(req.body.taskId, errRes) && 524 validators.isTaskId(req.body.inId, errRes) 525 ){ 526 events.taskSubTasked( 527 req.body.taskId, 528 req.body.inId, 529 req.body.blame, 530 utils.buildResCallback(res) 531 ) 532 533 534 } else { 535 res.status(400).send(errRes) 536 } 537 break 538 case 'task-de-sub-tasked': 539 if ( 540 validators.isTaskId(req.body.taskId, errRes) && 541 validators.isTaskId(req.body.inId, errRes) 542 ){ 543 events.taskDeSubTasked( 544 req.body.taskId, 545 req.body.inId, 546 req.body.blame, 547 utils.buildResCallback(res) 548 ) 549 } else { 550 res.status(400).send(errRes) 551 } 552 break 553 case 'task-claimed': 554 if ( 555 validators.isTaskId(req.body.taskId, errRes) && 556 validators.isTaskId(req.body.inId, errRes) 557 ){ 558 events.taskClaimed( 559 req.body.taskId, 560 req.body.blame, 561 req.body.inId, 562 utils.buildResCallback(res) 563 ) 564 } else { 565 res.status(400).send(errRes) 566 } 567 break 568 case 'task-unclaimed': 569 if ( 570 validators.isTaskId(req.body.taskId, errRes) && 571 validators.isTaskId(req.body.inId, errRes) 572 ){ 573 events.taskUnclaimed( 574 req.body.taskId, 575 req.body.blame, 576 req.body.inId, 577 utils.buildResCallback(res) 578 ) 579 } else { 580 res.status(400).send(errRes) 581 } 582 break 583 case 'task-removed': 584 if ( 585 validators.isMemberId(req.body.taskId, errRes) 586 ){ 587 events.memberPurged( 588 req.body.taskId, 589 req.body.blame, 590 utils.buildResCallback(res) 591 ) 592 } else if ( 593 validators.isResourceId(req.body.taskId, errRes) 594 ){ 595 events.resourcePurged( 596 req.body.taskId, 597 req.body.blame, 598 utils.buildResCallback(res) 599 ) 600 } else if ( 601 validators.isTaskId(req.body.taskId, errRes) 602 ){ 603 events.taskRemoved( 604 req.body.taskId, 605 req.body.blame, 606 utils.buildResCallback(res) 607 ) 608 } else { 609 res.status(400).send(errRes) 610 } 611 break 612 case 'task-grabbed': 613 if ( 614 validators.isTaskId(req.body.taskId, errRes) && 615 req.body.taskId !== req.body.blame 616 ){ 617 events.taskGrabbed( 618 req.body.taskId, 619 req.body.blame, 620 utils.buildResCallback(res) 621 ) 622 } else { 623 res.status(400).send(errRes) 624 } 625 break 626 case 'task-dropped': 627 if ( 628 validators.isTaskId(req.body.taskId, errRes) && 629 req.body.taskId !== req.body.blame 630 ){ 631 events.taskDropped( 632 req.body.taskId, 633 req.body.blame, 634 utils.buildResCallback(res) 635 ) 636 } else { 637 res.status(400).send(errRes) 638 } 639 break 640 case 'task-swapped': 641 if ( 642 validators.isTaskId(req.body.taskId, errRes) && 643 validators.isTaskId(req.body.swapId1, errRes) && 644 validators.isTaskId(req.body.swapId2, errRes) 645 ) { 646 events.taskSwapped( 647 req.body.taskId, 648 req.body.swapId1, 649 req.body.swapId2, 650 req.body.blame, 651 utils.buildResCallback(res) 652 ) 653 } else { 654 res.status(400).send(errRes) 655 } 656 break 657 case 'task-prioritized': 658 if ( 659 validators.isTaskId(req.body.inId, errRes) && 660 validators.isTaskId(req.body.fromId, errRes) && 661 validators.isTaskId(req.body.taskId, errRes) 662 ){ 663 events.taskPrioritized( 664 req.body.taskId, 665 req.body.inId, 666 req.body.fromId, 667 req.body.blame, 668 utils.buildResCallback(res) 669 ) 670 } else { 671 res.status(400).send(errRes) 672 } 673 break 674 case 'task-completed': 675 if ( 676 validators.isTaskId(req.body.inId, errRes) && 677 validators.isTaskId(req.body.taskId, errRes) 678 ){ 679 events.taskCompleted( 680 req.body.taskId, 681 req.body.inId, 682 req.body.blame, 683 utils.buildResCallback(res) 684 ) 685 } else { 686 res.status(400).send(errRes) 687 } 688 break 689 case 'tasks-received': 690 if (true) { // XXX 691 let safeTasks = req.body.tasks.map(t => { // hard question 692 t.deck = [] 693 return t 694 }) 695 events.tasksReceived( 696 safeTasks, 697 req.body.blame, 698 utils.buildResCallback(res)) 699 } else { 700 res.status(400).send(errRes) 701 } 702 break 703 default: 704 next() 705 } 706 }) 707 708 export default router