├── README.md ├── app.js ├── db ├── index.js └── users.js ├── package-lock.json ├── package.json └── views └── index.html /README.md: -------------------------------------------------------------------------------- 1 | # api-example-secrets-api -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const limit = require("express-rate-limit"); 3 | const bodyParser = require("body-parser"); 4 | const store = require("node-localstorage"); 5 | const expressBasicAuth = require("express-basic-auth"); 6 | const crypto = require("crypto"); 7 | const passport = require("passport"); 8 | const Strategy = require("passport-http-bearer").Strategy; 9 | const db = require("./db"); 10 | 11 | const localStorage = new store.LocalStorage("./scratch"); 12 | const app = express(); 13 | let functionalData = []; 14 | 15 | //Auth methods 16 | const basicAuth = expressBasicAuth({ 17 | authorizer: authenticate, 18 | unauthorizedResponse: "Error: Incorrect username or password.", 19 | }); 20 | 21 | function computeFunctionalData(username) { 22 | const userSecrets = db.users.getSecrets(username) || []; 23 | functionalData = data.secrets.concat(userSecrets); 24 | } 25 | 26 | function authenticate(username, password) { 27 | const pass = localStorage.getItem(username); 28 | if (pass) { 29 | const passwordMatches = expressBasicAuth.safeCompare( 30 | password, 31 | localStorage.getItem(username) 32 | ); 33 | if (passwordMatches) computeFunctionalData(username); 34 | return passwordMatches; 35 | } else { 36 | return false; 37 | } 38 | } 39 | 40 | function apiAuth(req, res, next) { 41 | const key = req.query.apiKey; 42 | if (localStorage.getItem("apiKeys").includes(key)) { 43 | next(); 44 | } 45 | res.status(401).json({ error: "API Key does not exist." }); 46 | } 47 | 48 | passport.use( 49 | new Strategy(function (token, cb) { 50 | db.users.findByToken(token, function (err, user) { 51 | if (err) { 52 | console.log(err); 53 | return cb(err); 54 | } 55 | if (!user) { 56 | return cb(null, false); 57 | } 58 | return cb(null, user); 59 | }); 60 | }) 61 | ); 62 | 63 | const limiter = limit.rateLimit({ 64 | windowMs: 15 * 60 * 1000, // 15 minutes 65 | max: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes) 66 | standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers 67 | legacyHeaders: false, // Disable the `X-RateLimit-*` headers 68 | }); 69 | 70 | app.use(limiter); 71 | app.use(bodyParser.json()); 72 | app.use(bodyParser.urlencoded({ extended: true })); 73 | app.set("view engine", "html"); 74 | app.set("views", __dirname + "/views"); 75 | app.engine("html", require("ejs").renderFile); 76 | 77 | let data = { 78 | secrets: [ 79 | { 80 | id: 1, 81 | secret: 82 | "I secretly eat ice cream straight from the tub when no one's looking.", 83 | emScore: 3, 84 | username: "user123", 85 | timestamp: "2023-06-25 12:01:23 utc", 86 | }, 87 | { 88 | id: 2, 89 | secret: 90 | "I pretend to be on important conference calls just so I can have some alone time in my home office and nap.", 91 | emScore: 6, 92 | username: "secretsnacker", 93 | timestamp: "2023-06-25 13:45:17 utc", 94 | }, 95 | { 96 | id: 3, 97 | secret: 98 | "I have a secret stash of chocolate hidden in my sock drawer to satisfy my sweet tooth.", 99 | emScore: 2, 100 | username: "chocoHoarder", 101 | timestamp: "2023-06-25 14:30:51 utc", 102 | }, 103 | { 104 | id: 4, 105 | secret: 106 | "I secretly dance in front of the mirror pretending to be a backup dancer for famous singers.", 107 | emScore: 7, 108 | username: "dancingqueen", 109 | timestamp: "2023-06-25 15:12:36 utc", 110 | }, 111 | { 112 | id: 5, 113 | secret: 114 | "I wear mismatched socks on purpose, just to see if anyone notices.", 115 | emScore: 2, 116 | username: "sockrebel", 117 | timestamp: "2023-06-25 16:05:09 utc", 118 | }, 119 | { 120 | id: 6, 121 | secret: 122 | "I pretend to know how to cook fancy dishes, but I'm really just following YouTube tutorials.", 123 | emScore: 5, 124 | username: "culinaryimposter", 125 | timestamp: "2023-06-25 17:20:42 utc", 126 | }, 127 | { 128 | id: 7, 129 | secret: 130 | "I've named all the plants in my house, and sometimes I have conversations with them.", 131 | emScore: 4, 132 | username: "plantwhisperer", 133 | timestamp: "2023-06-25 18:15:28 utc", 134 | }, 135 | { 136 | id: 8, 137 | secret: 138 | "I secretly binge-watch reality TV shows while pretending to be productive.", 139 | emScore: 4, 140 | username: "realitytvjunkie", 141 | timestamp: "2023-06-25 19:03:57 utc", 142 | }, 143 | { 144 | id: 9, 145 | secret: 146 | "I talk to my pet fish and imagine they're responding to me with witty comebacks.", 147 | emScore: 3, 148 | username: "fishwhisperer", 149 | timestamp: "2023-06-25 20:10:22 utc", 150 | }, 151 | { 152 | id: 10, 153 | secret: 154 | "I've been known to wear my pajamas under my work clothes for extra comfort during the day.", 155 | emScore: 2, 156 | username: "comfortcrawler", 157 | timestamp: "2023-06-25 21:07:14 utc", 158 | }, 159 | { 160 | id: 11, 161 | secret: 162 | "I've perfected the art of taking selfies in the bathroom to hide the fact that I'm really just sitting on the toilet.", 163 | emScore: 7, 164 | username: "masteroftoiletselfies", 165 | timestamp: "2023-06-25 22:15:39 utc", 166 | }, 167 | { 168 | id: 12, 169 | secret: 170 | "I have a secret alter ego on social media where I post silly memes and jokes anonymously.", 171 | emScore: 3, 172 | username: "mememaster123", 173 | timestamp: "2023-06-25 23:08:02 utc", 174 | }, 175 | { 176 | id: 13, 177 | secret: 178 | "I put on my headphones without playing any music, just to avoid small talk with strangers.", 179 | emScore: 3, 180 | username: "headphonehider", 181 | timestamp: "2023-06-26 00:01:54 utc", 182 | }, 183 | { 184 | id: 14, 185 | secret: 186 | "I've convinced my friends that I'm a great cook, but I actually order takeout and put it on fancy plates.", 187 | emScore: 6, 188 | username: "fakechefextraordinaire", 189 | timestamp: "2023-06-26 01:20:37 utc", 190 | }, 191 | { 192 | id: 15, 193 | secret: 194 | "I created a fake email address to sign up for all the free trials and discounts without getting spammed.", 195 | emScore: 4, 196 | username: "discounthunter", 197 | timestamp: "2023-06-26 02:18:09 utc", 198 | }, 199 | { 200 | id: 16, 201 | secret: 202 | "I pretend to be talking on the phone when I'm really just rehearsing conversations in my head.", 203 | emScore: 3, 204 | username: "phonetalkmaster", 205 | timestamp: "2023-06-26 03:25:43 utc", 206 | }, 207 | { 208 | id: 17, 209 | secret: 210 | "I secretly watch cute animal videos to brighten my day, even though I'm a tough-looking person.", 211 | emScore: 3, 212 | username: "toughandsoft", 213 | timestamp: "2023-06-26 04:15:26 utc", 214 | }, 215 | { 216 | id: 18, 217 | secret: 218 | "I've hidden a secret stash of snacks in the glove compartment of my car for emergency munchies.", 219 | emScore: 2, 220 | username: "snackstasher", 221 | timestamp: "2023-06-26 05:03:59 utc", 222 | }, 223 | { 224 | id: 19, 225 | secret: 226 | "I have an ongoing competition with myself to see how long I can go without doing laundry.", 227 | emScore: 4, 228 | username: "laundryslacker", 229 | timestamp: "2023-06-26 06:10:31 utc", 230 | }, 231 | { 232 | id: 20, 233 | secret: 234 | "I make up ridiculous excuses to avoid going to the gym, but really, I'm just lazy.", 235 | emScore: 4, 236 | username: "gymexcusemaster", 237 | timestamp: "2023-06-26 07:08:17 utc", 238 | }, 239 | { 240 | id: 21, 241 | secret: 242 | "I have a secret collection of embarrassing childhood photos that I use to blackmail my siblings.", 243 | emScore: 8, 244 | username: "siblingblackmailer", 245 | timestamp: "2023-06-26 08:12:44 utc", 246 | }, 247 | { 248 | id: 22, 249 | secret: 250 | "I change the language settings on people's phones when they're not looking, just to confuse them.", 251 | emScore: 6, 252 | username: "phoneprankster", 253 | timestamp: "2023-06-26 09:05:29 utc", 254 | }, 255 | { 256 | id: 23, 257 | secret: 258 | "I have a secret obsession with collecting random hotel toiletries from my travels.", 259 | emScore: 3, 260 | username: "toiletrycollector", 261 | timestamp: "2023-06-26 10:14:53 utc", 262 | }, 263 | { 264 | id: 24, 265 | secret: 266 | "I pretend to be a detective when I'm doing everyday chores, solving imaginary mysteries in my head.", 267 | emScore: 3, 268 | username: "imaginarydetective", 269 | timestamp: "2023-06-26 11:09:38 utc", 270 | }, 271 | { 272 | id: 25, 273 | secret: 274 | "I've perfected the art of eating snacks silently to avoid sharing them with others.", 275 | emScore: 4, 276 | username: "stealthsnacker", 277 | timestamp: "2023-06-26 12:25:11 utc", 278 | }, 279 | { 280 | id: 26, 281 | secret: 282 | "I have a secret Pinterest board dedicated to my future life as a millionaire, complete with mansion and exotic pets.", 283 | emScore: 6, 284 | username: "futuremillionaire", 285 | timestamp: "2023-06-26 13:15:53 utc", 286 | }, 287 | { 288 | id: 27, 289 | secret: 290 | "I use funny usernames when ordering takeout, just to see if the delivery drivers crack a smile.", 291 | emScore: 3, 292 | username: "hilariousfoodie", 293 | timestamp: "2023-06-26 14:07:47 utc", 294 | }, 295 | { 296 | id: 28, 297 | secret: 298 | "I've created a secret language with my best friend, and we use it to have silly conversations in public.", 299 | emScore: 3, 300 | username: "languagemasters", 301 | timestamp: "2023-06-26 15:10:55 utc", 302 | }, 303 | { 304 | id: 29, 305 | secret: 306 | "I've memorized cheesy pickup lines, just in case I need to lighten the mood in awkward situations.", 307 | emScore: 5, 308 | username: "pickuplinepro", 309 | timestamp: "2023-06-26 16:01:42 utc", 310 | }, 311 | { 312 | id: 30, 313 | secret: 314 | "I secretly hide my favorite snacks at the back of the pantry, hoping no one will find them.", 315 | emScore: 3, 316 | username: "snackninja", 317 | timestamp: "2023-06-26 17:09:15 utc", 318 | }, 319 | { 320 | id: 31, 321 | secret: 322 | "I've mastered the art of pretending to laugh at jokes I don't understand, just to fit in.", 323 | emScore: 3, 324 | username: "laughingpretender", 325 | timestamp: "2023-06-26 18:06:29 utc", 326 | }, 327 | { 328 | id: 32, 329 | secret: 330 | "I have a secret stash of cat videos that I watch whenever I need an instant mood boost.", 331 | emScore: 3, 332 | username: "catvideoenthusiast", 333 | timestamp: "2023-06-26 19:10:12 utc", 334 | }, 335 | { 336 | id: 33, 337 | secret: 338 | "I purposely mispronounce words to sound fancy, even though I have no idea how they're actually pronounced.", 339 | emScore: 5, 340 | username: "pronunciationmaster", 341 | timestamp: "2023-06-26 20:15:43 utc", 342 | }, 343 | { 344 | id: 34, 345 | secret: 346 | "I have a secret obsession with collecting funny-looking socks, and my drawer is full of them.", 347 | emScore: 3, 348 | username: "funkysockcollector", 349 | timestamp: "2023-06-26 21:08:57 utc", 350 | }, 351 | { 352 | id: 35, 353 | secret: 354 | "I talk to inanimate objects when no one's around, just to feel like I have an audience.", 355 | emScore: 3, 356 | username: "inanimateconversationalist", 357 | timestamp: "2023-06-26 22:02:04 utc", 358 | }, 359 | { 360 | id: 36, 361 | secret: 362 | "I've perfected the art of making silly faces in the mirror, just to make myself laugh.", 363 | emScore: 4, 364 | username: "sillyfaceartist", 365 | timestamp: "2023-06-26 23:09:36 utc", 366 | }, 367 | { 368 | id: 37, 369 | secret: 370 | "I secretly sing in the shower, imagining I'm performing at a sold-out concert.", 371 | emScore: 3, 372 | username: "showersongbird", 373 | timestamp: "2023-06-27 00:04:25 utc", 374 | }, 375 | { 376 | id: 38, 377 | secret: 378 | "I have a secret stash of candy hidden in my office desk drawer for those mid-afternoon cravings.", 379 | emScore: 3, 380 | username: "candycraver", 381 | timestamp: "2023-06-27 01:08:19 utc", 382 | }, 383 | { 384 | id: 39, 385 | secret: 386 | "I pretend to be a food critic when I go out to eat, just to see the staff's reaction.", 387 | emScore: 4, 388 | username: "foodiereviewer", 389 | timestamp: "2023-06-27 02:10:37 utc", 390 | }, 391 | { 392 | id: 40, 393 | secret: 394 | "I've created a secret language with my pet, and we have conversations that only we understand.", 395 | emScore: 5, 396 | username: "animallinguist", 397 | timestamp: "2023-06-27 03:05:29 utc", 398 | }, 399 | { 400 | id: 41, 401 | secret: 402 | "I've convinced my friends that I have a secret talent for juggling, but I can only juggle two balls.", 403 | emScore: 5, 404 | username: "jugglingenthusiast", 405 | timestamp: "2023-06-27 04:07:51 utc", 406 | }, 407 | { 408 | id: 42, 409 | secret: 410 | "I pretend to be a food blogger, taking pictures of my meals at restaurants even though I have no intention of posting them.", 411 | emScore: 6, 412 | username: "foodiepretender", 413 | timestamp: "2023-06-27 05:09:44 utc", 414 | }, 415 | { 416 | id: 43, 417 | secret: 418 | "I've convinced my friends that I'm a great singer, but in reality, I can't hold a tune.", 419 | emScore: 5, 420 | username: "singingimpostor", 421 | timestamp: "2023-06-27 06:15:22 utc", 422 | }, 423 | { 424 | id: 44, 425 | secret: 426 | "I've convinced my friends that I have a secret talent for beatboxing, but I can only make strange noises.", 427 | emScore: 4, 428 | username: "beatboxcharlatan", 429 | timestamp: "2023-06-27 07:07:11 utc", 430 | }, 431 | { 432 | id: 45, 433 | secret: 434 | "I secretly wear a superhero cape under my clothes, just in case the world needs saving.", 435 | emScore: 6, 436 | username: "secrethero", 437 | timestamp: "2023-06-27 08:12:55 utc", 438 | }, 439 | { 440 | id: 46, 441 | secret: 442 | "I've created a secret handshake with my best friend, and we use it whenever we meet, just to confuse others.", 443 | emScore: 3, 444 | username: "handshaketrickster", 445 | timestamp: "2023-06-27 09:08:39 utc", 446 | }, 447 | { 448 | id: 47, 449 | secret: 450 | "I have a secret collection of cheesy romance novels hidden in the attic, and I read them when I need a guilty pleasure.", 451 | emScore: 4, 452 | username: "romancenoveladdict", 453 | timestamp: "2023-06-27 10:09:46 utc", 454 | }, 455 | { 456 | id: 48, 457 | secret: 458 | "I've convinced my friends that I have a secret talent for magic tricks, but it's all sleight of hand.", 459 | emScore: 5, 460 | username: "magicianimpostor", 461 | timestamp: "2023-06-27 11:05:32 utc", 462 | }, 463 | { 464 | id: 49, 465 | secret: 466 | "I have a secret collection of funny cat videos on my phone that I watch whenever I need a pick-me-up.", 467 | emScore: 3, 468 | username: "catvideocollector", 469 | timestamp: "2023-06-27 12:04:29 utc", 470 | }, 471 | { 472 | id: 50, 473 | secret: 474 | "I pretend to be an art critic when visiting galleries, analyzing paintings even though I have no knowledge of art.", 475 | emScore: 4, 476 | username: "artcritiquefaker", 477 | timestamp: "2023-06-27 13:09:14 utc", 478 | }, 479 | ], 480 | }; 481 | 482 | //Index page 483 | app.get("/", (req, res) => { 484 | res.render("index.html"); 485 | }); 486 | 487 | app.get( 488 | "/clear", 489 | passport.authenticate("bearer", { session: false }), 490 | (req, res) => { 491 | if (req.user !== "angela") { 492 | res.send(401); 493 | } else { 494 | localStorage = new store.LocalStorage("./scratch"); 495 | db.users.deleteRecords(); 496 | } 497 | } 498 | ); 499 | 500 | app.post("/register", (req, res) => { 501 | const username = req.body.username; 502 | const password = req.body.password; 503 | if (localStorage.getItem(username)) { 504 | res.status(401).json({ error: "Username is already taken." }); 505 | } else { 506 | localStorage.setItem(username, password); 507 | res.status(200).json({ success: "Successfully registered." }); 508 | } 509 | }); 510 | 511 | //Get API Key 512 | app.get("/generate-api-key", async (req, res) => { 513 | const token = crypto.randomUUID(); 514 | // localStorage.clear(); 515 | let keys = JSON.parse(localStorage.getItem("apiKeys")) || []; 516 | keys.push(token); 517 | localStorage.setItem("apiKeys", JSON.stringify(keys)); 518 | res.json({ apiKey: token }); 519 | console.log(keys); 520 | }); 521 | 522 | app.post("/get-auth-token", (req, res) => { 523 | const username = req.body.username; 524 | const password = req.body.password; 525 | if (!localStorage.getItem(username)) { 526 | res.status(404).json({ error: "User does not exist." }); 527 | } 528 | const token = db.users.checkAlreadyHasToken(username); 529 | if (token) { 530 | res 531 | .status(404) 532 | .json({ error: "User has already issued token", token: token }); 533 | } 534 | 535 | if (localStorage.getItem(username) === password) { 536 | const token = crypto.randomUUID(); 537 | db.users.addRecord(username, token); 538 | res.status(200).json({ token: token }); 539 | } else { 540 | res.status(401).json({ error: "Incorrect password." }); 541 | } 542 | }); 543 | 544 | //Get a random secret 0. NO AUTH 545 | app.get("/random", (req, res) => { 546 | const randomIndex = Math.floor(Math.random() * data.secrets.length); 547 | res.json(data.secrets[randomIndex]); 548 | }); 549 | 550 | //Get all secrets paginated 1. BASIC AUTH 551 | app.get("/all", basicAuth, (req, res) => { 552 | const page = req.query.page; 553 | if (page) { 554 | res.json(functionalData.slice((page - 1) * 10, page * 10)); 555 | } else { 556 | res.json(functionalData.slice(0, 9)); 557 | } 558 | }); 559 | 560 | //Get a random secret with a particular embarrassment score or higher 561 | app.get("/filter", apiAuth, (req, res) => { 562 | let filteredSecrets = data.secrets; 563 | if (req.query.score) { 564 | filteredSecrets = filteredSecrets.filter( 565 | (secret) => secret.emScore >= parseInt(req.query.score) 566 | ); 567 | } 568 | if (filteredSecrets.length > 0) { 569 | res.json(filteredSecrets); 570 | } else { 571 | res.status(404).json({ error: "Secrets not found for the given filter." }); 572 | } 573 | }); 574 | 575 | // Route to retrieve a specific secret by username 576 | app.get( 577 | "/user-secrets", 578 | passport.authenticate("bearer", { session: false }), 579 | (req, res) => { 580 | const user = req.user.username; 581 | const filteredSecrets = db.users.getSecrets(user); 582 | console.log(filteredSecrets); 583 | if (filteredSecrets.length > 0) { 584 | res.json(filteredSecrets); 585 | } else { 586 | res 587 | .status(404) 588 | .json({ error: "Secrets not found for the given username." }); 589 | } 590 | } 591 | ); 592 | 593 | // Route to retrieve a specific secret by id 594 | app.get( 595 | "/secrets/:id", 596 | passport.authenticate("bearer", { session: false }), 597 | (req, res) => { 598 | computeFunctionalData(req.user.username); 599 | const id = req.params.id; 600 | const secret = functionalData.find((secret) => secret.id === parseInt(id)); 601 | secret 602 | ? res.json(secret) 603 | : res.status(404).json({ error: "Secret not found for the given id." }); 604 | } 605 | ); 606 | 607 | //POST 608 | app.post( 609 | "/secrets", 610 | passport.authenticate("bearer", { session: false }), 611 | (req, res) => { 612 | const username = req.user.username; 613 | const secrets = db.users.getSecrets(username); 614 | const newId = 615 | secrets.length > 0 ? parseInt(secrets.slice(-1)[0].id) + 1 : 51; 616 | 617 | const newData = { 618 | id: newId, 619 | secret: req.body.secret, 620 | emScore: parseInt(req.body.score), 621 | username: username, 622 | timestamp: `${new Date() 623 | .toISOString() 624 | .replace(/.\d+Z$/g, " utc") 625 | .replace("T", " ")}`, 626 | }; 627 | 628 | let weekAgo = new Date(); 629 | weekAgo.setDate(weekAgo.getDate() - 7); 630 | const oldestEntryDate = 631 | secrets.length > 0 ? new Date(secrets[0].timestamp) : new Date(); 632 | 633 | //If oldest entry date is more than a week old, clear cache. 634 | if ( 635 | oldestEntryDate.setDate(oldestEntryDate.getDate()) < 636 | weekAgo.setDate(weekAgo.getDate()) 637 | ) { 638 | localStorage.clear(); 639 | db.users.deleteRecords(); 640 | } 641 | db.users.addSecret(username, newData); 642 | 643 | res.status(200).json(newData); 644 | } 645 | ); 646 | 647 | //PUT 648 | 649 | app.put( 650 | "/secrets/:id", 651 | passport.authenticate("bearer", { session: false }), 652 | (req, res) => { 653 | const searchId = parseInt(req.params.id); 654 | const user = req.user.username; 655 | 656 | const newData = { 657 | id: searchId, 658 | secret: req.body.secret, 659 | emScore: parseInt(req.body.score), 660 | username: user, 661 | timestamp: `${new Date() 662 | .toISOString() 663 | .replace(/.\d+Z$/g, " utc") 664 | .replace("T", " ")}`, 665 | }; 666 | if (db.users.updateRecord(user, searchId, newData)) { 667 | res.status(200).json(newData); 668 | } else { 669 | res.status(404).json({ 670 | error: `Cannot update resource, given secret with id ${searchId} not found.`, 671 | }); 672 | } 673 | } 674 | ); 675 | 676 | //PATCH 677 | 678 | app.patch( 679 | "/secrets/:id", 680 | passport.authenticate("bearer", { session: false }), 681 | (req, res) => { 682 | const searchId = parseInt(req.params.id); 683 | const user = req.user.username; 684 | 685 | const match = db.users.getSecretById(user, searchId); 686 | if (!match) { 687 | res.status(404).json({ 688 | error: `Cannot update resource, given secret with id ${searchId} not found.`, 689 | }); 690 | } 691 | 692 | const newData = { 693 | id: match.id, 694 | secret: req.body.secret || match.secret, 695 | emScore: parseInt(req.body.score) || match.emScore, 696 | username: user, 697 | timestamp: `${new Date() 698 | .toISOString() 699 | .replace(/.\d+Z$/g, " utc") 700 | .replace("T", " ")}`, 701 | }; 702 | 703 | if (db.users.updateRecord(user, searchId, newData)) { 704 | res.status(200).json({ newData }); 705 | } else { 706 | res.status(404).json({ error: "Cannot update record." }); 707 | } 708 | } 709 | ); 710 | 711 | //DELETE 712 | app.delete( 713 | "/secrets/:id", 714 | passport.authenticate("bearer", { session: false }), 715 | (req, res) => { 716 | const searchId = parseInt(req.params.id); 717 | const user = req.user.username; 718 | if (!db.users.getSecretById(user, searchId)) { 719 | res.status(404).json({ 720 | error: `Cannot delete resource, given secret with id ${searchId} not found.`, 721 | }); 722 | } 723 | const result = db.users.deleteSecretWithId(user, searchId); 724 | console.log("result", result); 725 | if (result) { 726 | res.status(200).json({ 727 | message: `Secret with ID ${searchId} has been deleted successfully.`, 728 | }); 729 | } else { 730 | res.status(404).json({ 731 | error: `There was an issue deleting this resource.`, 732 | }); 733 | } 734 | } 735 | ); 736 | 737 | const port = 4000; 738 | app.listen(port, () => { 739 | console.log(`Server is running on port ${port}`); 740 | }); 741 | -------------------------------------------------------------------------------- /db/index.js: -------------------------------------------------------------------------------- 1 | exports.users = require("./users"); 2 | -------------------------------------------------------------------------------- /db/users.js: -------------------------------------------------------------------------------- 1 | var records = []; 2 | 3 | exports.findByToken = function (token, cb) { 4 | process.nextTick(function () { 5 | for (var i = 0, len = records.length; i < len; i++) { 6 | var record = records[i]; 7 | 8 | if (record.token === token) { 9 | return cb(null, record); 10 | } 11 | } 12 | return cb(null, null); 13 | }); 14 | }; 15 | 16 | exports.addRecord = function (username, token) { 17 | records.push({ 18 | username: username, 19 | token: token, 20 | secrets: [], 21 | }); 22 | }; 23 | 24 | exports.addSecret = function (username, secret) { 25 | for (var i = 0, len = records.length; i < len; i++) { 26 | var record = records[i]; 27 | if (record.username === username) { 28 | record.secrets.push(secret); 29 | } 30 | } 31 | }; 32 | 33 | exports.updateRecord = function (username, id, newData) { 34 | for (var i = 0, len = records.length; i < len; i++) { 35 | const record = records[i]; 36 | 37 | if (record.username === username) { 38 | console.log(records.secrets); 39 | for (var i = 0, len = record.secrets.length; i < len; i++) { 40 | const secret = record.secrets[i]; 41 | console.log("sec.id", secret.id); 42 | console.log("id", id); 43 | if (secret.id === id) { 44 | console.log("called"); 45 | record.secrets[i] = newData; 46 | console.log(secret); 47 | return true; 48 | } 49 | } 50 | } 51 | } 52 | return false; 53 | }; 54 | 55 | exports.checkAlreadyHasToken = function (username) { 56 | for (var i = 0, len = records.length; i < len; i++) { 57 | var record = records[i]; 58 | 59 | if (record.username === username) { 60 | return record.token; 61 | } else { 62 | return false; 63 | } 64 | } 65 | }; 66 | 67 | exports.getSecrets = function (username) { 68 | for (var i = 0, len = records.length; i < len; i++) { 69 | var record = records[i]; 70 | 71 | if (record.username === username) { 72 | return record.secrets; 73 | } 74 | } 75 | }; 76 | 77 | exports.getSecretById = function (username, id) { 78 | for (var i = 0, len = records.length; i < len; i++) { 79 | var record = records[i]; 80 | if (record.username === username) { 81 | for (var i = 0, len = record.secrets.length; i < len; i++) { 82 | var secret = record.secrets[i]; 83 | if (secret.id === id) { 84 | return secret; 85 | } 86 | } 87 | } 88 | } 89 | return false; 90 | }; 91 | 92 | exports.deleteSecretWithId = function (username, id) { 93 | for (var i = 0, len = records.length; i < len; i++) { 94 | var record = records[i]; 95 | if (record.username === username) { 96 | for (var i = 0, len = record.secrets.length; i < len; i++) { 97 | var secret = record.secrets[i]; 98 | console.log("db", secret); 99 | console.log("id", id); 100 | if (secret.id === id) { 101 | console.log("called"); 102 | record.secrets.splice(i, 1); 103 | console.log("after", record); 104 | return true; 105 | } 106 | } 107 | } 108 | } 109 | return false; 110 | }; 111 | 112 | exports.deleteRecords = function () { 113 | records = []; 114 | }; 115 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "secrets-api", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "secrets-api", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "body-parser": "^1.20.2", 13 | "ejs": "^3.1.9", 14 | "express": "^4.18.2", 15 | "express-basic-auth": "^1.2.1", 16 | "express-rate-limit": "^6.7.0", 17 | "node-localstorage": "^2.2.1", 18 | "passport": "^0.6.0", 19 | "passport-http-bearer": "^1.0.1" 20 | } 21 | }, 22 | "node_modules/accepts": { 23 | "version": "1.3.8", 24 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 25 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 26 | "dependencies": { 27 | "mime-types": "~2.1.34", 28 | "negotiator": "0.6.3" 29 | }, 30 | "engines": { 31 | "node": ">= 0.6" 32 | } 33 | }, 34 | "node_modules/ansi-styles": { 35 | "version": "4.3.0", 36 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 37 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 38 | "dependencies": { 39 | "color-convert": "^2.0.1" 40 | }, 41 | "engines": { 42 | "node": ">=8" 43 | }, 44 | "funding": { 45 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 46 | } 47 | }, 48 | "node_modules/array-flatten": { 49 | "version": "1.1.1", 50 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 51 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" 52 | }, 53 | "node_modules/async": { 54 | "version": "3.2.4", 55 | "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", 56 | "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" 57 | }, 58 | "node_modules/balanced-match": { 59 | "version": "1.0.2", 60 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 61 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 62 | }, 63 | "node_modules/basic-auth": { 64 | "version": "2.0.1", 65 | "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", 66 | "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", 67 | "dependencies": { 68 | "safe-buffer": "5.1.2" 69 | }, 70 | "engines": { 71 | "node": ">= 0.8" 72 | } 73 | }, 74 | "node_modules/basic-auth/node_modules/safe-buffer": { 75 | "version": "5.1.2", 76 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 77 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 78 | }, 79 | "node_modules/body-parser": { 80 | "version": "1.20.2", 81 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", 82 | "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", 83 | "dependencies": { 84 | "bytes": "3.1.2", 85 | "content-type": "~1.0.5", 86 | "debug": "2.6.9", 87 | "depd": "2.0.0", 88 | "destroy": "1.2.0", 89 | "http-errors": "2.0.0", 90 | "iconv-lite": "0.4.24", 91 | "on-finished": "2.4.1", 92 | "qs": "6.11.0", 93 | "raw-body": "2.5.2", 94 | "type-is": "~1.6.18", 95 | "unpipe": "1.0.0" 96 | }, 97 | "engines": { 98 | "node": ">= 0.8", 99 | "npm": "1.2.8000 || >= 1.4.16" 100 | } 101 | }, 102 | "node_modules/brace-expansion": { 103 | "version": "1.1.11", 104 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 105 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 106 | "dependencies": { 107 | "balanced-match": "^1.0.0", 108 | "concat-map": "0.0.1" 109 | } 110 | }, 111 | "node_modules/bytes": { 112 | "version": "3.1.2", 113 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 114 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", 115 | "engines": { 116 | "node": ">= 0.8" 117 | } 118 | }, 119 | "node_modules/call-bind": { 120 | "version": "1.0.2", 121 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", 122 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", 123 | "dependencies": { 124 | "function-bind": "^1.1.1", 125 | "get-intrinsic": "^1.0.2" 126 | }, 127 | "funding": { 128 | "url": "https://github.com/sponsors/ljharb" 129 | } 130 | }, 131 | "node_modules/chalk": { 132 | "version": "4.1.2", 133 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 134 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 135 | "dependencies": { 136 | "ansi-styles": "^4.1.0", 137 | "supports-color": "^7.1.0" 138 | }, 139 | "engines": { 140 | "node": ">=10" 141 | }, 142 | "funding": { 143 | "url": "https://github.com/chalk/chalk?sponsor=1" 144 | } 145 | }, 146 | "node_modules/color-convert": { 147 | "version": "2.0.1", 148 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 149 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 150 | "dependencies": { 151 | "color-name": "~1.1.4" 152 | }, 153 | "engines": { 154 | "node": ">=7.0.0" 155 | } 156 | }, 157 | "node_modules/color-name": { 158 | "version": "1.1.4", 159 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 160 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" 161 | }, 162 | "node_modules/concat-map": { 163 | "version": "0.0.1", 164 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 165 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" 166 | }, 167 | "node_modules/content-disposition": { 168 | "version": "0.5.4", 169 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", 170 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", 171 | "dependencies": { 172 | "safe-buffer": "5.2.1" 173 | }, 174 | "engines": { 175 | "node": ">= 0.6" 176 | } 177 | }, 178 | "node_modules/content-type": { 179 | "version": "1.0.5", 180 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", 181 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", 182 | "engines": { 183 | "node": ">= 0.6" 184 | } 185 | }, 186 | "node_modules/cookie": { 187 | "version": "0.5.0", 188 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", 189 | "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", 190 | "engines": { 191 | "node": ">= 0.6" 192 | } 193 | }, 194 | "node_modules/cookie-signature": { 195 | "version": "1.0.6", 196 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 197 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" 198 | }, 199 | "node_modules/debug": { 200 | "version": "2.6.9", 201 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 202 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 203 | "dependencies": { 204 | "ms": "2.0.0" 205 | } 206 | }, 207 | "node_modules/depd": { 208 | "version": "2.0.0", 209 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 210 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", 211 | "engines": { 212 | "node": ">= 0.8" 213 | } 214 | }, 215 | "node_modules/destroy": { 216 | "version": "1.2.0", 217 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", 218 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", 219 | "engines": { 220 | "node": ">= 0.8", 221 | "npm": "1.2.8000 || >= 1.4.16" 222 | } 223 | }, 224 | "node_modules/ee-first": { 225 | "version": "1.1.1", 226 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 227 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" 228 | }, 229 | "node_modules/ejs": { 230 | "version": "3.1.9", 231 | "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", 232 | "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", 233 | "dependencies": { 234 | "jake": "^10.8.5" 235 | }, 236 | "bin": { 237 | "ejs": "bin/cli.js" 238 | }, 239 | "engines": { 240 | "node": ">=0.10.0" 241 | } 242 | }, 243 | "node_modules/encodeurl": { 244 | "version": "1.0.2", 245 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 246 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", 247 | "engines": { 248 | "node": ">= 0.8" 249 | } 250 | }, 251 | "node_modules/escape-html": { 252 | "version": "1.0.3", 253 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 254 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" 255 | }, 256 | "node_modules/etag": { 257 | "version": "1.8.1", 258 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 259 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", 260 | "engines": { 261 | "node": ">= 0.6" 262 | } 263 | }, 264 | "node_modules/express": { 265 | "version": "4.18.2", 266 | "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", 267 | "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", 268 | "dependencies": { 269 | "accepts": "~1.3.8", 270 | "array-flatten": "1.1.1", 271 | "body-parser": "1.20.1", 272 | "content-disposition": "0.5.4", 273 | "content-type": "~1.0.4", 274 | "cookie": "0.5.0", 275 | "cookie-signature": "1.0.6", 276 | "debug": "2.6.9", 277 | "depd": "2.0.0", 278 | "encodeurl": "~1.0.2", 279 | "escape-html": "~1.0.3", 280 | "etag": "~1.8.1", 281 | "finalhandler": "1.2.0", 282 | "fresh": "0.5.2", 283 | "http-errors": "2.0.0", 284 | "merge-descriptors": "1.0.1", 285 | "methods": "~1.1.2", 286 | "on-finished": "2.4.1", 287 | "parseurl": "~1.3.3", 288 | "path-to-regexp": "0.1.7", 289 | "proxy-addr": "~2.0.7", 290 | "qs": "6.11.0", 291 | "range-parser": "~1.2.1", 292 | "safe-buffer": "5.2.1", 293 | "send": "0.18.0", 294 | "serve-static": "1.15.0", 295 | "setprototypeof": "1.2.0", 296 | "statuses": "2.0.1", 297 | "type-is": "~1.6.18", 298 | "utils-merge": "1.0.1", 299 | "vary": "~1.1.2" 300 | }, 301 | "engines": { 302 | "node": ">= 0.10.0" 303 | } 304 | }, 305 | "node_modules/express-basic-auth": { 306 | "version": "1.2.1", 307 | "resolved": "https://registry.npmjs.org/express-basic-auth/-/express-basic-auth-1.2.1.tgz", 308 | "integrity": "sha512-L6YQ1wQ/mNjVLAmK3AG1RK6VkokA1BIY6wmiH304Xtt/cLTps40EusZsU1Uop+v9lTDPxdtzbFmdXfFO3KEnwA==", 309 | "dependencies": { 310 | "basic-auth": "^2.0.1" 311 | } 312 | }, 313 | "node_modules/express-rate-limit": { 314 | "version": "6.7.0", 315 | "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.7.0.tgz", 316 | "integrity": "sha512-vhwIdRoqcYB/72TK3tRZI+0ttS8Ytrk24GfmsxDXK9o9IhHNO5bXRiXQSExPQ4GbaE5tvIS7j1SGrxsuWs+sGA==", 317 | "engines": { 318 | "node": ">= 12.9.0" 319 | }, 320 | "peerDependencies": { 321 | "express": "^4 || ^5" 322 | } 323 | }, 324 | "node_modules/express/node_modules/body-parser": { 325 | "version": "1.20.1", 326 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", 327 | "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", 328 | "dependencies": { 329 | "bytes": "3.1.2", 330 | "content-type": "~1.0.4", 331 | "debug": "2.6.9", 332 | "depd": "2.0.0", 333 | "destroy": "1.2.0", 334 | "http-errors": "2.0.0", 335 | "iconv-lite": "0.4.24", 336 | "on-finished": "2.4.1", 337 | "qs": "6.11.0", 338 | "raw-body": "2.5.1", 339 | "type-is": "~1.6.18", 340 | "unpipe": "1.0.0" 341 | }, 342 | "engines": { 343 | "node": ">= 0.8", 344 | "npm": "1.2.8000 || >= 1.4.16" 345 | } 346 | }, 347 | "node_modules/express/node_modules/raw-body": { 348 | "version": "2.5.1", 349 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", 350 | "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", 351 | "dependencies": { 352 | "bytes": "3.1.2", 353 | "http-errors": "2.0.0", 354 | "iconv-lite": "0.4.24", 355 | "unpipe": "1.0.0" 356 | }, 357 | "engines": { 358 | "node": ">= 0.8" 359 | } 360 | }, 361 | "node_modules/filelist": { 362 | "version": "1.0.4", 363 | "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", 364 | "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", 365 | "dependencies": { 366 | "minimatch": "^5.0.1" 367 | } 368 | }, 369 | "node_modules/filelist/node_modules/brace-expansion": { 370 | "version": "2.0.1", 371 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 372 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 373 | "dependencies": { 374 | "balanced-match": "^1.0.0" 375 | } 376 | }, 377 | "node_modules/filelist/node_modules/minimatch": { 378 | "version": "5.1.6", 379 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", 380 | "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", 381 | "dependencies": { 382 | "brace-expansion": "^2.0.1" 383 | }, 384 | "engines": { 385 | "node": ">=10" 386 | } 387 | }, 388 | "node_modules/finalhandler": { 389 | "version": "1.2.0", 390 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", 391 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", 392 | "dependencies": { 393 | "debug": "2.6.9", 394 | "encodeurl": "~1.0.2", 395 | "escape-html": "~1.0.3", 396 | "on-finished": "2.4.1", 397 | "parseurl": "~1.3.3", 398 | "statuses": "2.0.1", 399 | "unpipe": "~1.0.0" 400 | }, 401 | "engines": { 402 | "node": ">= 0.8" 403 | } 404 | }, 405 | "node_modules/forwarded": { 406 | "version": "0.2.0", 407 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 408 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", 409 | "engines": { 410 | "node": ">= 0.6" 411 | } 412 | }, 413 | "node_modules/fresh": { 414 | "version": "0.5.2", 415 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 416 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", 417 | "engines": { 418 | "node": ">= 0.6" 419 | } 420 | }, 421 | "node_modules/function-bind": { 422 | "version": "1.1.1", 423 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 424 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 425 | }, 426 | "node_modules/get-intrinsic": { 427 | "version": "1.2.1", 428 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", 429 | "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", 430 | "dependencies": { 431 | "function-bind": "^1.1.1", 432 | "has": "^1.0.3", 433 | "has-proto": "^1.0.1", 434 | "has-symbols": "^1.0.3" 435 | }, 436 | "funding": { 437 | "url": "https://github.com/sponsors/ljharb" 438 | } 439 | }, 440 | "node_modules/graceful-fs": { 441 | "version": "4.2.11", 442 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", 443 | "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" 444 | }, 445 | "node_modules/has": { 446 | "version": "1.0.3", 447 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 448 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 449 | "dependencies": { 450 | "function-bind": "^1.1.1" 451 | }, 452 | "engines": { 453 | "node": ">= 0.4.0" 454 | } 455 | }, 456 | "node_modules/has-flag": { 457 | "version": "4.0.0", 458 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 459 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 460 | "engines": { 461 | "node": ">=8" 462 | } 463 | }, 464 | "node_modules/has-proto": { 465 | "version": "1.0.1", 466 | "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", 467 | "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", 468 | "engines": { 469 | "node": ">= 0.4" 470 | }, 471 | "funding": { 472 | "url": "https://github.com/sponsors/ljharb" 473 | } 474 | }, 475 | "node_modules/has-symbols": { 476 | "version": "1.0.3", 477 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 478 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", 479 | "engines": { 480 | "node": ">= 0.4" 481 | }, 482 | "funding": { 483 | "url": "https://github.com/sponsors/ljharb" 484 | } 485 | }, 486 | "node_modules/http-errors": { 487 | "version": "2.0.0", 488 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 489 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 490 | "dependencies": { 491 | "depd": "2.0.0", 492 | "inherits": "2.0.4", 493 | "setprototypeof": "1.2.0", 494 | "statuses": "2.0.1", 495 | "toidentifier": "1.0.1" 496 | }, 497 | "engines": { 498 | "node": ">= 0.8" 499 | } 500 | }, 501 | "node_modules/iconv-lite": { 502 | "version": "0.4.24", 503 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 504 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 505 | "dependencies": { 506 | "safer-buffer": ">= 2.1.2 < 3" 507 | }, 508 | "engines": { 509 | "node": ">=0.10.0" 510 | } 511 | }, 512 | "node_modules/imurmurhash": { 513 | "version": "0.1.4", 514 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 515 | "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", 516 | "engines": { 517 | "node": ">=0.8.19" 518 | } 519 | }, 520 | "node_modules/inherits": { 521 | "version": "2.0.4", 522 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 523 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 524 | }, 525 | "node_modules/ipaddr.js": { 526 | "version": "1.9.1", 527 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 528 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", 529 | "engines": { 530 | "node": ">= 0.10" 531 | } 532 | }, 533 | "node_modules/jake": { 534 | "version": "10.8.7", 535 | "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", 536 | "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", 537 | "dependencies": { 538 | "async": "^3.2.3", 539 | "chalk": "^4.0.2", 540 | "filelist": "^1.0.4", 541 | "minimatch": "^3.1.2" 542 | }, 543 | "bin": { 544 | "jake": "bin/cli.js" 545 | }, 546 | "engines": { 547 | "node": ">=10" 548 | } 549 | }, 550 | "node_modules/media-typer": { 551 | "version": "0.3.0", 552 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 553 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", 554 | "engines": { 555 | "node": ">= 0.6" 556 | } 557 | }, 558 | "node_modules/merge-descriptors": { 559 | "version": "1.0.1", 560 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 561 | "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" 562 | }, 563 | "node_modules/methods": { 564 | "version": "1.1.2", 565 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 566 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", 567 | "engines": { 568 | "node": ">= 0.6" 569 | } 570 | }, 571 | "node_modules/mime": { 572 | "version": "1.6.0", 573 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 574 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 575 | "bin": { 576 | "mime": "cli.js" 577 | }, 578 | "engines": { 579 | "node": ">=4" 580 | } 581 | }, 582 | "node_modules/mime-db": { 583 | "version": "1.52.0", 584 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 585 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 586 | "engines": { 587 | "node": ">= 0.6" 588 | } 589 | }, 590 | "node_modules/mime-types": { 591 | "version": "2.1.35", 592 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 593 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 594 | "dependencies": { 595 | "mime-db": "1.52.0" 596 | }, 597 | "engines": { 598 | "node": ">= 0.6" 599 | } 600 | }, 601 | "node_modules/minimatch": { 602 | "version": "3.1.2", 603 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 604 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 605 | "dependencies": { 606 | "brace-expansion": "^1.1.7" 607 | }, 608 | "engines": { 609 | "node": "*" 610 | } 611 | }, 612 | "node_modules/ms": { 613 | "version": "2.0.0", 614 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 615 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" 616 | }, 617 | "node_modules/negotiator": { 618 | "version": "0.6.3", 619 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 620 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", 621 | "engines": { 622 | "node": ">= 0.6" 623 | } 624 | }, 625 | "node_modules/node-localstorage": { 626 | "version": "2.2.1", 627 | "resolved": "https://registry.npmjs.org/node-localstorage/-/node-localstorage-2.2.1.tgz", 628 | "integrity": "sha512-vv8fJuOUCCvSPjDjBLlMqYMHob4aGjkmrkaE42/mZr0VT+ZAU10jRF8oTnX9+pgU9/vYJ8P7YT3Vd6ajkmzSCw==", 629 | "dependencies": { 630 | "write-file-atomic": "^1.1.4" 631 | }, 632 | "engines": { 633 | "node": ">=0.12" 634 | } 635 | }, 636 | "node_modules/object-inspect": { 637 | "version": "1.12.3", 638 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", 639 | "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", 640 | "funding": { 641 | "url": "https://github.com/sponsors/ljharb" 642 | } 643 | }, 644 | "node_modules/on-finished": { 645 | "version": "2.4.1", 646 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", 647 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 648 | "dependencies": { 649 | "ee-first": "1.1.1" 650 | }, 651 | "engines": { 652 | "node": ">= 0.8" 653 | } 654 | }, 655 | "node_modules/parseurl": { 656 | "version": "1.3.3", 657 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 658 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 659 | "engines": { 660 | "node": ">= 0.8" 661 | } 662 | }, 663 | "node_modules/passport": { 664 | "version": "0.6.0", 665 | "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz", 666 | "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==", 667 | "dependencies": { 668 | "passport-strategy": "1.x.x", 669 | "pause": "0.0.1", 670 | "utils-merge": "^1.0.1" 671 | }, 672 | "engines": { 673 | "node": ">= 0.4.0" 674 | }, 675 | "funding": { 676 | "type": "github", 677 | "url": "https://github.com/sponsors/jaredhanson" 678 | } 679 | }, 680 | "node_modules/passport-http-bearer": { 681 | "version": "1.0.1", 682 | "resolved": "https://registry.npmjs.org/passport-http-bearer/-/passport-http-bearer-1.0.1.tgz", 683 | "integrity": "sha512-SELQM+dOTuMigr9yu8Wo4Fm3ciFfkMq5h/ZQ8ffi4ELgZrX1xh9PlglqZdcUZ1upzJD/whVyt+YWF62s3U6Ipw==", 684 | "dependencies": { 685 | "passport-strategy": "1.x.x" 686 | }, 687 | "engines": { 688 | "node": ">= 0.4.0" 689 | } 690 | }, 691 | "node_modules/passport-strategy": { 692 | "version": "1.0.0", 693 | "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", 694 | "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", 695 | "engines": { 696 | "node": ">= 0.4.0" 697 | } 698 | }, 699 | "node_modules/path-to-regexp": { 700 | "version": "0.1.7", 701 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 702 | "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" 703 | }, 704 | "node_modules/pause": { 705 | "version": "0.0.1", 706 | "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", 707 | "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" 708 | }, 709 | "node_modules/proxy-addr": { 710 | "version": "2.0.7", 711 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 712 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 713 | "dependencies": { 714 | "forwarded": "0.2.0", 715 | "ipaddr.js": "1.9.1" 716 | }, 717 | "engines": { 718 | "node": ">= 0.10" 719 | } 720 | }, 721 | "node_modules/qs": { 722 | "version": "6.11.0", 723 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", 724 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", 725 | "dependencies": { 726 | "side-channel": "^1.0.4" 727 | }, 728 | "engines": { 729 | "node": ">=0.6" 730 | }, 731 | "funding": { 732 | "url": "https://github.com/sponsors/ljharb" 733 | } 734 | }, 735 | "node_modules/range-parser": { 736 | "version": "1.2.1", 737 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 738 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 739 | "engines": { 740 | "node": ">= 0.6" 741 | } 742 | }, 743 | "node_modules/raw-body": { 744 | "version": "2.5.2", 745 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", 746 | "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", 747 | "dependencies": { 748 | "bytes": "3.1.2", 749 | "http-errors": "2.0.0", 750 | "iconv-lite": "0.4.24", 751 | "unpipe": "1.0.0" 752 | }, 753 | "engines": { 754 | "node": ">= 0.8" 755 | } 756 | }, 757 | "node_modules/safe-buffer": { 758 | "version": "5.2.1", 759 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 760 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 761 | "funding": [ 762 | { 763 | "type": "github", 764 | "url": "https://github.com/sponsors/feross" 765 | }, 766 | { 767 | "type": "patreon", 768 | "url": "https://www.patreon.com/feross" 769 | }, 770 | { 771 | "type": "consulting", 772 | "url": "https://feross.org/support" 773 | } 774 | ] 775 | }, 776 | "node_modules/safer-buffer": { 777 | "version": "2.1.2", 778 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 779 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 780 | }, 781 | "node_modules/send": { 782 | "version": "0.18.0", 783 | "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", 784 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", 785 | "dependencies": { 786 | "debug": "2.6.9", 787 | "depd": "2.0.0", 788 | "destroy": "1.2.0", 789 | "encodeurl": "~1.0.2", 790 | "escape-html": "~1.0.3", 791 | "etag": "~1.8.1", 792 | "fresh": "0.5.2", 793 | "http-errors": "2.0.0", 794 | "mime": "1.6.0", 795 | "ms": "2.1.3", 796 | "on-finished": "2.4.1", 797 | "range-parser": "~1.2.1", 798 | "statuses": "2.0.1" 799 | }, 800 | "engines": { 801 | "node": ">= 0.8.0" 802 | } 803 | }, 804 | "node_modules/send/node_modules/ms": { 805 | "version": "2.1.3", 806 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 807 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 808 | }, 809 | "node_modules/serve-static": { 810 | "version": "1.15.0", 811 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", 812 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", 813 | "dependencies": { 814 | "encodeurl": "~1.0.2", 815 | "escape-html": "~1.0.3", 816 | "parseurl": "~1.3.3", 817 | "send": "0.18.0" 818 | }, 819 | "engines": { 820 | "node": ">= 0.8.0" 821 | } 822 | }, 823 | "node_modules/setprototypeof": { 824 | "version": "1.2.0", 825 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 826 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" 827 | }, 828 | "node_modules/side-channel": { 829 | "version": "1.0.4", 830 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", 831 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", 832 | "dependencies": { 833 | "call-bind": "^1.0.0", 834 | "get-intrinsic": "^1.0.2", 835 | "object-inspect": "^1.9.0" 836 | }, 837 | "funding": { 838 | "url": "https://github.com/sponsors/ljharb" 839 | } 840 | }, 841 | "node_modules/slide": { 842 | "version": "1.1.6", 843 | "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", 844 | "integrity": "sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw==", 845 | "engines": { 846 | "node": "*" 847 | } 848 | }, 849 | "node_modules/statuses": { 850 | "version": "2.0.1", 851 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 852 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", 853 | "engines": { 854 | "node": ">= 0.8" 855 | } 856 | }, 857 | "node_modules/supports-color": { 858 | "version": "7.2.0", 859 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 860 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 861 | "dependencies": { 862 | "has-flag": "^4.0.0" 863 | }, 864 | "engines": { 865 | "node": ">=8" 866 | } 867 | }, 868 | "node_modules/toidentifier": { 869 | "version": "1.0.1", 870 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 871 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", 872 | "engines": { 873 | "node": ">=0.6" 874 | } 875 | }, 876 | "node_modules/type-is": { 877 | "version": "1.6.18", 878 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 879 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 880 | "dependencies": { 881 | "media-typer": "0.3.0", 882 | "mime-types": "~2.1.24" 883 | }, 884 | "engines": { 885 | "node": ">= 0.6" 886 | } 887 | }, 888 | "node_modules/unpipe": { 889 | "version": "1.0.0", 890 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 891 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", 892 | "engines": { 893 | "node": ">= 0.8" 894 | } 895 | }, 896 | "node_modules/utils-merge": { 897 | "version": "1.0.1", 898 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 899 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", 900 | "engines": { 901 | "node": ">= 0.4.0" 902 | } 903 | }, 904 | "node_modules/vary": { 905 | "version": "1.1.2", 906 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 907 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", 908 | "engines": { 909 | "node": ">= 0.8" 910 | } 911 | }, 912 | "node_modules/write-file-atomic": { 913 | "version": "1.3.4", 914 | "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", 915 | "integrity": "sha512-SdrHoC/yVBPpV0Xq/mUZQIpW2sWXAShb/V4pomcJXh92RuaO+f3UTWItiR3Px+pLnV2PvC2/bfn5cwr5X6Vfxw==", 916 | "dependencies": { 917 | "graceful-fs": "^4.1.11", 918 | "imurmurhash": "^0.1.4", 919 | "slide": "^1.1.5" 920 | } 921 | } 922 | } 923 | } 924 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "secrets-api", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "body-parser": "^1.20.2", 14 | "ejs": "^3.1.9", 15 | "express": "^4.18.2", 16 | "express-basic-auth": "^1.2.1", 17 | "express-rate-limit": "^6.7.0", 18 | "node-localstorage": "^2.2.1", 19 | "passport": "^0.6.0", 20 | "passport-http-bearer": "^1.0.1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /views/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Secrets API Documentation 6 | 87 | 88 | 89 | 90 |

Secrets API Documentation

91 |

Welcome to the Secrets API. This API allows you to manage and retrieve secrets anonymously. Please refer to the 92 | documentation below for details on how to interact with the API.

93 |
  • The API is rate limited to 100 requests every 15 minutes.
  • 94 |
  • All user submitted data (including registration, tokens, 95 | usernames, passwords, secrets) are erased on a regular basis.
  • 96 |
  • This API is just for education purposes, please don't 97 | rely on it for production.
  • 98 | 99 |

    Authentication

    100 | 111 |
    112 |
    113 |

    POST /register

    114 |

    Registers a new user. If the username is already taken, it will return an error.

    115 |

    Request Body:

    116 |
    {
    117 |   "username": "The username of the new user.",
    118 |   "password": "The password of the new user."
    119 | }
    120 |
    121 |

    Example Request:

    122 |
    123 | POST https://secrets-api.appbrewery.com/register 124 |
    {
    125 |   "username": "jackbauer",
    126 |   "password": "IAmTheBest"
    127 | }
    128 |
    129 |

    Example Response:

    130 |
    131 |
    {
    132 |   "success": "Successfully registered."
    133 | }
    134 |
    135 |
    136 |
    137 | 138 |
    139 |

    GET /generate-api-key

    140 |

    Generates a new API key.

    141 |
    142 |

    Example Request:

    143 |
    144 | GET https://secrets-api.appbrewery.com/generate-api-key 145 |
    146 |

    Example Response:

    147 |
    148 |
    {
    149 |   "apiKey": "generated-api-key"
    150 | }
    151 |
    152 |
    153 |
    154 | 155 |
    156 |

    POST /get-auth-token

    157 |

    Generates an authentication token for a user. If the user does not exist or the password is incorrect, it will 158 | return an error.

    159 |

    Request Body:

    160 |
    {
    161 |   "username": "The username of the registered user.",
    162 |   "password": "The password of the registered user."
    163 | }
    164 |
    165 |

    Example Request:

    166 |
    167 | POST https://secrets-api.appbrewery.com/get-auth-token 168 |
    {
    169 |   "username": "jackbauer",
    170 |   "password": "IAmTheBest"
    171 | }
    172 |
    173 |

    Example Response:

    174 |
    175 |
    {
    176 |   "token": "generated-auth-token"
    177 | }
    178 |
    179 |
    180 |
    181 |
    182 |

    REST API

    183 | 184 |
    185 |

    GET /random

    186 |

    Returns a random secret. No authentication is required.

    187 |
    188 |

    Example Request:

    189 |
    190 | GET https://secrets-api.appbrewery.com/random 191 |
    192 |

    Example Response:

    193 |
    194 |
    {
    195 |     "id": "random-id",
    196 |     "secret": "This is a random secret.",
    197 |     "emScore": 3,
    198 |     "username": "user123",
    199 |     "timestamp": "2022-10-01T12:34:56Z"
    200 | }
    201 |
    202 |
    203 |
    204 | 205 |
    206 |

    GET /all

    207 |

    Returns all secrets, paginated. Basic authentication is required.

    208 |

    Query Parameters:

    209 | 212 |
    213 |

    Example Request:

    214 |
    215 | GET https://secrets-api.appbrewery.com/all?page=1 216 |
    217 |

    Example Response:

    218 |
    219 |
    [
    220 |     {
    221 |       "id": "1",
    222 |       "secret": "This is a secret.",
    223 |       "emScore": 3,
    224 |       "username": "user123",
    225 |       "timestamp": "2022-10-01T12:34:56Z"
    226 |     },
    227 |     {
    228 |       "id": "2",
    229 |       "secret": "Another secret.",
    230 |       "emScore": 3,
    231 |       "username": "user123",
    232 |       "timestamp": "2022-10-02T10:11:12Z"
    233 |     }
    234 |     // ... more secrets ...
    235 |   ]
    236 |
    237 |
    238 |
    239 | 240 |
    241 |

    GET /filter

    242 |

    Returns a random secret with a particular embarrassment score or higher. API key authentication is required.

    243 |

    Query Parameters:

    244 | 248 |
    249 |

    Example Request:

    250 |
    251 | GET https://secrets-api.appbrewery.com/filter?score=5&apiKey=b886c845-9989-43aa-8c60-ea4a669bb587 252 |
    253 |

    Example Response:

    254 |
    255 |
    [
    256 |     {
    257 |       "id": "1",
    258 |       "secret": "This is a secret with embarrassment score 5 or higher.",
    259 |       "emScore": 5,
    260 |       "username": "user123",
    261 |       "timestamp": "2022-10-01T12:34:56Z"
    262 |     },
    263 |     {
    264 |       "id": "2",
    265 |       "secret": "Another secret with embarrassment score 5 or higher.",
    266 |       "emScore": 7,
    267 |       "username": "user123",
    268 |       "timestamp": "2022-10-02T10:11:12Z"
    269 |     }
    270 |     // ... more secrets ...
    271 |   ]
    272 |
    273 |
    274 |
    275 | 276 |
    277 |

    GET /user-secrets

    278 |

    Returns all the secrets of the authenticated user. Bearer token authentication is required.

    279 |
    280 |

    Example Request:

    281 |
    282 | GET https://secrets-api.appbrewery.com/user-secrets 283 |
    284 |

    Example Response:

    285 |
    286 |
    [
    287 |     {
    288 |       "id": "1",
    289 |       "secret": "This is a secret of the user.",
    290 |       "emScore": 3,
    291 |       "username": "user123",
    292 |       "timestamp": "2022-10-01T12:34:56Z"
    293 |     },
    294 |     {
    295 |       "id": "2",
    296 |       "secret": "Another secret of the user.",
    297 |       "emScore": 3,
    298 |       "username": "user123",
    299 |       "timestamp": "2022-10-02T10:11:12Z"
    300 |     }
    301 |     // ... more secrets ...
    302 |   ]
    303 |
    304 |
    305 |
    306 | 307 | 308 |
    309 |

    GET /secrets/{id}

    310 |

    Returns the secret with the specified ID. Bearer token authentication is required.

    311 |

    URL Parameters:

    312 | 315 |
    316 |

    Example Request:

    317 |
    318 | GET https://secrets-api.appbrewery.com/secrets/1 319 |
    320 |

    Example Response:

    321 |
    322 |
    {
    323 |   "id": "1",
    324 |   "secret": "This is a secret.",
    325 |   "emScore": 3,
    326 |   "username": "user123",
    327 |   "timestamp": "2022-10-01T12:34:56Z"
    328 | }
    329 |
    330 |
    331 |
    332 | 333 |
    334 |

    POST /secrets

    335 |

    Adds a new secret. Bearer token authentication is required.

    336 |

    Request Body:

    337 |
    {
    338 |   "secret": "This is a new secret.",
    339 |   "score": "Embarrassment score"
    340 | }
    341 |
    342 |

    Example Request:

    343 |
    344 | POST https://secrets-api.appbrewery.com/secrets 345 |
    {
    346 |   "secret": "This is a new secret.",
    347 |   "score": "Updated embarrassment score"
    348 | }
    349 |
    350 |

    Example Response:

    351 |
    352 |
    {
    353 |   "id": "3",
    354 |   "secret": "This is a new secret.",
    355 |   "emScore": 3,
    356 |   "username": "user123",
    357 |   "timestamp": "2022-10-03T08:15:00Z"
    358 | }
    359 |
    360 |
    361 |
    362 | 363 |
    364 |

    PUT /secrets/{id}

    365 |

    Updates the content of the secret with the specified ID. Bearer token authentication is required.

    366 |

    URL Parameters:

    367 | 370 |

    Request Body:

    371 |
    {
    372 |   "secret": "Updated secret content.",
    373 |   "score": 5
    374 | }
    375 |
    376 |

    Example Request:

    377 |
    378 | PUT https://secrets-api.appbrewery.com/secrets/1 379 |
    {
    380 |   "secret": "Updated secret content",
    381 |   "score": "Updated embarrassment score"
    382 | }
    383 |
    384 |

    Example Response:

    385 |
    386 |
    {
    387 |   "id": "1",
    388 |   "secret": "Updated secret content.",
    389 |   "emScore": 3,
    390 |   "username": "user123",
    391 |   "timestamp": "2022-10-01T12:34:56Z"
    392 | }
    393 |
    394 |
    395 |
    396 |
    397 |

    PATCH /secrets/{id}

    398 |

    Partially updates the content of the secret with the specified ID. Bearer token authentication is required.

    399 |

    URL Parameters:

    400 | 403 |

    Request Body:

    404 |
    {
    405 |   "secret": "Updated secret content",
    406 |   "score": "Updated embarrassment score"
    407 | }
    408 |
    409 |

    Example Request:

    410 |
    411 | PATCH https://secrets-api.appbrewery.com/secrets/1 412 |
    {
    413 |   "score": 2
    414 | }
    415 |
    416 |

    Example Response:

    417 |
    418 |
    {
    419 |   "id": "1",
    420 |   "secret": "Partially updated secret content.",
    421 |   "emScore": 3,
    422 |   "username": "user123",
    423 |   "timestamp": "2022-10-01T12:34:56Z"
    424 | }
    425 |
    426 |
    427 |
    428 | 429 |
    430 |

    DELETE /secrets/{id}

    431 |

    Deletes the secret with the specified ID. Bearer token authentication is required.

    432 |

    URL Parameters:

    433 | 436 |
    437 |

    Example Request:

    438 |
    439 | DELETE https://secrets-api.appbrewery.com/secrets/1 440 |
    441 |

    Example Response:

    442 |
    443 |
    {
    444 |   "message": "Secret with ID 1 has been deleted successfully."
    445 | }
    446 |
    447 |
    448 |
    449 | 450 | 451 | 454 | 455 | --------------------------------------------------------------------------------