From fa4483c1dbd59bda4670e475e5ac3bc467577120 Mon Sep 17 00:00:00 2001 From: sneedmaster Date: Sun, 28 Sep 2025 12:28:02 +0200 Subject: [PATCH] Probably a good idea to have a git repo actually --- .DS_Store | Bin 0 -> 6148 bytes .gitignore | 8 + .prettierrc | 6 + .vscode/settings.json | 5 + LICENSE.txt | 661 +++++ README.md | 3 + config/board_defaults.json | 1 + config/locale/countries.json | 251 ++ config/locale/meme_flags.json | 1 + config/locale/permissions.json | 33 + config/roles.json | 93 + package-lock.json | 2741 ++++++++++++++++++ package.json | 52 + public/assets/capcode_icons/admin.png | Bin 0 -> 1539 bytes public/assets/capcode_icons/janny.png | Bin 0 -> 1146 bytes public/assets/capcode_icons/manager.png | Bin 0 -> 1444 bytes public/assets/capcode_icons/mod.png | Bin 0 -> 1133 bytes public/assets/capcode_icons/system.png | Bin 0 -> 1670 bytes public/favicon.ico | Bin 0 -> 4286 bytes public/img/country_flags/ad.png | Bin 0 -> 643 bytes public/img/country_flags/ae.png | Bin 0 -> 408 bytes public/img/country_flags/af.png | Bin 0 -> 604 bytes public/img/country_flags/ag.png | Bin 0 -> 591 bytes public/img/country_flags/ai.png | Bin 0 -> 643 bytes public/img/country_flags/al.png | Bin 0 -> 600 bytes public/img/country_flags/am.png | Bin 0 -> 497 bytes public/img/country_flags/an.png | Bin 0 -> 488 bytes public/img/country_flags/ao.png | Bin 0 -> 428 bytes public/img/country_flags/ar.png | Bin 0 -> 506 bytes public/img/country_flags/as.png | Bin 0 -> 647 bytes public/img/country_flags/at.png | Bin 0 -> 403 bytes public/img/country_flags/au.png | Bin 0 -> 673 bytes public/img/country_flags/aw.png | Bin 0 -> 524 bytes public/img/country_flags/ax.png | Bin 0 -> 663 bytes public/img/country_flags/az.png | Bin 0 -> 589 bytes public/img/country_flags/ba.png | Bin 0 -> 593 bytes public/img/country_flags/bb.png | Bin 0 -> 585 bytes public/img/country_flags/bd.png | Bin 0 -> 504 bytes public/img/country_flags/be.png | Bin 0 -> 449 bytes public/img/country_flags/bf.png | Bin 0 -> 497 bytes public/img/country_flags/bg.png | Bin 0 -> 462 bytes public/img/country_flags/bh.png | Bin 0 -> 457 bytes public/img/country_flags/bi.png | Bin 0 -> 675 bytes public/img/country_flags/bj.png | Bin 0 -> 486 bytes public/img/country_flags/bm.png | Bin 0 -> 611 bytes public/img/country_flags/bn.png | Bin 0 -> 639 bytes public/img/country_flags/bo.png | Bin 0 -> 500 bytes public/img/country_flags/br.png | Bin 0 -> 593 bytes public/img/country_flags/bs.png | Bin 0 -> 526 bytes public/img/country_flags/bt.png | Bin 0 -> 631 bytes public/img/country_flags/bv.png | Bin 0 -> 512 bytes public/img/country_flags/bw.png | Bin 0 -> 443 bytes public/img/country_flags/by.png | Bin 0 -> 514 bytes public/img/country_flags/bz.png | Bin 0 -> 600 bytes public/img/country_flags/ca.png | Bin 0 -> 628 bytes public/img/country_flags/cc.png | Bin 0 -> 625 bytes public/img/country_flags/cd.png | Bin 0 -> 528 bytes public/img/country_flags/cf.png | Bin 0 -> 614 bytes public/img/country_flags/cg.png | Bin 0 -> 521 bytes public/img/country_flags/ch.png | Bin 0 -> 367 bytes public/img/country_flags/ci.png | Bin 0 -> 453 bytes public/img/country_flags/ck.png | Bin 0 -> 586 bytes public/img/country_flags/cl.png | Bin 0 -> 450 bytes public/img/country_flags/cm.png | Bin 0 -> 525 bytes public/img/country_flags/cn.png | Bin 0 -> 472 bytes public/img/country_flags/co.png | Bin 0 -> 483 bytes public/img/country_flags/cr.png | Bin 0 -> 477 bytes public/img/country_flags/cs.png | Bin 0 -> 439 bytes public/img/country_flags/cu.png | Bin 0 -> 563 bytes public/img/country_flags/cv.png | Bin 0 -> 529 bytes public/img/country_flags/cx.png | Bin 0 -> 608 bytes public/img/country_flags/cy.png | Bin 0 -> 428 bytes public/img/country_flags/cz.png | Bin 0 -> 476 bytes public/img/country_flags/de.png | Bin 0 -> 545 bytes public/img/country_flags/dj.png | Bin 0 -> 572 bytes public/img/country_flags/dk.png | Bin 0 -> 495 bytes public/img/country_flags/dm.png | Bin 0 -> 620 bytes public/img/country_flags/do.png | Bin 0 -> 508 bytes public/img/country_flags/dz.png | Bin 0 -> 582 bytes public/img/country_flags/ec.png | Bin 0 -> 500 bytes public/img/country_flags/ee.png | Bin 0 -> 429 bytes public/img/country_flags/eg.png | Bin 0 -> 465 bytes public/img/country_flags/eh.png | Bin 0 -> 508 bytes public/img/country_flags/er.png | Bin 0 -> 653 bytes public/img/country_flags/es.png | Bin 0 -> 469 bytes public/img/country_flags/et.png | Bin 0 -> 592 bytes public/img/country_flags/fam.png | Bin 0 -> 532 bytes public/img/country_flags/fi.png | Bin 0 -> 489 bytes public/img/country_flags/fj.png | Bin 0 -> 610 bytes public/img/country_flags/fk.png | Bin 0 -> 648 bytes public/img/country_flags/fm.png | Bin 0 -> 552 bytes public/img/country_flags/fo.png | Bin 0 -> 474 bytes public/img/country_flags/fr.png | Bin 0 -> 545 bytes public/img/country_flags/ga.png | Bin 0 -> 489 bytes public/img/country_flags/gb.png | Bin 0 -> 599 bytes public/img/country_flags/gd.png | Bin 0 -> 637 bytes public/img/country_flags/ge.png | Bin 0 -> 594 bytes public/img/country_flags/gf.png | Bin 0 -> 545 bytes public/img/country_flags/gh.png | Bin 0 -> 490 bytes public/img/country_flags/gi.png | Bin 0 -> 463 bytes public/img/country_flags/gl.png | Bin 0 -> 470 bytes public/img/country_flags/gm.png | Bin 0 -> 493 bytes public/img/country_flags/gn.png | Bin 0 -> 480 bytes public/img/country_flags/gp.png | Bin 0 -> 488 bytes public/img/country_flags/gq.png | Bin 0 -> 537 bytes public/img/country_flags/gr.png | Bin 0 -> 487 bytes public/img/country_flags/gs.png | Bin 0 -> 630 bytes public/img/country_flags/gt.png | Bin 0 -> 493 bytes public/img/country_flags/gu.png | Bin 0 -> 509 bytes public/img/country_flags/gw.png | Bin 0 -> 516 bytes public/img/country_flags/gy.png | Bin 0 -> 645 bytes public/img/country_flags/hk.png | Bin 0 -> 527 bytes public/img/country_flags/hm.png | Bin 0 -> 673 bytes public/img/country_flags/hn.png | Bin 0 -> 537 bytes public/img/country_flags/hr.png | Bin 0 -> 524 bytes public/img/country_flags/ht.png | Bin 0 -> 487 bytes public/img/country_flags/hu.png | Bin 0 -> 432 bytes public/img/country_flags/id.png | Bin 0 -> 430 bytes public/img/country_flags/ie.png | Bin 0 -> 481 bytes public/img/country_flags/il.png | Bin 0 -> 431 bytes public/img/country_flags/in.png | Bin 0 -> 503 bytes public/img/country_flags/io.png | Bin 0 -> 658 bytes public/img/country_flags/iq.png | Bin 0 -> 515 bytes public/img/country_flags/ir.png | Bin 0 -> 512 bytes public/img/country_flags/is.png | Bin 0 -> 532 bytes public/img/country_flags/it.png | Bin 0 -> 420 bytes public/img/country_flags/jm.png | Bin 0 -> 637 bytes public/img/country_flags/jo.png | Bin 0 -> 473 bytes public/img/country_flags/jp.png | Bin 0 -> 420 bytes public/img/country_flags/ke.png | Bin 0 -> 569 bytes public/img/country_flags/kg.png | Bin 0 -> 510 bytes public/img/country_flags/kh.png | Bin 0 -> 549 bytes public/img/country_flags/ki.png | Bin 0 -> 656 bytes public/img/country_flags/km.png | Bin 0 -> 577 bytes public/img/country_flags/kn.png | Bin 0 -> 604 bytes public/img/country_flags/kp.png | Bin 0 -> 561 bytes public/img/country_flags/kr.png | Bin 0 -> 592 bytes public/img/country_flags/kw.png | Bin 0 -> 486 bytes public/img/country_flags/ky.png | Bin 0 -> 643 bytes public/img/country_flags/kz.png | Bin 0 -> 616 bytes public/img/country_flags/la.png | Bin 0 -> 563 bytes public/img/country_flags/lb.png | Bin 0 -> 517 bytes public/img/country_flags/lc.png | Bin 0 -> 520 bytes public/img/country_flags/li.png | Bin 0 -> 537 bytes public/img/country_flags/lk.png | Bin 0 -> 627 bytes public/img/country_flags/lr.png | Bin 0 -> 466 bytes public/img/country_flags/ls.png | Bin 0 -> 628 bytes public/img/country_flags/lt.png | Bin 0 -> 508 bytes public/img/country_flags/lu.png | Bin 0 -> 481 bytes public/img/country_flags/lv.png | Bin 0 -> 465 bytes public/img/country_flags/ly.png | Bin 0 -> 419 bytes public/img/country_flags/ma.png | Bin 0 -> 432 bytes public/img/country_flags/mc.png | Bin 0 -> 380 bytes public/img/country_flags/md.png | Bin 0 -> 566 bytes public/img/country_flags/me.png | Bin 0 -> 448 bytes public/img/country_flags/mg.png | Bin 0 -> 453 bytes public/img/country_flags/mh.png | Bin 0 -> 628 bytes public/img/country_flags/mk.png | Bin 0 -> 664 bytes public/img/country_flags/ml.png | Bin 0 -> 474 bytes public/img/country_flags/mm.png | Bin 0 -> 483 bytes public/img/country_flags/mn.png | Bin 0 -> 492 bytes public/img/country_flags/mo.png | Bin 0 -> 588 bytes public/img/country_flags/mp.png | Bin 0 -> 597 bytes public/img/country_flags/mq.png | Bin 0 -> 655 bytes public/img/country_flags/mr.png | Bin 0 -> 569 bytes public/img/country_flags/ms.png | Bin 0 -> 614 bytes public/img/country_flags/mt.png | Bin 0 -> 420 bytes public/img/country_flags/mu.png | Bin 0 -> 496 bytes public/img/country_flags/mv.png | Bin 0 -> 542 bytes public/img/country_flags/mw.png | Bin 0 -> 529 bytes public/img/country_flags/mx.png | Bin 0 -> 574 bytes public/img/country_flags/my.png | Bin 0 -> 571 bytes public/img/country_flags/mz.png | Bin 0 -> 584 bytes public/img/country_flags/na.png | Bin 0 -> 647 bytes public/img/country_flags/nc.png | Bin 0 -> 591 bytes public/img/country_flags/ne.png | Bin 0 -> 537 bytes public/img/country_flags/nf.png | Bin 0 -> 602 bytes public/img/country_flags/ng.png | Bin 0 -> 482 bytes public/img/country_flags/ni.png | Bin 0 -> 508 bytes public/img/country_flags/nl.png | Bin 0 -> 453 bytes public/img/country_flags/no.png | Bin 0 -> 512 bytes public/img/country_flags/np.png | Bin 0 -> 443 bytes public/img/country_flags/nr.png | Bin 0 -> 527 bytes public/img/country_flags/nu.png | Bin 0 -> 572 bytes public/img/country_flags/nz.png | Bin 0 -> 639 bytes public/img/country_flags/om.png | Bin 0 -> 478 bytes public/img/country_flags/pa.png | Bin 0 -> 519 bytes public/img/country_flags/pe.png | Bin 0 -> 397 bytes public/img/country_flags/pf.png | Bin 0 -> 498 bytes public/img/country_flags/pg.png | Bin 0 -> 593 bytes public/img/country_flags/ph.png | Bin 0 -> 538 bytes public/img/country_flags/pk.png | Bin 0 -> 569 bytes public/img/country_flags/pl.png | Bin 0 -> 374 bytes public/img/country_flags/pm.png | Bin 0 -> 689 bytes public/img/country_flags/pn.png | Bin 0 -> 657 bytes public/img/country_flags/pr.png | Bin 0 -> 556 bytes public/img/country_flags/ps.png | Bin 0 -> 472 bytes public/img/country_flags/pt.png | Bin 0 -> 554 bytes public/img/country_flags/pw.png | Bin 0 -> 550 bytes public/img/country_flags/py.png | Bin 0 -> 473 bytes public/img/country_flags/qa.png | Bin 0 -> 450 bytes public/img/country_flags/re.png | Bin 0 -> 545 bytes public/img/country_flags/ro.png | Bin 0 -> 495 bytes public/img/country_flags/rs.png | Bin 0 -> 423 bytes public/img/country_flags/ru.png | Bin 0 -> 420 bytes public/img/country_flags/rw.png | Bin 0 -> 533 bytes public/img/country_flags/sa.png | Bin 0 -> 551 bytes public/img/country_flags/sb.png | Bin 0 -> 624 bytes public/img/country_flags/sc.png | Bin 0 -> 608 bytes public/img/country_flags/sd.png | Bin 0 -> 492 bytes public/img/country_flags/se.png | Bin 0 -> 542 bytes public/img/country_flags/sg.png | Bin 0 -> 468 bytes public/img/country_flags/sh.png | Bin 0 -> 645 bytes public/img/country_flags/si.png | Bin 0 -> 510 bytes public/img/country_flags/sj.png | Bin 0 -> 512 bytes public/img/country_flags/sk.png | Bin 0 -> 562 bytes public/img/country_flags/sl.png | Bin 0 -> 436 bytes public/img/country_flags/sm.png | Bin 0 -> 502 bytes public/img/country_flags/sn.png | Bin 0 -> 532 bytes public/img/country_flags/so.png | Bin 0 -> 527 bytes public/img/country_flags/sr.png | Bin 0 -> 513 bytes public/img/country_flags/st.png | Bin 0 -> 584 bytes public/img/country_flags/sv.png | Bin 0 -> 501 bytes public/img/country_flags/sy.png | Bin 0 -> 422 bytes public/img/country_flags/sz.png | Bin 0 -> 643 bytes public/img/country_flags/tc.png | Bin 0 -> 624 bytes public/img/country_flags/td.png | Bin 0 -> 570 bytes public/img/country_flags/tf.png | Bin 0 -> 527 bytes public/img/country_flags/tg.png | Bin 0 -> 562 bytes public/img/country_flags/th.png | Bin 0 -> 452 bytes public/img/country_flags/tj.png | Bin 0 -> 496 bytes public/img/country_flags/tk.png | Bin 0 -> 638 bytes public/img/country_flags/tl.png | Bin 0 -> 514 bytes public/img/country_flags/tm.png | Bin 0 -> 593 bytes public/img/country_flags/tn.png | Bin 0 -> 495 bytes public/img/country_flags/to.png | Bin 0 -> 426 bytes public/img/country_flags/tr.png | Bin 0 -> 492 bytes public/img/country_flags/tt.png | Bin 0 -> 617 bytes public/img/country_flags/tv.png | Bin 0 -> 536 bytes public/img/country_flags/tw.png | Bin 0 -> 465 bytes public/img/country_flags/tz.png | Bin 0 -> 642 bytes public/img/country_flags/ua.png | Bin 0 -> 446 bytes public/img/country_flags/ug.png | Bin 0 -> 531 bytes public/img/country_flags/um.png | Bin 0 -> 571 bytes public/img/country_flags/us.png | Bin 0 -> 609 bytes public/img/country_flags/uy.png | Bin 0 -> 532 bytes public/img/country_flags/uz.png | Bin 0 -> 515 bytes public/img/country_flags/va.png | Bin 0 -> 553 bytes public/img/country_flags/vc.png | Bin 0 -> 577 bytes public/img/country_flags/ve.png | Bin 0 -> 528 bytes public/img/country_flags/vg.png | Bin 0 -> 630 bytes public/img/country_flags/vi.png | Bin 0 -> 616 bytes public/img/country_flags/vn.png | Bin 0 -> 474 bytes public/img/country_flags/vu.png | Bin 0 -> 604 bytes public/img/country_flags/wf.png | Bin 0 -> 554 bytes public/img/country_flags/ws.png | Bin 0 -> 476 bytes public/img/country_flags/xx.png | Bin 0 -> 447 bytes public/img/country_flags/ye.png | Bin 0 -> 413 bytes public/img/country_flags/yt.png | Bin 0 -> 593 bytes public/img/country_flags/za.png | Bin 0 -> 642 bytes public/img/country_flags/zm.png | Bin 0 -> 500 bytes public/img/country_flags/zw.png | Bin 0 -> 574 bytes public/img/default_banner.png | Bin 0 -> 9831 bytes public/img/icons/bumplock.png | Bin 0 -> 379 bytes public/img/icons/lock.png | Bin 0 -> 881 bytes public/img/icons/loop.png | Bin 0 -> 685 bytes public/img/icons/sticky.png | Bin 0 -> 754 bytes public/img/icons/unlisted.png | Bin 0 -> 657 bytes public/img/logo.png | Bin 0 -> 26829 bytes public/img/spoiler.png | Bin 0 -> 1312 bytes public/img/theme/bg-yotsuba.png | Bin 0 -> 145 bytes scripts/create_admin.ts | 36 + scripts/migrations/01_schema.sql | 76 + src/.DS_Store | Bin 0 -> 6148 bytes src/cache.ts | 89 + src/components/board_list.tsx | 21 + src/components/capcode.tsx | 20 + src/components/datetime.tsx | 12 + src/components/form.tsx | 48 + src/components/mod_nav.tsx | 43 + src/components/page.tsx | 12 + src/components/page_body.tsx | 54 + src/components/page_head.tsx | 30 + src/config.ts | 99 + src/ctx.ts | 39 + src/db.ts | 25 + src/db/ban.ts | 54 + src/db/banner.ts | 44 + src/db/board.ts | 151 + src/db/news.ts | 43 + src/db/post.ts | 367 +++ src/db/schema.ts | 133 + src/db/user.ts | 120 + src/error.tsx | 73 + src/main.ts | 42 + src/permissions.ts | 77 + src/routes.ts | 109 + src/routes/actions/login.ts | 25 + src/routes/actions/logout.ts | 5 + src/routes/index.tsx | 30 + src/routes/login.tsx | 34 + src/routes/mod/actions/board_actions.ts | 88 + src/routes/mod/actions/create_board.ts | 29 + src/routes/mod/actions/create_user.ts | 49 + src/routes/mod/actions/update_password.ts | 27 + src/routes/mod/actions/update_permissions.ts | 46 + src/routes/mod/actions/user_actions.ts | 213 ++ src/routes/mod/banners.tsx | 123 + src/routes/mod/bans.tsx | 120 + src/routes/mod/boards.tsx | 146 + src/routes/mod/index.tsx | 144 + src/routes/mod/logs.tsx | 35 + src/routes/mod/news.tsx | 124 + src/routes/mod/permissions.tsx | 77 + src/routes/mod/reports.tsx | 35 + src/routes/mod/users.tsx | 231 ++ src/zod_schema.ts | 184 ++ tsconfig.json | 21 + web/styles/base.scss | 145 + web/styles/themes/yotsuba.scss | 113 + 320 files changed, 7716 insertions(+) create mode 100644 .DS_Store create mode 100755 .gitignore create mode 100755 .prettierrc create mode 100755 .vscode/settings.json create mode 100755 LICENSE.txt create mode 100755 README.md create mode 100644 config/board_defaults.json create mode 100755 config/locale/countries.json create mode 100644 config/locale/meme_flags.json create mode 100644 config/locale/permissions.json create mode 100755 config/roles.json create mode 100755 package-lock.json create mode 100755 package.json create mode 100755 public/assets/capcode_icons/admin.png create mode 100755 public/assets/capcode_icons/janny.png create mode 100755 public/assets/capcode_icons/manager.png create mode 100755 public/assets/capcode_icons/mod.png create mode 100755 public/assets/capcode_icons/system.png create mode 100755 public/favicon.ico create mode 100755 public/img/country_flags/ad.png create mode 100755 public/img/country_flags/ae.png create mode 100755 public/img/country_flags/af.png create mode 100755 public/img/country_flags/ag.png create mode 100755 public/img/country_flags/ai.png create mode 100755 public/img/country_flags/al.png create mode 100755 public/img/country_flags/am.png create mode 100755 public/img/country_flags/an.png create mode 100755 public/img/country_flags/ao.png create mode 100755 public/img/country_flags/ar.png create mode 100755 public/img/country_flags/as.png create mode 100755 public/img/country_flags/at.png create mode 100755 public/img/country_flags/au.png create mode 100755 public/img/country_flags/aw.png create mode 100755 public/img/country_flags/ax.png create mode 100755 public/img/country_flags/az.png create mode 100755 public/img/country_flags/ba.png create mode 100755 public/img/country_flags/bb.png create mode 100755 public/img/country_flags/bd.png create mode 100755 public/img/country_flags/be.png create mode 100755 public/img/country_flags/bf.png create mode 100755 public/img/country_flags/bg.png create mode 100755 public/img/country_flags/bh.png create mode 100755 public/img/country_flags/bi.png create mode 100755 public/img/country_flags/bj.png create mode 100755 public/img/country_flags/bm.png create mode 100755 public/img/country_flags/bn.png create mode 100755 public/img/country_flags/bo.png create mode 100755 public/img/country_flags/br.png create mode 100755 public/img/country_flags/bs.png create mode 100755 public/img/country_flags/bt.png create mode 100755 public/img/country_flags/bv.png create mode 100755 public/img/country_flags/bw.png create mode 100755 public/img/country_flags/by.png create mode 100755 public/img/country_flags/bz.png create mode 100755 public/img/country_flags/ca.png create mode 100755 public/img/country_flags/cc.png create mode 100755 public/img/country_flags/cd.png create mode 100755 public/img/country_flags/cf.png create mode 100755 public/img/country_flags/cg.png create mode 100755 public/img/country_flags/ch.png create mode 100755 public/img/country_flags/ci.png create mode 100755 public/img/country_flags/ck.png create mode 100755 public/img/country_flags/cl.png create mode 100755 public/img/country_flags/cm.png create mode 100755 public/img/country_flags/cn.png create mode 100755 public/img/country_flags/co.png create mode 100755 public/img/country_flags/cr.png create mode 100755 public/img/country_flags/cs.png create mode 100755 public/img/country_flags/cu.png create mode 100755 public/img/country_flags/cv.png create mode 100755 public/img/country_flags/cx.png create mode 100755 public/img/country_flags/cy.png create mode 100755 public/img/country_flags/cz.png create mode 100755 public/img/country_flags/de.png create mode 100755 public/img/country_flags/dj.png create mode 100755 public/img/country_flags/dk.png create mode 100755 public/img/country_flags/dm.png create mode 100755 public/img/country_flags/do.png create mode 100755 public/img/country_flags/dz.png create mode 100755 public/img/country_flags/ec.png create mode 100755 public/img/country_flags/ee.png create mode 100755 public/img/country_flags/eg.png create mode 100755 public/img/country_flags/eh.png create mode 100755 public/img/country_flags/er.png create mode 100755 public/img/country_flags/es.png create mode 100755 public/img/country_flags/et.png create mode 100755 public/img/country_flags/fam.png create mode 100755 public/img/country_flags/fi.png create mode 100755 public/img/country_flags/fj.png create mode 100755 public/img/country_flags/fk.png create mode 100755 public/img/country_flags/fm.png create mode 100755 public/img/country_flags/fo.png create mode 100755 public/img/country_flags/fr.png create mode 100755 public/img/country_flags/ga.png create mode 100755 public/img/country_flags/gb.png create mode 100755 public/img/country_flags/gd.png create mode 100755 public/img/country_flags/ge.png create mode 100755 public/img/country_flags/gf.png create mode 100755 public/img/country_flags/gh.png create mode 100755 public/img/country_flags/gi.png create mode 100755 public/img/country_flags/gl.png create mode 100755 public/img/country_flags/gm.png create mode 100755 public/img/country_flags/gn.png create mode 100755 public/img/country_flags/gp.png create mode 100755 public/img/country_flags/gq.png create mode 100755 public/img/country_flags/gr.png create mode 100755 public/img/country_flags/gs.png create mode 100755 public/img/country_flags/gt.png create mode 100755 public/img/country_flags/gu.png create mode 100755 public/img/country_flags/gw.png create mode 100755 public/img/country_flags/gy.png create mode 100755 public/img/country_flags/hk.png create mode 100755 public/img/country_flags/hm.png create mode 100755 public/img/country_flags/hn.png create mode 100755 public/img/country_flags/hr.png create mode 100755 public/img/country_flags/ht.png create mode 100755 public/img/country_flags/hu.png create mode 100755 public/img/country_flags/id.png create mode 100755 public/img/country_flags/ie.png create mode 100755 public/img/country_flags/il.png create mode 100755 public/img/country_flags/in.png create mode 100755 public/img/country_flags/io.png create mode 100755 public/img/country_flags/iq.png create mode 100755 public/img/country_flags/ir.png create mode 100755 public/img/country_flags/is.png create mode 100755 public/img/country_flags/it.png create mode 100755 public/img/country_flags/jm.png create mode 100755 public/img/country_flags/jo.png create mode 100755 public/img/country_flags/jp.png create mode 100755 public/img/country_flags/ke.png create mode 100755 public/img/country_flags/kg.png create mode 100755 public/img/country_flags/kh.png create mode 100755 public/img/country_flags/ki.png create mode 100755 public/img/country_flags/km.png create mode 100755 public/img/country_flags/kn.png create mode 100755 public/img/country_flags/kp.png create mode 100755 public/img/country_flags/kr.png create mode 100755 public/img/country_flags/kw.png create mode 100755 public/img/country_flags/ky.png create mode 100755 public/img/country_flags/kz.png create mode 100755 public/img/country_flags/la.png create mode 100755 public/img/country_flags/lb.png create mode 100755 public/img/country_flags/lc.png create mode 100755 public/img/country_flags/li.png create mode 100755 public/img/country_flags/lk.png create mode 100755 public/img/country_flags/lr.png create mode 100755 public/img/country_flags/ls.png create mode 100755 public/img/country_flags/lt.png create mode 100755 public/img/country_flags/lu.png create mode 100755 public/img/country_flags/lv.png create mode 100755 public/img/country_flags/ly.png create mode 100755 public/img/country_flags/ma.png create mode 100755 public/img/country_flags/mc.png create mode 100755 public/img/country_flags/md.png create mode 100755 public/img/country_flags/me.png create mode 100755 public/img/country_flags/mg.png create mode 100755 public/img/country_flags/mh.png create mode 100755 public/img/country_flags/mk.png create mode 100755 public/img/country_flags/ml.png create mode 100755 public/img/country_flags/mm.png create mode 100755 public/img/country_flags/mn.png create mode 100755 public/img/country_flags/mo.png create mode 100755 public/img/country_flags/mp.png create mode 100755 public/img/country_flags/mq.png create mode 100755 public/img/country_flags/mr.png create mode 100755 public/img/country_flags/ms.png create mode 100755 public/img/country_flags/mt.png create mode 100755 public/img/country_flags/mu.png create mode 100755 public/img/country_flags/mv.png create mode 100755 public/img/country_flags/mw.png create mode 100755 public/img/country_flags/mx.png create mode 100755 public/img/country_flags/my.png create mode 100755 public/img/country_flags/mz.png create mode 100755 public/img/country_flags/na.png create mode 100755 public/img/country_flags/nc.png create mode 100755 public/img/country_flags/ne.png create mode 100755 public/img/country_flags/nf.png create mode 100755 public/img/country_flags/ng.png create mode 100755 public/img/country_flags/ni.png create mode 100755 public/img/country_flags/nl.png create mode 100755 public/img/country_flags/no.png create mode 100755 public/img/country_flags/np.png create mode 100755 public/img/country_flags/nr.png create mode 100755 public/img/country_flags/nu.png create mode 100755 public/img/country_flags/nz.png create mode 100755 public/img/country_flags/om.png create mode 100755 public/img/country_flags/pa.png create mode 100755 public/img/country_flags/pe.png create mode 100755 public/img/country_flags/pf.png create mode 100755 public/img/country_flags/pg.png create mode 100755 public/img/country_flags/ph.png create mode 100755 public/img/country_flags/pk.png create mode 100755 public/img/country_flags/pl.png create mode 100755 public/img/country_flags/pm.png create mode 100755 public/img/country_flags/pn.png create mode 100755 public/img/country_flags/pr.png create mode 100755 public/img/country_flags/ps.png create mode 100755 public/img/country_flags/pt.png create mode 100755 public/img/country_flags/pw.png create mode 100755 public/img/country_flags/py.png create mode 100755 public/img/country_flags/qa.png create mode 100755 public/img/country_flags/re.png create mode 100755 public/img/country_flags/ro.png create mode 100755 public/img/country_flags/rs.png create mode 100755 public/img/country_flags/ru.png create mode 100755 public/img/country_flags/rw.png create mode 100755 public/img/country_flags/sa.png create mode 100755 public/img/country_flags/sb.png create mode 100755 public/img/country_flags/sc.png create mode 100755 public/img/country_flags/sd.png create mode 100755 public/img/country_flags/se.png create mode 100755 public/img/country_flags/sg.png create mode 100755 public/img/country_flags/sh.png create mode 100755 public/img/country_flags/si.png create mode 100755 public/img/country_flags/sj.png create mode 100755 public/img/country_flags/sk.png create mode 100755 public/img/country_flags/sl.png create mode 100755 public/img/country_flags/sm.png create mode 100755 public/img/country_flags/sn.png create mode 100755 public/img/country_flags/so.png create mode 100755 public/img/country_flags/sr.png create mode 100755 public/img/country_flags/st.png create mode 100755 public/img/country_flags/sv.png create mode 100755 public/img/country_flags/sy.png create mode 100755 public/img/country_flags/sz.png create mode 100755 public/img/country_flags/tc.png create mode 100755 public/img/country_flags/td.png create mode 100755 public/img/country_flags/tf.png create mode 100755 public/img/country_flags/tg.png create mode 100755 public/img/country_flags/th.png create mode 100755 public/img/country_flags/tj.png create mode 100755 public/img/country_flags/tk.png create mode 100755 public/img/country_flags/tl.png create mode 100755 public/img/country_flags/tm.png create mode 100755 public/img/country_flags/tn.png create mode 100755 public/img/country_flags/to.png create mode 100755 public/img/country_flags/tr.png create mode 100755 public/img/country_flags/tt.png create mode 100755 public/img/country_flags/tv.png create mode 100755 public/img/country_flags/tw.png create mode 100755 public/img/country_flags/tz.png create mode 100755 public/img/country_flags/ua.png create mode 100755 public/img/country_flags/ug.png create mode 100755 public/img/country_flags/um.png create mode 100755 public/img/country_flags/us.png create mode 100755 public/img/country_flags/uy.png create mode 100755 public/img/country_flags/uz.png create mode 100755 public/img/country_flags/va.png create mode 100755 public/img/country_flags/vc.png create mode 100755 public/img/country_flags/ve.png create mode 100755 public/img/country_flags/vg.png create mode 100755 public/img/country_flags/vi.png create mode 100755 public/img/country_flags/vn.png create mode 100755 public/img/country_flags/vu.png create mode 100755 public/img/country_flags/wf.png create mode 100755 public/img/country_flags/ws.png create mode 100755 public/img/country_flags/xx.png create mode 100755 public/img/country_flags/ye.png create mode 100755 public/img/country_flags/yt.png create mode 100755 public/img/country_flags/za.png create mode 100755 public/img/country_flags/zm.png create mode 100755 public/img/country_flags/zw.png create mode 100644 public/img/default_banner.png create mode 100755 public/img/icons/bumplock.png create mode 100755 public/img/icons/lock.png create mode 100644 public/img/icons/loop.png create mode 100755 public/img/icons/sticky.png create mode 100755 public/img/icons/unlisted.png create mode 100755 public/img/logo.png create mode 100755 public/img/spoiler.png create mode 100755 public/img/theme/bg-yotsuba.png create mode 100755 scripts/create_admin.ts create mode 100755 scripts/migrations/01_schema.sql create mode 100644 src/.DS_Store create mode 100755 src/cache.ts create mode 100755 src/components/board_list.tsx create mode 100755 src/components/capcode.tsx create mode 100755 src/components/datetime.tsx create mode 100644 src/components/form.tsx create mode 100755 src/components/mod_nav.tsx create mode 100755 src/components/page.tsx create mode 100755 src/components/page_body.tsx create mode 100755 src/components/page_head.tsx create mode 100755 src/config.ts create mode 100755 src/ctx.ts create mode 100755 src/db.ts create mode 100755 src/db/ban.ts create mode 100755 src/db/banner.ts create mode 100755 src/db/board.ts create mode 100755 src/db/news.ts create mode 100755 src/db/post.ts create mode 100755 src/db/schema.ts create mode 100755 src/db/user.ts create mode 100755 src/error.tsx create mode 100755 src/main.ts create mode 100755 src/permissions.ts create mode 100755 src/routes.ts create mode 100755 src/routes/actions/login.ts create mode 100755 src/routes/actions/logout.ts create mode 100755 src/routes/index.tsx create mode 100755 src/routes/login.tsx create mode 100644 src/routes/mod/actions/board_actions.ts create mode 100644 src/routes/mod/actions/create_board.ts create mode 100644 src/routes/mod/actions/create_user.ts create mode 100644 src/routes/mod/actions/update_password.ts create mode 100644 src/routes/mod/actions/update_permissions.ts create mode 100644 src/routes/mod/actions/user_actions.ts create mode 100644 src/routes/mod/banners.tsx create mode 100755 src/routes/mod/bans.tsx create mode 100755 src/routes/mod/boards.tsx create mode 100755 src/routes/mod/index.tsx create mode 100755 src/routes/mod/logs.tsx create mode 100755 src/routes/mod/news.tsx create mode 100644 src/routes/mod/permissions.tsx create mode 100755 src/routes/mod/reports.tsx create mode 100755 src/routes/mod/users.tsx create mode 100644 src/zod_schema.ts create mode 100755 tsconfig.json create mode 100755 web/styles/base.scss create mode 100755 web/styles/themes/yotsuba.scss diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..464bfcad737ce8e3a0c9ee4a4c2af2ff0dd61f06 GIT binary patch literal 6148 zcmeHK%}T>S5Z-O8O({YS3Oz1(E!Ze66fYsx7cim+m718M!I&*cY7eE5v%Zi|;`2DO zy8(+ii`W_1{pNQ!`$6`HF~+?`8{URt9xff(NXkAK~gq0qISMh8) zwfE0tS_E-A%Tz%e%^>CWI!+_G^yDIqa#ic=fYq@&Q@giXIb)|Uoa52DFIMA|L0=4q zqxHID9ULB=UQC~pmsGxKIytbdWY1s)@1U5~y!u(1$n+6BRd$s{NDL4I!~ij{-3*v> z!Rl@|4YYbbTmFH`%--z*`E z7$64z83VjE@h2WE%ABn~mWOApfOZcJ1@lT&KtSKP1b~72NKXZIT%ZnluEA0xj)Hzw Q4oDXPMF@4oz%MZH1ubkzg#Z8m literal 0 HcmV?d00001 diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..fcbb8ac --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +dist +node_modules +public/css +public/js +public/uploads +public/banners +config/site.json +config/assets diff --git a/.prettierrc b/.prettierrc new file mode 100755 index 0000000..438397a --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "plugins": [ + "prettier-plugin-css-order", + "@trivago/prettier-plugin-sort-imports" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100755 index 0000000..8f39472 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib", + "typescript.enablePromptUseWorkspaceTsdk": true, + "editor.formatOnSave": true +} diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100755 index 0000000..0ad25db --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/README.md b/README.md new file mode 100755 index 0000000..a199144 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# czchan + +Český anonymní imageboard. diff --git a/config/board_defaults.json b/config/board_defaults.json new file mode 100644 index 0000000..d7e5e8b --- /dev/null +++ b/config/board_defaults.json @@ -0,0 +1 @@ +{ "private": false, "unlisted": false } diff --git a/config/locale/countries.json b/config/locale/countries.json new file mode 100755 index 0000000..cfe891f --- /dev/null +++ b/config/locale/countries.json @@ -0,0 +1,251 @@ +{ + "af": "Afghánistán", + "ax": "Alandy", + "al": "Albánie", + "dz": "Alžírsko", + "as": "Americká Samoa", + "vi": "Americké Panenské ostrovy", + "ad": "Andorra", + "ao": "Angola", + "ai": "Anguilla", + "aq": "Antarktida", + "ag": "Antigua a Barbuda", + "ar": "Argentina", + "am": "Arménie", + "aw": "Aruba", + "au": "Austrálie", + "az": "Ázerbájdžán", + "bs": "Bahamy", + "bh": "Bahrajn", + "bd": "Bangladéš", + "bb": "Barbados", + "be": "Belgie", + "bz": "Belize", + "by": "Bělorusko", + "bj": "Benin", + "bm": "Bermudy", + "bt": "Bhútán", + "bo": "Bolívie", + "bq": "Bonaire, Svatý Eustach a Saba", + "ba": "Bosna a Hercegovina", + "bw": "Botswana", + "bv": "Bouvetův ostrov", + "br": "Brazílie", + "io": "Britské indickooceánské území", + "vg": "Britské Panenské ostrovy", + "bn": "Brunej", + "bg": "Bulharsko", + "bf": "Burkina Faso", + "bi": "Burundi", + "ck": "Cookovy ostrovy", + "cw": "Curaçao", + "td": "Čad", + "me": "Černá Hora", + "cz": "Česko", + "cn": "Čína", + "dk": "Dánsko", + "dm": "Dominika", + "do": "Dominikánská republika", + "dj": "Džibutsko", + "eg": "Egypt", + "ec": "Ekvádor", + "er": "Eritrea", + "ee": "Estonsko", + "et": "Etiopie", + "fo": "Faerské ostrovy", + "fk": "Falklandy (Malvíny)", + "fj": "Fidži", + "ph": "Filipíny", + "fi": "Finsko", + "fr": "Francie", + "gf": "Francouzská Guyana", + "tf": "Francouzská jižní a antarktická území", + "pf": "Francouzská Polynésie", + "ga": "Gabon", + "gm": "Gambie", + "gh": "Ghana", + "gi": "Gibraltar", + "gd": "Grenada", + "gl": "Grónsko", + "ge": "Gruzie", + "gp": "Guadeloupe", + "gu": "Guam", + "gt": "Guatemala", + "gn": "Guinea", + "gw": "Guinea-Bissau", + "gg": "Guernsey", + "gy": "Guyana", + "ht": "Haiti", + "hm": "Heardův ostrov a McDonaldovy ostrovy", + "hn": "Honduras", + "hk": "Hongkong", + "cl": "Chile", + "hr": "Chorvatsko", + "in": "Indie", + "id": "Indonésie", + "iq": "Irák", + "ir": "Írán", + "ie": "Irsko", + "is": "Island", + "it": "Itálie", + "il": "Izrael", + "jm": "Jamajka", + "jp": "Japonsko", + "ye": "Jemen", + "je": "Jersey", + "za": "Jihoafrická republika", + "gs": "Jižní Georgie a Jižní Sandwichovy ostrovy", + "kr": "Jižní Korea", + "ss": "Jižní Súdán", + "jo": "Jordánsko", + "ky": "Kajmanské ostrovy", + "kh": "Kambodža", + "cm": "Kamerun", + "ca": "Kanada", + "cv": "Kapverdy", + "qa": "Katar", + "kz": "Kazachstán", + "ke": "Keňa", + "ki": "Kiribati", + "cc": "Kokosové ostrovy", + "co": "Kolumbie", + "km": "Komory", + "cg": "Kongo", + "cd": "Konžská demokratická republika", + "cr": "Kostarika", + "cu": "Kuba", + "kw": "Kuvajt", + "cy": "Kypr", + "kg": "Kyrgyzstán", + "la": "Laos", + "ls": "Lesotho", + "lb": "Libanon", + "lr": "Libérie", + "ly": "Libye", + "li": "Lichtenštejnsko", + "lt": "Litva", + "lv": "Lotyšsko", + "lu": "Lucembursko", + "mo": "Macao", + "mg": "Madagaskar", + "hu": "Maďarsko", + "my": "Malajsie", + "mw": "Malawi", + "mv": "Maledivy", + "ml": "Mali", + "mt": "Malta", + "im": "Ostrov Man", + "ma": "Maroko", + "mh": "Marshallovy ostrovy", + "mq": "Martinik", + "mu": "Mauricius", + "mr": "Mauritánie", + "yt": "Mayotte", + "um": "Menší odlehlé ostrovy USA", + "mx": "Mexiko", + "fm": "Mikronésie", + "md": "Moldavsko", + "mc": "Monako", + "mn": "Mongolsko", + "ms": "Montserrat", + "mz": "Mosambik", + "mm": "Myanmar", + "na": "Namibie", + "nr": "Nauru", + "de": "Německo", + "np": "Nepál", + "ne": "Niger", + "ng": "Nigérie", + "ni": "Nikaragua", + "nu": "Niue", + "nl": "Nizozemsko", + "nf": "Norfolk", + "no": "Norsko", + "nc": "Nová Kaledonie", + "nz": "Nový Zéland", + "om": "Omán", + "pk": "Pákistán", + "pw": "Palau", + "ps": "Palestina", + "pa": "Panama", + "pg": "Papua Nová Guinea", + "py": "Paraguay", + "pe": "Peru", + "pn": "Pitcairnovy ostrovy", + "ci": "Pobřeží slonoviny", + "pl": "Polsko", + "pr": "Portoriko", + "pt": "Portugalsko", + "at": "Rakousko", + "re": "Réunion", + "gq": "Rovníková Guinea", + "ro": "Rumunsko", + "ru": "Rusko", + "rw": "Rwanda", + "gr": "Řecko", + "pm": "Saint Pierre a Miquelon", + "sv": "Salvador", + "ws": "Samoa", + "sm": "San Marino", + "sa": "Saúdská Arábie", + "sn": "Senegal", + "kp": "Severní Korea", + "mk": "Severní Makedonie", + "mp": "Severní Mariany", + "sc": "Seychely", + "sl": "Sierra Leone", + "sg": "Singapur", + "sk": "Slovensko", + "si": "Slovinsko", + "so": "Somálsko", + "ae": "Spojené arabské emiráty", + "gb": "Spojené království Velké Británie a Severního Irska", + "us": "Spojené státy americké", + "rs": "Srbsko", + "lk": "Srí Lanka", + "cf": "Středoafrická republika", + "sd": "Súdán", + "sr": "Surinam", + "sh": "Svatá Helena, Ascension a Tristan da Cunha", + "lc": "Svatá Lucie", + "bl": "Svatý Bartoloměj", + "kn": "Svatý Kryštof a Nevis", + "mf": "Svatý Martin (francouzská část)", + "sx": "Svatý Martin (nizozemská část)", + "st": "Svatý Tomáš a Princův ostrov", + "vc": "Svatý Vincenc a Grenadiny", + "sz": "Svazijsko", + "sy": "Sýrie", + "sb": "Šalomounovy ostrovy", + "es": "Španělsko", + "sj": "Špicberky a Jan Mayen", + "se": "Švédsko", + "ch": "Švýcarsko", + "tj": "Tádžikistán", + "tz": "Tanzanie", + "th": "Thajsko", + "tw": "Tchaj-wan", + "tg": "Togo", + "tk": "Tokelau", + "to": "Tonga", + "tt": "Trinidad a Tobago", + "tn": "Tunisko", + "tr": "Turecko", + "tm": "Turkmenistán", + "tc": "Turks a Caicos", + "tv": "Tuvalu", + "ug": "Uganda", + "ua": "Ukrajina", + "uy": "Uruguay", + "uz": "Uzbekistán", + "cx": "Vánoční ostrov", + "vu": "Vanuatu", + "va": "Vatikán", + "ve": "Venezuela", + "vn": "Vietnam", + "tl": "Východní Timor", + "wf": "Wallis a Futuna", + "zm": "Zambie", + "eh": "Západní Sahara", + "zw": "Zimbabwe" +} diff --git a/config/locale/meme_flags.json b/config/locale/meme_flags.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/config/locale/meme_flags.json @@ -0,0 +1 @@ +{} diff --git a/config/locale/permissions.json b/config/locale/permissions.json new file mode 100644 index 0000000..29ebe3e --- /dev/null +++ b/config/locale/permissions.json @@ -0,0 +1,33 @@ +{ + "MANAGE_USERS": "Spravovat uživatele", + "MANAGE_BANNERS": "Spravovat bannery", + "ALL_BANNERS": "Spravovat bannery (+)", + "MANAGE_BANS": "Spravovat bany", + "ALL_BANS": "Spravovat bany (+)", + "MANAGE_BOARDS": "Spravovat nástěnky", + "ALL_BOARDS": "Spravovat nástěnky (+)", + "MANAGE_NEWS": "Spravovat novinky", + "ALL_NEWS": "Spravovat novinky (+)", + "MANAGE_REPORTS": "Spravovat hlášení", + "MANAGE_POSTS": "Spravovat příspěvky", + "DELETE_POSTS": "Mazat příspěvky", + "DELETE_FILES": "Mazat soubory", + "SPOILER_POSTS": "Spoilerovat příspěvky", + "MOVE_POSTS": "Přesouvat příspěvky", + "EDIT_POSTS": "Upravovat příspěvky", + "VIEW_IPS": "Zobrazovat IP adresy", + "VIEW_LOGS": "Zobrazovat záznamy", + "VIEW_METADATA": "Zobrazovat metadata", + "VIEW_PRIVATE_BOARDS": "Zobrazovat soukromé nástěnky", + "CAPCODE": "Capcode", + "CUSTOM_CAPCODE": "Vlastní capcode", + "BYPASS_BAN": "Obcházet bany", + "BYPASS_LOCK": "Obcházet zámky", + "BYPASS_BLOCK": "Obcházet blokace", + "BYPASS_FILTER": "Obcházet filtry", + "BYPASS_RATELIMIT": "Obcházet limity", + "ALL_FIELDS": "Používat všechna pole", + "ADVANCED_MARKUP": "Používat rozšířený markup", + "MANAGE_SITE_ASSETS": "Spravovat assety", + "MANAGE_SITE_CONFIG": "Spravovat nastavení stránky" +} diff --git a/config/roles.json b/config/roles.json new file mode 100755 index 0000000..1e21acb --- /dev/null +++ b/config/roles.json @@ -0,0 +1,93 @@ +{ + "janny": { + "rank": 3, + "capcode": "Uklízeč:ffd700:janny.png", + "permissions": [ + "MANAGE_REPORTS", + "BAN_USERS", + "DELETE_POSTS", + "DELETE_FILES", + "USE_CAPCODE", + "PRIVATE_BOARDS" + ] + }, + "mod": { + "rank": 2, + "capcode": "Moderátor:800080:mod.png", + "permissions": [ + "MANAGE_POSTS", + "MANAGE_REPORTS", + "BAN_USERS", + "UNBAN_USERS", + "DELETE_POSTS", + "DELETE_FILES", + "MOVE_POSTS", + "VIEW_IPS", + "USE_CAPCODE", + "PRIVATE_BOARDS" + ] + }, + "manager": { + "rank": 1, + "capcode": "Manažer:4169e1:manager.png", + "permissions": [ + "MANAGE_USERS", + "MANAGE_BOARDS", + "MANAGE_BANNERS", + "MANAGE_POSTS", + "MANAGE_REPORTS", + "MANAGE_NEWS", + "BAN_USERS", + "UNBAN_USERS", + "DELETE_POSTS", + "DELETE_FILES", + "MOVE_POSTS", + "EDIT_POSTS", + "VIEW_IPS", + "VIEW_METADATA", + "VIEW_LOGS", + "USE_CAPCODE", + "CUSTOM_CAPCODE", + "BYPASS_BAN", + "BYPASS_LOCK", + "BYPASS_BLOCK", + "BYPASS_FILTER", + "BYPASS_RATELIMIT", + "ALL_FIELDS", + "PRIVATE_BOARDS", + "ADVANCED_MARKUP" + ] + }, + "admin": { + "rank": 0, + "capcode": "Admin:008000:admin.png", + "permissions": [ + "MANAGE_USERS", + "MANAGE_BOARDS", + "MANAGE_BANNERS", + "MANAGE_POSTS", + "MANAGE_REPORTS", + "MANAGE_BANS", + "MANAGE_NEWS", + "BAN_USERS", + "UNBAN_USERS", + "DELETE_POSTS", + "DELETE_FILES", + "MOVE_POSTS", + "EDIT_POSTS", + "VIEW_IPS", + "VIEW_METADATA", + "VIEW_LOGS", + "USE_CAPCODE", + "CUSTOM_CAPCODE", + "BYPASS_BAN", + "BYPASS_LOCK", + "BYPASS_BLOCK", + "BYPASS_FILTER", + "BYPASS_RATELIMIT", + "ALL_FIELDS", + "PRIVATE_BOARDS", + "ADVANCED_MARKUP" + ] + } +} diff --git a/package-lock.json b/package-lock.json new file mode 100755 index 0000000..9753682 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2741 @@ +{ + "name": "czchan", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "czchan", + "version": "1.0.0", + "license": "SEE LICENSE IN LICENSE.txt", + "dependencies": { + "@kitajs/html": "4.2.9", + "@kitajs/ts-html-plugin": "4.1.2", + "async-mutex": "0.5.0", + "bcrypt": "6.0.0", + "cookie-parser": "1.4.7", + "express": "5.1.0", + "formidable": "3.5.4", + "iovalkey": "0.3.3", + "jsonwebtoken": "9.0.2", + "pg": "8.16.3", + "postgres-migrations": "5.3.0", + "prettier": "3.6.2", + "read": "4.1.0", + "sass": "1.92.1", + "superjson": "2.2.2", + "typescript": "5.9.2", + "uuid": "13.0.0", + "zod": "4.1.7" + }, + "devDependencies": { + "@trivago/prettier-plugin-sort-imports": "5.2.2", + "@types/bcrypt": "6.0.0", + "@types/cookie-parser": "1.4.9", + "@types/express": "5.0.3", + "@types/formidable": "3.4.5", + "@types/jsonwebtoken": "9.0.10", + "@types/node": "24.3.1", + "@types/pg": "8.15.5", + "prettier": "3.6.2", + "prettier-plugin-css-order": "2.1.2" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@iovalkey/commands": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@iovalkey/commands/-/commands-0.1.0.tgz", + "integrity": "sha512-/B9W4qKSSITDii5nkBCHyPkIkAi+ealUtr1oqBJsLxjSRLka4pxun2VvMNSmcwgAMxgXtQfl0qRv7TE+udPJzg==", + "license": "MIT" + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@kitajs/html": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/@kitajs/html/-/html-4.2.9.tgz", + "integrity": "sha512-FDHHf5Mi5nR0D+Btq86IV1O9XfsePVCiC5rwU4PXjw2aHja16FmIiwLZBO0CS16rJxKkibjMldyRLAW2ni2mzA==", + "license": "MIT", + "dependencies": { + "csstype": "^3.1.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/kitajs/html?sponsor=1" + } + }, + "node_modules/@kitajs/ts-html-plugin": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@kitajs/ts-html-plugin/-/ts-html-plugin-4.1.2.tgz", + "integrity": "sha512-XE9iIe93TELBdQSvNC3xxXOPDhkcK7on4Oi2HUKhln3jAc5hzn1o33uzjHCYhLeW36r/LXCT70beoXRCFcuTxQ==", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "tslib": "^2.8.1", + "yargs": "^17.7.2" + }, + "bin": { + "ts-html-plugin": "dist/cli.js", + "xss-scan": "dist/cli.js" + }, + "funding": { + "url": "https://github.com/kitajs/html?sponsor=1" + }, + "peerDependencies": { + "@kitajs/html": "^4.2.5", + "typescript": "^5.6.2" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", + "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher/node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT", + "optional": true + }, + "node_modules/@trivago/prettier-plugin-sort-imports": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-5.2.2.tgz", + "integrity": "sha512-fYDQA9e6yTNmA13TLVSA+WMQRc5Bn/c0EUBditUHNfMMxN7M82c38b1kEggVE3pLpZ0FwkwJkUEKMiOi52JXFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@babel/generator": "^7.26.5", + "@babel/parser": "^7.26.7", + "@babel/traverse": "^7.26.7", + "@babel/types": "^7.26.7", + "javascript-natural-sort": "^0.7.1", + "lodash": "^4.17.21" + }, + "engines": { + "node": ">18.12" + }, + "peerDependencies": { + "@vue/compiler-sfc": "3.x", + "prettier": "2.x - 3.x", + "prettier-plugin-svelte": "3.x", + "svelte": "4.x || 5.x" + }, + "peerDependenciesMeta": { + "@vue/compiler-sfc": { + "optional": true + }, + "prettier-plugin-svelte": { + "optional": true + }, + "svelte": { + "optional": true + } + } + }, + "node_modules/@types/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cookie-parser": { + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.9.tgz", + "integrity": "sha512-tGZiZ2Gtc4m3wIdLkZ8mkj1T6CEHb35+VApbL2T14Dew8HA7c+04dmKqsKRNC+8RJPm16JEK0tFSwdZqubfc4g==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/express": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz", + "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.7.tgz", + "integrity": "sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/formidable": { + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/@types/formidable/-/formidable-3.4.5.tgz", + "integrity": "sha512-s7YPsNVfnsng5L8sKnG/Gbb2tiwwJTY1conOkJzTMRvJAlLFW1nEua+ADsJQu8N1c0oTHx9+d5nqg10WuT9gHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.3.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.1.tgz", + "integrity": "sha512-3vXmQDXy+woz+gnrTvuvNrPzekOi+Ds0ReMxw0LzBiK3a+1k0kQn9f2NWk+lgD4rJehFUmYy2gMhJ2ZI+7YP9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.10.0" + } + }, + "node_modules/@types/pg": { + "version": "8.15.5", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.5.tgz", + "integrity": "sha512-LF7lF6zWEKxuT3/OR8wAZGzkg4ENGXFNyiV/JeOt9z5B+0ZVwbql9McqX5c/WStFq1GaGso7H1AzP/qSzmlCKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "0.17.5", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", + "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", + "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "license": "MIT" + }, + "node_modules/async-mutex": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.3.0", + "node-gyp-build": "^4.8.4" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "optional": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/copy-anything": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", + "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", + "license": "MIT", + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/css-declaration-sorter": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz", + "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "license": "ISC", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "optional": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/formidable": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", + "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", + "license": "MIT", + "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", + "dezalgo": "^1.0.4", + "once": "^1.4.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immutable": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz", + "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==", + "license": "MIT" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/iovalkey": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/iovalkey/-/iovalkey-0.3.3.tgz", + "integrity": "sha512-4rTJX6Q5wTYEvxboXi8DsEiUo+OvqJGtLYOSGm37KpdRXsG5XJjbVtYKGJpPSWP+QT7rWscA4vsrdmzbEbenpw==", + "license": "MIT", + "dependencies": { + "@iovalkey/commands": "^0.1.0", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "optional": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "license": "MIT", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/javascript-natural-sort": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", + "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "optional": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pg": { + "version": "8.16.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", + "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.9.1", + "pg-pool": "^3.10.1", + "pg-protocol": "^1.10.3", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.2.7" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", + "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", + "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", + "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", + "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-less": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-less/-/postcss-less-6.0.0.tgz", + "integrity": "sha512-FPX16mQLyEjLzEuuJtxA8X3ejDLNGGEG503d2YGZR5Ask1SpDN8KmZUMpzCvyalWRywAn1n1VOA5dcqfCLo5rg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "postcss": "^8.3.5" + } + }, + "node_modules/postcss-scss": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.9.tgz", + "integrity": "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-scss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.4.29" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-migrations": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/postgres-migrations/-/postgres-migrations-5.3.0.tgz", + "integrity": "sha512-gnTHWJZVWbW8T3mXIxJm1JRU853TqBVWkhgfsTJr7zqT3VexjRmJj9kNi96rVhfTezDU4FVW6pf701kLOZiKIA==", + "license": "MIT", + "dependencies": { + "pg": "^8.6.0", + "sql-template-strings": "^2.2.2" + }, + "bin": { + "pg-validate-migrations": "dist/bin/validate.js" + }, + "engines": { + "node": ">10.17.0" + } + }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-css-order": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/prettier-plugin-css-order/-/prettier-plugin-css-order-2.1.2.tgz", + "integrity": "sha512-vomxPjHI6pOMYcBuouSJHxxQClJXaUpU9rsV9IAO2wrSTZILRRlrxAAR8t9UF6wtczLkLfNRFUwM+ZbGXOONUA==", + "dev": true, + "license": "ISC", + "dependencies": { + "css-declaration-sorter": "^7.1.1", + "postcss-less": "^6.0.0", + "postcss-scss": "^4.0.9" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "prettier": "3.x" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", + "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.7.0", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/read": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/read/-/read-4.1.0.tgz", + "integrity": "sha512-uRfX6K+f+R8OOrYScaM3ixPY4erg69f8DN6pgTvMcA9iRc8iDhwrA4m3Yu8YYKsXJgVvum+m8PkRboZwwuLzYA==", + "license": "ISC", + "dependencies": { + "mute-stream": "^2.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "license": "MIT", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/sass": { + "version": "1.92.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.92.1.tgz", + "integrity": "sha512-ffmsdbwqb3XeyR8jJR6KelIXARM9bFQe8A6Q3W4Klmwy5Ckd5gz7jgUNHo4UOqutU5Sk1DtKLbpDP0nLCg1xqQ==", + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sql-template-strings": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/sql-template-strings/-/sql-template-strings-2.2.2.tgz", + "integrity": "sha512-UXhXR2869FQaD+GMly8jAMCRZ94nU5KcrFetZfWEMd+LVVG6y0ExgHAhatEcKZ/wk8YcKPdi+hiD2wm75lq3/Q==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/superjson": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz", + "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==", + "license": "MIT", + "dependencies": { + "copy-anything": "^3.0.2" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uuid": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", + "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/zod": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.7.tgz", + "integrity": "sha512-6qi6UYyzAl7W9uV29KvcSFXqK4QCYNYUz2YASPNBWpJE1RY6R1nArmmFPgGY/CBYWzpeMw3EOER+DR9a05O4IA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/package.json b/package.json new file mode 100755 index 0000000..e75dcbf --- /dev/null +++ b/package.json @@ -0,0 +1,52 @@ +{ + "name": "czchan", + "version": "1.0.0", + "description": "Český anonymní imageboard", + "main": "./src/main.ts", + "scripts": { + "test": "echo \"Léky teď\"", + "backend": "npx xss-scan && npx tsc", + "frontend": "npx sass ./web/styles:./public/css", + "build": "npm run backend && npm run frontend", + "start": "node ./dist/src/main.js", + "create-admin": "node ./dist/scripts/create_admin.js" + }, + "repository": { + "type": "git", + "url": "https://git.czchan.org/sneedmaster/czchan" + }, + "author": "sneedmaster ", + "license": "AGPL-3.0-only", + "devDependencies": { + "@trivago/prettier-plugin-sort-imports": "5.2.2", + "@types/bcrypt": "6.0.0", + "@types/cookie-parser": "1.4.9", + "@types/express": "5.0.3", + "@types/formidable": "3.4.5", + "@types/jsonwebtoken": "9.0.10", + "@types/node": "24.4.0", + "@types/pg": "8.15.5", + "prettier": "3.6.2", + "prettier-plugin-css-order": "2.1.2" + }, + "dependencies": { + "@kitajs/html": "4.2.9", + "@kitajs/ts-html-plugin": "4.1.2", + "async-mutex": "0.5.0", + "bcrypt": "6.0.0", + "cookie-parser": "1.4.7", + "express": "5.1.0", + "formidable": "3.5.4", + "iovalkey": "0.3.3", + "jsonwebtoken": "9.0.2", + "pg": "8.16.3", + "postgres-migrations": "5.3.0", + "prettier": "3.6.2", + "read": "4.1.0", + "sass": "1.92.1", + "typescript": "5.9.2", + "superjson": "2.2.2", + "uuid": "13.0.0", + "zod": "4.1.8" + } +} diff --git a/public/assets/capcode_icons/admin.png b/public/assets/capcode_icons/admin.png new file mode 100755 index 0000000000000000000000000000000000000000..6745b2a5d8a868fcb22e66312060b66333e8cf46 GIT binary patch literal 1539 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 M07*qoM6N<$f_{Ad=Kufz literal 0 HcmV?d00001 diff --git a/public/assets/capcode_icons/manager.png b/public/assets/capcode_icons/manager.png new file mode 100755 index 0000000000000000000000000000000000000000..45b0a384e261ea1cf02d2fdac440a9d1fb551b6e GIT binary patch literal 1444 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 literal 0 HcmV?d00001 diff --git a/public/assets/capcode_icons/system.png b/public/assets/capcode_icons/system.png new file mode 100755 index 0000000000000000000000000000000000000000..f0aaf7912e698568e3fae54821980b685919e1f2 GIT binary patch literal 1670 zcmV;126_33P)z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy32;bRa{vGf6951U69E94oEQKA00(qQO+^Rk1r7-oE*d|ZJ^%m( z+(|@1R9M5smVazrWf{jm?>XoG>}~JuZFk$XtfPYl=vc-!Hkg=f16jll49PZuGGVdZ z{^7^sA_fgH`iF~A`NNo)8ii?*ndl#ypb<8W4*oE+Wg^rKI$K;RZEx>wdw-vM?m6${ zA8i?%YgdMrCwWiuyytnJ=gH^$p7%ZQ@vMDrT(ub=XK~oF9tv941AJ1}R9idQ6pv4Q zQUuysny;yDUb!*~v@LrB(O`K0zV5ETBi-Er;0w!+e`PE-nNFu=Y-~(c#$pr8UBB)2 zn{T#d&bG9#EnTe)#?|BR4$S-ey1RnFp5?@!OsCWH0On+fk1r$sbsylrAVYfzX!*ZO z5DkVO{o;WO_&<=Lt1AFJwCwmlEJIT~KK4=bS-^p+KvPq1{sH+Y{OBGbTW>Y@cu| z_Nzwf>^=!Zl^IDan84G*Z%)3&rW>!JwJH9Qiud;R^1$xh?&S3J&O*8T9Duqc=DKKg z?C+Jj8zTPe??x%(e?r1_U7mdGF@AmQ*i8^vO#r6WZs$*T+>d>23&o12SkY9p#;|QNt}EKD6#S8zL6k}>NDD@9V0H5= zYJuD;Wci-;4F3~>jh)8j_QmGxDBm4=8NX-%-g)F34BmS`hqtY`D6ZgWijHR5_HZhY zD|@_~%JYYz;*=;kWG;wJ>z6&R^16QijlC)yH$J>CE-el)a@*%nH?^^8RhW?(k5U}Gnz2s_lgER2kOnLP5g2e%B%c9BxS3}NG zWGXOIhKxNs(j`ddJpi8m#d)^2RS(7Vk^0rWNKm*Fl^4FnhPLE{Xx0dqxI3m05@C6qv4v;hbL_;Q-l1s5H7=~aP zV0a4C029G90Wq1#mifW)b6LYuk1nNwwwgf1eD>deT(CI6gc+cr#z)2$TmS*nQy2nx zP&)T#+=|26#tuB6hY{4Rzv}wb)KVnqco@gJEOyZp=jP8FCXLYm1xHW@m`b5?@8Jx; zir86#0YBkzn8{?4ow4)m3Jia5Nw;C0Qk|di1^t)KpK&YPH=Ru1H$6G^&qzJC)&zV8 zjt2$;Xq?nJR=2D|l5dhoBxq`C;va9E=C)hf85M^7Dyx#3=R%* zXXn%G-hY_)Mn`FEY-G)vHT#boIUuJo75|5A5+S=s*!~`j6;KYd@@jUPN-o1N0wMYYB1%3=nFDBEb z13mRpvJWAw7LhtobwI5`YEdd0@L9fK(8q?*S(3>l18jZ=WptkIeY_9gKOD!{TxxBd Qu>b%707*qoM6N<$g0rp^%K!iX literal 0 HcmV?d00001 diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100755 index 0000000000000000000000000000000000000000..6585a85fa84327fa3001f2859547cadf393086e3 GIT binary patch literal 4286 zcmc&&OKcle6n*pYZzi6n3M8ZyAt50MA5|4j z`Lm$u2M|JRun=N_V1rmzmAXO2fLbBS0#H}DZE)hoO&a6Gm;0QIHE|ke#ztYH>v!kg zbI-Z&Jgu%bQK0-gqmkJ>!A>>Xp43T|kG+sY2cV1etl|akSS1_x#M0iVT2%GQ* zU4uHx5a9+!_O6NgJK4L+@v^_NI(nF8<7JN9w!pIH`xs$;aBwV%O>x}zd6uzmVqMxt zZ3BwE&)QlZgwW#^hS7e(M7)vQ*oiZYdRYiPQL(k}fgxgXW9ViX>2po`7!t;1pd-uZ zvsiJUMb!a{(WFo7HEQ$&AVkWwz;Bisiv-7Js6I7II}ZVVxXcp$Gb{|bibEL71^Bzm z6yx9{!O7?LpO34|3OW)DK@ny|(Uqu09X) zEMGmmORQ^CeXl(zUGHKKp#|hR=;HJ0G66m^B?g1b<9niOwEgs8C|}|CK)ywY_>Na$ z#EC+%>*u5#sZC_$+iE*KJhN{M?i=}|rH$Nm0YqV_!gKOD%DqV+{Yq3W@9q=e*aJ#S z?ctw!YSB>inV722nwoLL)?;&V!z?EZ z^Jdbt=6ft_q1Uo+9eVgs)!}L$4?p_UKdDr@)Z4Rjw!3>rF%iF`XxshMmbvYEES9>a z>pfRBEm2Sv`>$Y7yoCDW?sf3uD@y!&u~=-DhC-nLl-mnOCm10tzjgZbN9*)YoH$cM z-d8)}_E@07^m#*$ z`k?3(IxytggTa-xALx|SPd)CzG`$Y!+M`dObz6EkdvNk3ea9xpwp4sYtbQE~4&J+n zr(3%A&RkVBF^%_}Enj;_pXY@)c|QEQbN)Y!^!=)%cdR9V%u=?NSxPNtYH}%C3th?9 zn8_^OOtbjKf`&~p0Msx>bGYdl{Ba{+!I!Lagm9De-wb#_Nrr|krT`pU$^bZ4%L1(0 EzZ5<|NB{r; literal 0 HcmV?d00001 diff --git a/public/img/country_flags/ad.png b/public/img/country_flags/ad.png new file mode 100755 index 0000000000000000000000000000000000000000..625ca84f9ec596848d4b967b5556fda897ca7183 GIT binary patch literal 643 zcmV-}0(||6P)S}{rU-#^xW|9%5S{`_G8k=zVG=|5luWB>#JF#yj01oZa& zTu&JQ008~}|NQ&{0055x0sR95`v3p?1_t~7{`v!N`v3d-{`&g=`T6amqXIBA$0h)T zKokWRkpoMx|Go@O4FvhR>O1p+i7`B6t^3)y2dJ<#?4I?d4x-E}Az98Z`2`TmzkmP! z{`(J9{pauh-+zJD{JOYLhW+QSzkh#x`Y&wke*WLjpZ~u9`2XuCr@{^OJ|MkZb_UGR@Z=V>fB*iaq<8P{*B}2u-uMnu#J~U$KrBDL0bT$1_irEqiZcM6WEF7g<|*+% zzkjHi-TwRc=f7_t9|K*?`1?07lmG&V<)V$@{}>pUYZ(~+fDw@M3(WY1!T<=sp}f-o0K!lJ3<>Jc!&VtMslE3ph2^H3FGe(F z4|^}slF1@l1Nxc}^5hjjU=0la|37)k@b@neJ^1|l@87?_{{z(l6@kcKe}Db@_Y267 z<&g#201!YdV6*>2R0EZ*bYA%%2Vi7me5m>mAb?naPGtnD20MY__n-f`0mzg8{s9CK z*hzmtW)s%H$oS{aAAkS?JLxaT`2W9u;n%DD1Q-B%#%TuGpFSS|0000&5HW|5#c7{r(N){s9YCi84$~U|{$Glm>AD0*K|^yLU}ZO+eP) ze;~;4`#0mCzs$dX{r~-&?f370zkUTM{{OB1_1E8DKmYz*JbN)f0I>i8Q1#1Kum1f0 z{pZggpt}FRe*ORX>;JEx|9}4c_w(m__W!pfKK=atLxM{ZXbC_7vHbh@@9*EgK-ECy zzkh)Y{?EYhFEQc&-#`EN9{7Lq7*OE*@9)2U|NQmmC(vO40VtezBLF}U2%?OI(TG)& zJC>!~;SYmN-)zb6gc(7A-^Q(#z0vHb9!CJ#T#ii{@&pjek8j`pfX)5|H00+GpnLvt za{MtfWBC4qk%R63j~{=2{QLgr`wyTb*amx2hce{;{QL3a|BhWh#6A5bKmf5of*%NgdVm^$Zuo!gI_LQd|G=96g51yWl0Agf71rLE? q3XWqq@&Lw?Iyk0*94-cc00RK~MQKxxUU$F%0000= z`}|7!`YQYT5C8xIh~*yx1DgQ1_jkSr_QgN{Gn)!7=zkRT=l(yS{y%^Jd$>%yrP!)vVq~xpZ~xA{{HjZ>hB|)Z~OQoBLBm{yCqA0{k;C`&+mV~|1$jj15^wU zKukakbkF}kzkmk({q_6zmUTZaUWBOraqa5!?(V-pgMR}R{04?0&`AIR#PSE^hrdi> z;*7t4gLVFSJ$J5DTibsorq@%ai5h7B2l^1G@*gM=fUW@uAjYc<48V{DX@ijvE(11_ dg@FMezyQzuBHsGv+i3s*002ovPDHLkV1jTADB=JB literal 0 HcmV?d00001 diff --git a/public/img/country_flags/ai.png b/public/img/country_flags/ai.png new file mode 100755 index 0000000000000000000000000000000000000000..74ed29d92616c86757d3c0ec04378301c8f591b4 GIT binary patch literal 643 zcmV-}0(||6P)ocPy#thD++(+#W%SS0Qvp^`1%0+_yGC)0QUL-|M&m^ z`T_tk0M7pe&iDc7fh@K74M*k&0{8f*88BG;;GY6ve?;&4{{Q>_{{8&^`u+g@|Ns5} z|C?Nv0*Gb#mu+n4z6ZW~#qjy_|6ji&ZeIEMk16oyzH)i@Pk$MNxLARR?a#kwpMS72 zF)}cG2M8dRUN*~81wCz%$A2$f`_IL|A|w6j^RHtc7{2`lYG-KkQ{(*knSqh%3kzr7 zs%s1kKL7#%F#yj00-^B^D;pD^^AqC)4C(?1m>@q26ceiU4*2{4`u_d~@vQ*ykp&A1 z3h$o${r~^~`vQo?Pm-bY@cm79zVQG5%l!B6)Riap%L`bGemnT<@2|iAfl64!%YWS~ z`p0YY_dig_cYpw5WYAmG*lPLk!;jy8|NZ#~MF0Q%{rCG1ko^Di`(Fhkd3F{?py0c9Vg{{gA~^#>F<|Nj5KapBR^@Bf~CXSi|t;lF=C z8-4)<5DUX^V9+rDCI3RuUy#}V{s46X!}R~}@4ug&fA|+@;n!b(1zCZ900KKMX({{sIILBZE*UQbc|SngJt$+`m9Kz!<;5 dBm+Qz0RWL%NC2%sdjkLf002ovPDHLkV1lARHI@JX literal 0 HcmV?d00001 diff --git a/public/img/country_flags/al.png b/public/img/country_flags/al.png new file mode 100755 index 0000000000000000000000000000000000000000..92354cb6e257be2cade71cb825027ce8d9efc06d GIT binary patch literal 600 zcmV-e0;m0nP)PbXFRCwBA zyv)hK@{fUmi2(>${xJOc&A`II@PmN?BKC`c;SU)710$d~Kmf4-r6niy16BY3`}g0! ze=L9h&Uybegp-Ns@87v^zGQMU|NZla@z1Y+zkdVKmFbHB0*HkH4F0@)&iwE1;&GRLuzkX(OGXMMa``@o$|Ni_2x(y(Jn3#TnHT?el|IhC;U%pHK`}6Df z@2@|9J^lXU=Z~KvfBzi*^!4A*U;lv`e*Fge3Lt=(82$q_`~m9z|ND;$!(WEqze~6o ze}X|JFO$H(U+VvX#{c{avi3L7@c;n?3L&6l7=Hix`}emN<6i~#|7+iUWBL8-&##}m zKKx5zXJr5LA80nvkwCM6egFs{po9Y?& zL6B;olm7ns#=rm&Kp-3b{RIXY(1{??Ha=$NKfkS+|9@us$Mfg+93j@fzkY#y2y!;i z&kO(o1PWH5>zPHwn7|>;0Mz;WPl13iP!Y(WKYy4&4hBXMSPugOKmaiw2gk!dq!|4J mj$lZE0R~eis zC&uN{cDQ8`!@ZNLOqwtoG6nhzPx}SV-d6yzFfcIz75{(rngJyDkKrFw8iaoThHCr$ zo8k9wMhS^O3=9AP!~%5B|9}4(ASQtkLN$m1CV&5705X362095KfLKJhW;OjWdGY7l zzrX*$DuLu*pcFC#NdEr)SL!$SY=)lz0mSm_*B_u)|AM4JP)(}33zy1Lc&_o7? zKL7y)bkd){fBym<_v`mxpbeRs`+WRVot-5A{r$IX$CJFg13u{3eX_{0R++j zR}GCkSy{&4zk!krKoTg(2n-k?g31B}5JW!NMq0d0000q< zM7LZb@rX?|r)2RP-+aO3(7q?c3+$-Vv0E)PxM3SDV%@s`#GkZvw_x^WBa~uq3^l}t zWdf(j(=(>^SgETc#5#EZT(4ObRkfxbzP9G;yza0;Ygc8-_*?EP(ca#`l6-Z6D0{tL zQ4|~MCSi!9Q9YkW=V$ix#EqZ!rc?eWA0TwdVID+3aqrYUXUhCI)Ad5!(cP!BhhW$Ayb2&r8kK!bz*2`~UE W(Ndrek0Jg50000NL7TW{{R0! zJP>i}(MpzaSkj z=eqvqa`^|6{{QbQRQ11q3~Vg_kG}Z}5I`(hw}ncX8D4$|h64jUK>or*=^r$dfI%n0 z$3MKz;!F0w91GjkkV%_X`@4 wKR^hm`X5LwD9*v?4;TUC6Bz$&3;+QJ0QLcJ=WT{TU;qFB07*qoM6N<$f>3bX%K!iX literal 0 HcmV?d00001 diff --git a/public/img/country_flags/as.png b/public/img/country_flags/as.png new file mode 100755 index 0000000000000000000000000000000000000000..32f30e4ce4eedd22d4f09c4f3a46c52dd064f113 GIT binary patch literal 647 zcmV;20(kw2P);Rmc0RI30 z|Nj5|{Q>>`0R8>{{QLs@`vM01^D6+%Ap+Y10Qw3Sr3ED)s=c}b05Jg0{{#dC0RR60 z)z$y}`~dv>0Q&p?0Q>ne0Khr|#}EMN1`-ttJvaFq6VTDg`OMJS*4F}vg#m0fQ1$QM z42u6QTCg3Kt8+Li1J#Y_zUd>4Lw{URe1tEjLbKmY(S0M7pd(g2qsJ0|z;{R`~d z`n<6E*3bb41p*Zr{{R344LkeqyaMyV01O!W0PZ0+TjlEQ0st`p&i?@b0Q{Gi0m{n% z`T6-B``1JQx+4kM0{rp_FfRy7P6Qq%_1|&<`|JPx`Uew8u)+%h2+ zdiAI7xt}_}e|$CmbI9$2>u h*D)|iF)#oG7yty8`q2#I8zBGy002ovPDHLkV1j5VEF}N{ literal 0 HcmV?d00001 diff --git a/public/img/country_flags/at.png b/public/img/country_flags/at.png new file mode 100755 index 0000000000000000000000000000000000000000..0f15f34f2883c4b4360fc871d7105309f1533282 GIT binary patch literal 403 zcmV;E0c`$>P)@|4`Xj5kLT%`al?B=W5I`&prl$WjwHQRjfmQ&G0jUOA z@&|Dug_Rm`2Y251|$~)1M2@@6mI}!8O6olw6y^Q z5X--d7nzS8`+x5q12kBmVFD!~j6c5_fMKno0(1^Q0I>i=is|LjXX40RRttS6cG0UZ?;7002ovPDHLkV1fxUnjZiF literal 0 HcmV?d00001 diff --git a/public/img/country_flags/au.png b/public/img/country_flags/au.png new file mode 100755 index 0000000000000000000000000000000000000000..a01389a745d51e16b01a9dc0a707572564a17625 GIT binary patch literal 673 zcmV;S0$%-zP)>fJ3En$GhGS>sbE%%m3$AD)q?8M9y>88-}kR7#RKlk!P~Y_PLuF7~U~3`~nC7 zF#yj00ZUDdpLsm{7ajP|&HwoK0Usg|6%f4L_{`Mi{rvv-`ukf=Ed&Gs-sA7L!Q7*a zj{*QO0M7pb%?Sw^g@yy{>ihEY{`vU@3=8@G0rvO$i3mOL`~mv-`W+b$Mmr&io5dg< z5v!7q0*L95jt`TzK8Kd(Utv)OSp_aLv){6ccV+Z`{Q2+asKUU&aO3`Kpz6wW8wp`< z28M3{0mSqnB#A*-c*8%1=RD#sSOwMznKA3=e&iEzwo{cA=PgXK`2OQ}gqId83!|%* zA_Kz@fB*n70M7pdECCwp4H&@R`1|(w-}M5x*74i)0}%fAt;XafA{48))#>Z>?CD#}*e}Ret0tl$#*RMZ7}Jl7Z|M45`5*URzH9L z{rmSnPy;{!u>dsyO%meg+00000NkvXX Hu0mjfN{&}S literal 0 HcmV?d00001 diff --git a/public/img/country_flags/aw.png b/public/img/country_flags/aw.png new file mode 100755 index 0000000000000000000000000000000000000000..a3579c2d621069c8128d7cf16440d5e45a3ab3cd GIT binary patch literal 524 zcmV+n0`vWeP)^}0h@W9fA9JOn#opKkr#VO<{LPm{QLg}EdH3Vb1Zzpp zqiZ+XNBm%5{`ViKi{bCTe}Dh~2a+HyfB%6Q|9}7eKVjxEfB<6Q<6r}-{{7;G=hB{; zLB+p+|Ni^;|DXT={s75;AoBO$f4@PDf8sps4FCQC1Q64wUw^h*hy4HZXO3U)@85qw zD*pci8U|DWQu_BVi2MUWKoOv;00M{w=;|*lY_o%kegGx^{`&*A{SSok_usF-P)oof z|A86+0*K|;FQAM6fB*FtoEI&bZG5mq328#as{}<$F zWDQ{Ffz1MH001phxCBNdC_2H6U$AI~GQbEJqg*Ti0R{kQ(yH?TRrCY^ O0000^@RCwBA zRL|V|7QjV)*ul;Rlceqc6W0{`~_8AeMg& z|LUXE8U8c=`}dziv6P|mEu%JjM}pqh@65md|Ni~w->=_)fBgZXe?Nc!ot?WGAOHX{ z0M7pe`uG9>{{ZXo`u+d?{`u+u=j#9V2j%Sc^797z|N8p+{rLO+`TP9&`u!FW2mk>3 z0#G;xDFDDQ2vg|)-@5Zw?JVTrAxNs~5&|AZ;5uWfQ`vUsMGm`>aGWoI7=P|oW>%~E z|Kk8dq@aG@HzdsB>fj@shYzX-a%=q<(frSYmfLIv5{HbzQ${~M_P_y6C&m>3uU0*DFd0+8#0#Gk)^|NLQBRAyCBeB9H+Wn{=Mr@*PJ_ZKV< zB>w@GFfafF5Ks>TBP0ZV{bBt5_ve*sKkwd`OiO#eX6=u=cfX!E#rX3NBT(umNC7Yu z0Ro8S7X#4ce?Xr63$phg)Bk64X8r&5``_O`AFf^jc>p5y?+^38KVKLa00M}`wU!~x z=I_1Fe}Df4D*yZI@1I|PfBgnRptt`0`2{iqMpRNPo|?TW>MPOFW(r1_!xlT-yep5|G*@e%^SFxf#DC32@n7=0M7pe00011 zNe=7o{FIuhzQ(sjF~t7>`T+s_0sj00{`>*}`~Uy^|Ni?^Z7Tcx|FW0O0tlpm;s5{T z+df{s^E1rDZN;v4VP>0|8GkY`{`&t1s2YfV{r~st=KUYCl59Xr00M{!Xv@F<41fPH zoWA^R$>xvtF5yd$xc~X{7o-}f=ig7DY9RXc``_>1K-c{N2q2&ahQEIq{`~z1RCDs; zw*~7zIJ!sAKj8J}&!7K)enC|K{|nUc|Mwq|27mwp*#K1f8;Jh=2byv8+}GKw-@AGz z%-rMu^XK3HKtBLA{Qvdq*I%FsKn(x^1k&&qY(LP_Uw?q)|3jz0PF?=a(?4bU?qHzN z5I+E=z&88?2q2IKpz*){`~&I%+VJc5-=Dw#?LYB#!lHLRL7BZ8-NZ5x*TXaKmf6TZ20qEQj`^F;~x-W z05U+*fBxNj@_W(7kJ(ujGnWgh$g%(Z2hso#K#U9wI~hQ+`3Dk_7z`j1tL#*2FTcgW f@C+EF009O7>dRn2w6d?H00000NkvXXu0mjfueTzu literal 0 HcmV?d00001 diff --git a/public/img/country_flags/bb.png b/public/img/country_flags/bb.png new file mode 100755 index 0000000000000000000000000000000000000000..0df19c71d20d7fdc06e1cba01028983439b2bdae GIT binary patch literal 585 zcmV-P0=E5$P) zj51&U|NhG$_=oZTA7 z0mJ{l|Ns2{|Nr-Y#^3*c{=WU_!>_-;fvROCxPdkR1P~M0Xa=C_KR`47{sXG+pY!Yf zqu=)*{%)W0>;M0szkdJy_507SKYxDz`3KYh5I`&-cY{^``2$3Z|NsB`^ZWO@y}$qd z{r%_nFQBC$4Is{6hz5WF0@(mI8^j0N`~Tl>LzQ1Ye}a&q>MtPo*RQ`|OMu4x1!@2Y zAfSdne}TsT{`c$8-(P?J0c~)w`~?J`zx?#Dg*yp^z&88=2q2IKpt(TRKniTbum8-y z1bBWi0e#F0wgIH{FVJ|Pxj-8L0tgsNfByUdJMs7b-@h3^8h-!E3i%CU{QCW$1*GB6 ze~`2PfHeF82q2&aU>N+96yg32bmdP5238q{|Gydk0=52OVEN6!@<&OA7Z~7wL16>b z@DCt>7#SFn85mf=5eY;LZ#OUe_l@D-2Zn#Y82<4v{9|E|jkRQ8_`$&N0jL_N03g5s XX0T^_9W~6o00000NkvXXu0mjfXpthO literal 0 HcmV?d00001 diff --git a/public/img/country_flags/bd.png b/public/img/country_flags/bd.png new file mode 100755 index 0000000000000000000000000000000000000000..076a8bf87c0cedcce47099c6b74b59f2c9d1dbce GIT binary patch literal 504 zcmVCcfPV}OzZe+)fYC1)`hmpw%>WR9L@~Dk00x0r!@5ksR;+hV0>ZLfIcBS@orh;x zv}95za5W)x_7^}bV3YqpdH?h;NZnuSC%?V#{+0gl`~RQckJx{&l>NO~;@7XgzkdJz zEh{VwR0LSuiyWGR{aKQ z00-R5+27mwp+3@G@Um)kt zzdstU{yWl9SIB_0ft3II3)BD*KrA2|{``^Tm;4PDy`=a1tTre>fEI#8 u{{2z<2NVVaum*qtVq}m+iAaI~Aiw~?reC_kVQV=60000gsB+3J~Dn`pdxgmx19A{hgns>oXkhsL8>j&wfIu2Rsv+R_ zAB5T1GyoL?1Q1BWpFe+p|Nf1n;TJ-~Kd@?uhChFRHUI<=#0HS!U%!8$YJib28yLY( z0tg_G2B7i3e*FSj@aq@44gY_`jRywQ69xu=0D?FP=vyd&x*M*V6|CV8P{Uu4OBfgc z0tjNmA4y3`us8lPG6H?}8%zRafgb(Cp!5f*6oP{xAq} rF#LPR@IoIX4%E%Yz;K6w0U*EtuUcjA`-_J300000NkvXXu0mjf+>pV; literal 0 HcmV?d00001 diff --git a/public/img/country_flags/bf.png b/public/img/country_flags/bf.png new file mode 100755 index 0000000000000000000000000000000000000000..ab5ce8fe1237a18d6809a5570024eb108cb14a3e GIT binary patch literal 497 zcmV@|4`Xj5kLT%`al?B=W5I`(ov;U*021-Lr1*!W5(gW7O z@cTDV13&<=05Ky_HBiN$KMcQt819e>gHP`g_|Np;&&;0(C^$RHS>olZ*Q41X9v27}ar6#W9}0h{^{OajIK0?h(y z_zOe~zZd`lhy{p$|NA4!EeST}?;nUxkP49VfMx;1;V%&U|N9rH;SU2q05LM8GXV8K n(H~UyA0);fa6B_H00bBSQ>$p&WfB*UmB!B(-`wPhZ^#?@C^U5EbbPyl_g=3Hc01Sh${{P9=HB%4| z34*<3-m=C?^;L%miR{xQv2*hU;8+Y&0Dxf-W~KjsusWFAC4@j0#9_j;X5z6SjRhH> zd}sd(7FPhVFdSi!*Zj@;_Sc`kfByUdk|3A-`STmZ_yb~qxIp9o{E-p)bLt=iKmf7) z+|Ix(`{&<1pm88mK&}8g1WG~}e||Ik`OB#Cw~m1UAb?na(Zlrn4}+v6P!1@{@CT$5 z&-#|8y{IY2iKmf5omHz(^qAp)ym^JhN+jk7Vet{H$(Z8=>|AV=V zva&!M00M{!p&AHub^kXuF^G#pRQ&(-8^nMkpk@XJfB<6o_wOIv5C1P;X6Wwu|KUB< zc%X*g2-OTg#J~U$KumBafq;(A|A`a+OH08N{X($;=pP0KfB<5Fs|GR7oM!0i{{QYB zSm|$=!=N@Wf}I2qKuka<{f7YtBjf)QCj1u{`~UM7nrdKh0c`*VF+c!8{Q&j%sZ;-Z zd;dRshENS5{{S`og~Sg)05QIL^-54s5b7kL(8GuS8yfz9{>%Ui`+q-vfW*P*^_43C z0mK4S`s2qBh&}(NP5W0_$$%QQ@R)`GfB*vkck5Jby^kNv00000NkvXXu0mjfy0pt~ literal 0 HcmV?d00001 diff --git a/public/img/country_flags/bi.png b/public/img/country_flags/bi.png new file mode 100755 index 0000000000000000000000000000000000000000..5cc2e30cfc47452d5bef949628e955a522d59e50 GIT binary patch literal 675 zcmV;U0$lxxP)$wbBBJ zMTk#;k4<3e#ePFE;{?TciOx=-NpIdg`t|#dcz^$YFqm+of7AU<7UGr+0t^5F#PsJM z(2)${Jd)SmT+KY5@!|h>po)L5-!r~>3uJux|0`&Jz~QGy6a*CiGyVtq1|Wc#zWw|0 z_y6C2|NnAvaR2)A>-NX%|3Ci{R?!hvH~jzU``ypCK7Rkq&&~h)FDNvCApj5nF#yj0 z0_X_*{{Q{_|NQXv@Av-qBMmR$teo!O*6-%u-J+8r3n~2s`s?uP{Qmp>{`~+900M{! zNC9;}eE(3IS9-C{62AZc<>Jx>%af}0@?r&05Jg0 z{{#T;0Os`NWh`ko4>SM%|Kw(60099Kad7|t0qcf^`tR-^sIP`Fhru7lkI#w#>Hq?W zkzqQ+@08y_|9v}t;{CEE%)%nTc=-MC@P)7S@!~g$((Ud|+28Q224FCZIGXF0FQ1zcbfB*dX_lJRr z;s3wCNa|2D02KoSAbE^!0Dxf-mZ69ApkCb5fP^9ydGO$cm6w`kGj(t#`M{tFlLo%j z*4%mm2&CaJSn02SzkrtfWBLcO;r%bLy5Gnoqrh)qC;VKmZT`F#yj00W%wG zw4zx17##T6*Z=hN0Tvey7!|wG?K;V)0{r{_`}_X<{Qmp<|NQ*_`}@YSvI2-@&cAgG z*R?p0?_>V(@&EVlEP?{x|1esA`||VNU68t8zyJRJ_3zg&hTp&0WMzR?0R#}s1|E|% zRV}tRkH6o2#3;(nsVMXI-pgBmh17ms`TzTu!pfBl%*_9R;OEbWDJei#0R#}sGpDd$ z96Spi@~bn7G5z{~^_!ZLy2#s;5B~o8&G`G*|6f0Ap0WS=_rFd2C(ubi4FCZEF#yj0 z1a!1=2e0}MwgDi3SU@2S4mePl{012eQ4J-3 z|Mm`933BS6zyJRJ1;ztF0D(08|NVzmQWB*4&)@%$um=Y`lmy1W-@hDx{{qQh3=9AP x#JH7#;TJ3-|G=X54_FW_j)5c_0|P*S0RT0dOSD~(4;cUe002ovPDHLkV1hfJDkcB` literal 0 HcmV?d00001 diff --git a/public/img/country_flags/bn.png b/public/img/country_flags/bn.png new file mode 100755 index 0000000000000000000000000000000000000000..8fb09849e9b5712e9cdd8a2c25035da201535cf5 GIT binary patch literal 639 zcmV-_0)YLAP)8n_8%|dG+hlCr-g% z|9<~wU<9iE{r}%DMphZ1Jpcj3%*DZBWo-@72DFWlk(-gj@!ngb-$#D&C_i9e{QvLY zw|{?r|NHlgiHY~?^IHG`05Jg0{{(k=dF}7->+9=cXlNG}79t%T{rC3_t(pA#`tSX# z4jKOh|NsB|;rr`({{a5__y7Wk5omBxQSrlv5B>nb??0VZ8qCZrpS+?}gatn>o%-+B zPl=+||6st##PspQ2Y>)#{Cu5(QJ~_^?faD-pMV;E|7MwLubjQ@B-gLso$mVYgECpU zxpfp2fEt*XnBKpC4-fz`0M7pb@zVeZ8g>5r-~k2r`Sbhd;{WmN02I(80QmHEv)Ik{ z>H7Nm`}_MdF)?FhWi>T50*LYBMFw`s0-y$le?J(QfB$Fr_2c8e-`_!j`0PG|ckZ*_ zzkY$-`sc5Tips@{7Xbo@1sGC5IYwrQfB%2{|MTZR!ygX*-%McG4cZmTY ZzyK0(F$K@T-Dv;-002ovPDHLkV1la=J3;^e literal 0 HcmV?d00001 diff --git a/public/img/country_flags/bo.png b/public/img/country_flags/bo.png new file mode 100755 index 0000000000000000000000000000000000000000..ce7ba522aa7e948d581478432643c230eed1a658 GIT binary patch literal 500 zcmV^3LvnC{|x`%yaFl$ss8ha0V@6XKS=c-5cwO(_{}IL0ki=i zfLOjo{AE-9^-mn=h(G`Tfz|!{{r?|W+uz?{^yeR#!Nm9NlRN`J0I_^YV&Ikj@%Q)F z|Ns9m{0FN3^Z!3k!*8HnFvf4N5xas50L!z?-wXsAPk7$ ze<1l+iQ((fPYeJ7#KQ200VpKFA^{3Nph^G!{9^b86oDW}(Ek1R7ZiL9e}Ret0*H}e qBRB&8AR!3%7c6cef(%d+Aiw~vBYd|xMihYn0000j-HAXl7XJGjM{~r+i{r~sxzrQwG-&h#_Y&p;L z=ieV7_s^f-zyJOD{rC5(+EoAn1k&)I;s3Kw&;LT?{{2}P{Pq8T&j0^^J4?LvUd;UK z&+k8ffB*XXONL(tXahh1fi?X94^$0Q>Z$uRRO)Y4)uapmB!2(-ul(c1=C+V!kAF`) z$PCo;`_FHns{jIs3Fu;wy-Z(c2YwAz{&V4aXk?bejFO*Op&u`@i~PecqBpL@DIoojb8mb+WHGK%fG2>)a3=iVf_~v+`s?*1;!`?Kmai^I3i*ZIYtpN f{g(k500bBS@Ns{bTs^2gCr95H^s6Fo1}O2_S$Nfl}4;<}o-r{^R8Q_xtzX z|Nnu?|Ni^;2gm@D3=F@4EC#P1& zzuV{ie)#0qf2Lnh)j(~3VeG$ufPMl9Aczeh3x56n#l!R`HRQLfpFjVwfkN;%I39k( zVgne^e?jDThF>oL0*Hk{T6(^^8pGqq44*zhqZ1OZzkZ>_F(mFeH~<0+0A4S6=>Lb* QN&o-=07*qoM6N<$f&=*Yr2qf` literal 0 HcmV?d00001 diff --git a/public/img/country_flags/bt.png b/public/img/country_flags/bt.png new file mode 100755 index 0000000000000000000000000000000000000000..1d512dfff42db1ea3e7c59fa7dd69319e789ee12 GIT binary patch literal 631 zcmV--0*L*IP)?&DC3JV7l?ccjcgB=Qq4l4w|Q;eOZ4z|IjsEI`#PYSloM|AFHF zfy)2>XZQ=$@t;lU|F1tRzpnmg{PUOLH{-8A|F{4CvUU?d0I>k|0TuuM^_1cNUy$nm zzZn?*{^!*ACzSEy$NyhHzB0Wn`TXRU=!%=n%Ci4h82|!^32gR%glZtk^6USnb00o@ z{`LDW>%U)&BK_|-y-75RkKVi&O=|xC<=6keK+72b0*D2q;Xlai|Ns9mF#h@fM(h;aV>^!x1-zNu02XV-n@ zm3imZ@&EI0hChG)|NZlafdL?Zn1I0yH26P<=DUx29QQsc%}izF;QV-S-hYOl|1V4z zX|a-<=Kf#g*Z)7i{{y|szyJ_HEKEEM|C#>$KmJzoZa)M2-~Wm-e7Cl+vi)L|kY`}} z&A$F20RV*|BkB*O Rz6SsR002ovPDHLkV1m;fPLlut literal 0 HcmV?d00001 diff --git a/public/img/country_flags/bv.png b/public/img/country_flags/bv.png new file mode 100755 index 0000000000000000000000000000000000000000..160b6b5b79db15e623fa55e5774e5d160b933180 GIT binary patch literal 512 zcmV+b0{{JqP)O=a{vExMP2%`MCSoB^FIcLe_%lf;|~%E5I`(IQNh}3Ao>6Q|DFUXMn*>AqQd`w z|1kXd^B;tM|Njjl{{hM0zwd6?1Q0+hV1xeud-4=Wy?p-%sO`^#2S61Jzk!N?s)6X& zzhA%p|N6}=D+{y%Ab`Lc{sL9~1=0UN4*CD*7s%9KAf+JHKs~=eB-8KTKvw|-5R1&; zzd&a|ob(5%^Z$Q=wHy9p13+aOpFRNu5F>N&`Tk_-7w>=n{RejQzkfh&Kn{rf10?_b z{tFTZibx5v&dxav5I~H7|Ney-|DWN1$%1FyagzUW0464;_wU~W1Q5$TW@eGxtUvee z3vAf*8|igK9~@*rr66bh|NrkNM8z+V zAV?>O@ek;bKfu6d00k6 zPW&$KB`I@TtA?2x@Q~7pdcWi#1>DDZ2>MuG0I@IumHuaV^&7|soAMvT0IK){RtF@1 zgH;3B;_Qq-34j1%{P^)BFE1~|NkA(gBv!!4$aw$$eSiRB1ga=2D|__l(H|iA4T2Ex z>lc`SQ9x2&UjE?0g8%`<0`lvhzd$}*14J4{IhY2@0~G@V5J82%q9;EBlXu0U&@_82I`BfBC|Yn~P8llm?myQ3_K18=`^X_ivyEfB<6p_xd%%#*GZ! z-C&_V5IGPDK|g;X*gypg3;+Sd!XPTj0Q7fK5>&$Y{sHB| zjK9DB00sa3QR4q|tc?L6fSCSm+3>HW`NxbYpP7GsX8sK%KS9yYPfWi)GJ)AFzdp14 z{>t>1fsFwmfLQ*Ci!yxq#8guv=_mQ;-(Rp{AoSPbXFRCwBA z{KboB21 z`ppP*BG3|`!vO*asNvt=e_;3gWsp&PA@E<2jYs6ilRy6-zmSku5?~OOP=5aV@4sI_ zSN#QP00zQ!JD>SpAKYgR_QStF|9~F;2M|Ck zz%U1e+FyoWe;B_0VVclq;}vp_kNy3hKMWrj7*{kqJbMcaQ;_Pve;I)x1Q0+h46j}> zeiN4E1lkA+>|e|*mzJsW^M3)l^W_^hf&c&c`Mv->`48yHFF-%s0|+3-*GLfwiRNz% m3}3(u4lrUsie?}H2rvLBbQv(L;??y40000e`b1WGV} ze#gbd^&bp=ef-GykAYc$@BhDltSqdeSy?RXJpcawy+5fJAb?mHcsPJ&{d@EF@4HtJ z)qno||1xd*zaM|Ot4f#|8A1AQ-~Pubz$7IBv;-i4SQvovfB*ge{fFVtAE4aNE7yPi z`^Eg=5yP(^Ur(Q5`TLjM%#`8#_rHuB|9}1hItd_vSpNO`#qjs<|KGp=|NIHm@MXqK z79K7}ZvOwQ9Df%s{`~lX*xI%KzW-!k{Lc96Cr|@G0I@Lq`0?-epMSr9|NHeD?&umBG}6iH(DSogJte zDERNk_kV0Zng09%Itd_vSb&=T{rt@+B*66dJ0my{fpNkvBm!jpV_;xnVgd@XDk?Dx zh=IJyzyJ_HjJJUrz)|>#fr0nRl^;KTz#|yK`2G9$BS%LzureS32rvK^u`=B;c)+&+ O0000Vbu`-)NXk-Y`p?2$B#e1 z{(buQ`}e;;zyJNd_UF(2w_nb@VUiUP2igD-Kuin_BL6vl`zgIQV*mR8|8G|2U!T7I zn`-)Ow$bxne}9LGfB5z5*XcJ*K(;aeSD>o^0st`p&i@1e@&c~g4N@7~{}B9V!5sbp z`j|K8<@Nq-&kt=P>@pAZ`T7Q#;RiPl^j;kSgdzX}05Jg0{{#R8=f>y-z~LG0`u_d< z{QLg=2?6_k)DH^)`=sFo8YTUi-v;{r`o8J{6Wj>^)c^vB2^cJY|Nnmc_5b@He}DY@ z_51JdufKoSslWR1>-W9)3`r;XfsXn0`_Fc($H!lT!V@5Xn1Igu{qOIuf50&L{qg7D z?|*(LofMGa_%_k-*{}b_0g?9mMSwvE)Bq4bEIr*1})*zF_+U)&LMdEMOb{Nb-vOh6FrNC(tkd{&4;N%g_D$&)>g> z0^hO}m?z%iIsOJ1mka;_#K>R{i_RZNQ42x8VJSe8f#Ern2@qfa%&RHCyvg>Q00000 LNkvXXu0mjf=TkSf literal 0 HcmV?d00001 diff --git a/public/img/country_flags/cd.png b/public/img/country_flags/cd.png new file mode 100755 index 0000000000000000000000000000000000000000..5e489424884d2ec9e429f70d69af00edf242a077 GIT binary patch literal 528 zcmV+r0`L8aP)JkPqeYl28iLgD=0{><0$P44T5yOrT$dE?(KkwMFdoG^-J zGv9P)Kk|i5`lcNgUUAbboca5{hI)v&h!9!~`Yg)Ld}$VwYqqXn@gVLi>3LSVGm1W? z3qnDJAk6chH(u7f~FohUBCxfQDx8?5BQsCcprAnfVhO SHC~zk0000@|NZ~|iEi8i zh~?j3h8^dV0y&$fY|i?}`2XK;hJSzm|G$6#|L@;V)&KqZ3sU;$?>`_ZF38C6g#jRd z7@s_ODJ3Nev=9gw8UKSQhX4P)z5oCE{eQ>y|1j|H-+x9%#`EXT0R#{W1H*5i5@u$g zs{agN2m-%;fixfipeaCQpFe*F2q2cU!)f`}`5%6M06O*WzrTN({{8*;CkCkD_wPBk z=llWket~@P=ieVecEOTuB>(}$vTge|RYlbYUmpDW_v_E!KfnL|{{8n?G|>28zowm@ z_UrGj-+zAtrGTUoztWctUjPD#h2amw@BhDn5OHz(52cFPg zot=T34qlT?57Mzkg5?0ssOG0Ftp>paW8OyZ`_I07*qoM6N<$f+2k} AjsO4v literal 0 HcmV?d00001 diff --git a/public/img/country_flags/cg.png b/public/img/country_flags/cg.png new file mode 100755 index 0000000000000000000000000000000000000000..a859792ef32a02b41503b5ab5f216191af397e02 GIT binary patch literal 521 zcmV+k0`~ohP)i@P`2b{=v9^Ktp0uZ001fLMSM)r!?%mH+B91 z=ii?+(ckX8zrd=&8g~5t-|*}I50LQ;KzRlRfB=Hn08;wv->)Q*Kc0LLgTdsU-~VfV z{r~#=|1XdRMzE6r0tlo5X#B6gzhXrG`18Y*0!g6iieLY~Kvgqz}`W zLWO>Z3Lv@r7+CdZpiy8Ae}EeP0z(_<2Y>)#Vfe-1#`njMUlORAfdMG=o8iiD#%aG9 z#Q!h|feZ$rzkeYJ=p=vuV)VVru?7U|?FE@Zm|i>fBlzF zc~M_qKf%h=bAcoS;}NE7f8|yFe%B9?;;8%o@BeG_!|(4qhyo=(h-XBmKHXpc{~y!A z`THH3fsDVeudko)ARzm9UL&JI!+~uEM*rBES1=kd6zV%LH0J*N$gIQAc0y}k9qTFv z4h1oVG?rB#zNY^8{QUp5wE>>R#S4NZQd1i@F)*?OF@6y}@zmk^!Gr7L9asuAf!ae1 z{{CbBBH^Az=(+fAb?mHzA!MjRs*H~|NF=I z{~td;@BjZG-hUVsqZ#w(|L@=b|NQ>{d(*K^00G3pzycQk`jp|{UxvSb;p*Vv_V?Sr z{{Q;@@3$6WKrBG#F*5vR z`2XiW&+Xo6{KsbP_-SF+DJF`pL$*;0gaz z7NKVhyo`U;4*0+SK#>rfFFfafF5DUYXKh;r+@n*scPQUy6`!63S zGXn!71H*qtMn-04W+o=4|487=moEST#KiEAA;m(3;Xgy1iO{3BKc9a12~-UM7^Z`) z1qdLZI~bN-c=z}J|D88K{QmR*&tHcBxB*ZDKmf5Ya0q<-!`is-{h$8~f7m(x{QZSX zH3Jv`1P}|uy*o^wIGI2G`1kie)4zYe_&EQ6|NftW0ohon1}3I|Z{7d|5aVBMp0ssVo0Ek~;W>Z#0SJsA+2j`G% zv|UVeYYs-#Sn6_J90h1VosR?LBU7{U1rQ6+R0f9sPrg3=`~UA>#=n0a|7Q67_y6zT zP{j57|G!`V{{zu)Mn+knB>(|98e;E17s>tTLaMG-~Y>h{r`!g0jL-tfS4HmF#P%V_xIo5zyJLP zk|2W%e*G8w^1X|KC618W{eAoCFX+EI literal 0 HcmV?d00001 diff --git a/public/img/country_flags/cn.png b/public/img/country_flags/cn.png new file mode 100755 index 0000000000000000000000000000000000000000..89144146219e6fbec7eaa89e1bf4b073d299569e GIT binary patch literal 472 zcmV;}0Vn>6P)$bmtfBa_T{rmsVufH!rO2O)W0!b+P{TrwO zAb?mv&i(NBbu;G`sX*)cv$d*%>MiL-(QdhpkjakVmyu%k;sfcNRj;yhJaxT5MTg0u5&QfH8#Bf O0000XbC_7v4G9~kE;3? z$h?1GrT>4y7{3|*{r>Zd0U&@_82$jY{AFPH^Plk#hy=QU5o|Ds0oDLyFn~2M0QE2c z1P~*G;PKK9g@-SH{rUU<_aC4k|G@~v`1Kn|{`&=C|M{yZ!G37cNq_)iVfexD=MNA8 zh5p0Hf4_c1v;p<}|Ak}_F!%rh2&CcXZ-(!`82&@8$XA^gF6?xq}zd#y5N`WeVBbyB+frxZH0- literal 0 HcmV?d00001 diff --git a/public/img/country_flags/cs.png b/public/img/country_flags/cs.png new file mode 100755 index 0000000000000000000000000000000000000000..8254790ca72f98d9e79d94bdfcb8839b1fd434ad GIT binary patch literal 439 zcmV;o0Z9IdP)|s3jQ%L{DG0k=ogG+0SLgMnA-pV!aytqt1(q3VU5g2cTadE%W~lnu`}m; z-504zOD10s2K8+~RqF^K7O;8$o;>*sR0>r0`!|sM`x~eNg#P~i_4n8BzrTI~MgGdl z@-i@d0|+1%Q&SyHO(9WHu7Cgk{{@--A8b6x2B1zb1BygNIks=N00WJ{7y^0)%zz1kWJN^&bany+5X-;o*O`wU`+x5q12BMq=+`e0f-vAB ze||Ik`OBc90(3J#0I>iAmFf2%21!Yv97q%>_xm>^i2a8FLV}HlAfRG^0AlbRVNjfdgKH->)<41fMG{Q1Z53yA)}NHF>VM86o=*Z=~EMTLRklg;&|8vY-+ zh5rBh|L@=bKM?ZoKOp1JpMQV<{rmmr|L=eQfFkpA_5cJB<7Wm2?&_*f&z|!oCH%Iq zVPN&@1^xa7gnxhi`TP6#zh6KNfByZH;9_972M|CY z4S#m*VEXjw_teQhfB)LctFzlGTKpda#8eRa1F`|=V+j!s1_l{`0AhU0z`&K2_5c3; z?|=UACdB;@3Hy%?Ffai9!1(6P8-M@;JL%7#4}bn}Wu*W1^8Nqk|9@zxfJk8Yg2Rge z5=!5G`~e6c79IwMkAMHSMfCl8%kXF6O^EHl82Alg`~}6s-@pI={`>#;Kf}J}IRF8~ zc$$I1RCFdZQi0L={}0epF!T#d{sWT?V3G+SzyRi{Q|!NXWpDrh002ovPDHLkV1m+z B7Bv6> literal 0 HcmV?d00001 diff --git a/public/img/country_flags/cv.png b/public/img/country_flags/cv.png new file mode 100755 index 0000000000000000000000000000000000000000..a63f7eaf63c028615b2ded5878b5e14a7dbe962f GIT binary patch literal 529 zcmV+s0`C2ZP)*82p^=00=`tlmIeOf&y&dk@6oT z&CV8YCOfMzZ7b-;WHc4ffO0*K`UGc#9pHKW{n=1`s=Y4`sB0UGuD|F2(+4xFrs{6A+r|Fh*S$P>SRGs(%l z+O-QHfEa)O{tdAK=ovi517(5A-n@AO5I`)f0sl(Vm><0RZzq2;(QtLuuFT6X6;y@p zb-8u)9gSFj-3}5^z@SqUX4qfA01!aT3<}j|#!Osn3@^V(zxysO&hx#~ZN`f)vS*$d zpLlA?$HBnM&cM&k$j1o^^oKjY0t65vgZ)>ehy*ei5K#*ZyWju7F%Ll?01#jRixNU5 T4U6zw00000NkvXXu0mjf>cZT~ literal 0 HcmV?d00001 diff --git a/public/img/country_flags/cx.png b/public/img/country_flags/cx.png new file mode 100755 index 0000000000000000000000000000000000000000..48e31adbf4cc0074f40e95f87c1f103b91fe270e GIT binary patch literal 608 zcmV-m0-ybfP)Uz~~Q*L}q{yKmai^Fs!d`Rbj9@@=xl~KZbvQ|NZ^*|L@=bfBpc` zKOp-38^rz{DEepVv)`L`Yyt=%76t}3hHqR?<~aN{I`!9n&u`}MKs~?z|Ni|Cihlk3 z1w?Q7fI4M?HUI<=3&<7!82Y{{Q;@?^mDN?|=V) z{r~^ll=rtu5CcE}F#**BRkyY(adR>Mc_H{~T5^XRV~oVFSgBv#s=t2z{grn0SJIVV z|Ni~D^5u8)5(a<(0%_pmV<{@S{pr)+Nt0E6|KyV7{&xHKulY}Z0S*2Ic2cIyuhSoY z&3*a@=p=vu0%~ALNs$10lZEA9e*W!WzkWV{%9A7hYlh}ewm<)V{rd@2{rBIm&Idr} z{RV0P2mmnv&i?}d08{q%2kh+oUtbt0DGA2L@2{yn^Xv%Q%_4hjZ2Q{KfBuh*0fWjOcoPYi>{Qk`ejtlm`TPy!Q@c0|K1n4Ay00P>u ul!4(JQe+}W>@P40kp+Sq42=5$0t^6?P(4CrvcmZQ0000s1`2Y9I{~ve$KWO@Yukru?KTxgz{s9CK3*7Y} zZC_6Qf4TJkuQ&gHzW)Di+kXz$|9k=eg(Cm|XMz~|=g%L20Ahjo4`KiVr|y5|U;qEV z`p@?NKb!P_9;^R?@o<+hfSm*oKp+h;f&YIQ*bg)L>I;A4WVP82*P-nR{;Wu@%{Vvyu7@~4txIf`R>OD+-01E zIfRil07L2S-Mat*#Q65@TRuKMWMi;}EJy%|ff@h;2;_%%@7_UT@edf{0+7H22rvMY Ws9gjvbyTka0000? z0048MLcfb{@Lpld*gfdL?Z zSQvhRtN^J#x6%GQNHxSfxHgc;AD{-1tAIKH0tl!9q}uD*5!1Kl8Kk6va!f$;fJ%WL z`2Cv^NdEc54D$xi27mx!VfgXqT8ME7!~2)OPy-`SXu#NiAkhzFFflLy1Q-A_8F>@M S6G{sJ0000h<6BFn%a z@b8~2SoNP@zd$;E{sbbRuHQd?{QCI=sNwhbA3*&Qe}GP900=;09NYi^fU@pUdVa9*13;+Sd!tjgXKhXQEMobL97(p6<{RLvMGBN!7 j!N9=G@a-1^K!5=NcXWu!7_DDe00000NkvXXu0mjfeQx^H literal 0 HcmV?d00001 diff --git a/public/img/country_flags/dj.png b/public/img/country_flags/dj.png new file mode 100755 index 0000000000000000000000000000000000000000..582af364f8a9cb680628beae33cc9a2dbe0559f4 GIT binary patch literal 572 zcmV-C0>k}@P);we;9uK`S<6~zaM{qBL9B<0yBR7V`E_e2q4D) z|Nnpa!Ep64!{fLA8NdLj;oraifB*mg`;Xx-kn#6Fhzn-q&in!pKrBENJRA&WD*ySo z7*5@0`26EP69WTC_22)0z>5C-g{l_hVFa245I`UeKudt6h7^Mc@BgDW81BCO4-x~L z`sXhc{R3+I%fRsKA3y*x{R6rHsA1>M|6jifb2E4w{z{xB{`~z5CPC;Y7bk|9<`c2PXgjR$^CT@Hzz$KrBEfF#?VC z^aSdB^z+g5SJMxCJOGqNsQwQkfr0#&=?~CJ009Kjz|71H^!MIRd#Ajb^76;aUyQ$y z%m(TN#spBq-#`C>zGeUjAdrR+|30kwu=eoBL!3-pGMq9%bs!`E|ACMovw

4;Zk2 z8GbPU1Q5%#7t@Nb6*GKbU;u{yA29j{CVzn$|6qa)V3LCYAiw~8(_SNKujRx50000< KMNUMnLSTY(1rd4x literal 0 HcmV?d00001 diff --git a/public/img/country_flags/dk.png b/public/img/country_flags/dk.png new file mode 100755 index 0000000000000000000000000000000000000000..e2993d3c59ae78855f777c158a6aae6c1fb5c843 GIT binary patch literal 495 zcmVh!ZNvLM`<}kPiIA3?K?Zl!VJuS0ABN12uI2v;s z000mK68GQM4oDR3?|C6;zBc4LR82Q1eETXSa+3nD0Ad8%4|Ml`Fn}2U{~ypshW{9V zk%{T!hYtV&#KHiVV*o?2zW>+&Bgm+K00G4EikX==E9>w5`yf~S`o*<00G4K6dZ++hy)_Bw{QPEdi2K7 l5H1Kw2asrHVqgFWFaQRwS@oh;XP^K8002ovPDHLkV1foV*8Tth literal 0 HcmV?d00001 diff --git a/public/img/country_flags/dm.png b/public/img/country_flags/dm.png new file mode 100755 index 0000000000000000000000000000000000000000..5fbffcba3cb0f20016c9717614127b89db4c9664 GIT binary patch literal 620 zcmV-y0+aoTP)yt}{r~U(pZ|aU z{rk@#!dhJXj+vP`D&$&!2yPR?Jud5I`&tqo00z_V3?cpb$_)%#~li z5Bw?p&G@T5&5h~tB<;UIJ-`3_mgbQL+5iwhOdtm^{QZlh+Tg>NcGo{qR#UR9ewx4g zlzHy^uRp(j{rmOj?;oHBfB*n70M7pb{ow!s5r+W$=Kufw0RQ~_o$a1N^GPoTT-yiw z&XGVBSbe$r0&xrf|M~#~9P-zx0st`p&i?@b004Y@cH`sX`~3X;`}>j`1PlZ1WGkKd zF1~FN+w&9T}cJeJULy4V8r?0tNHM6WN&<2126ppbC05A-~q$vMCOd&N)3^rn&3WaiZo>@dB zxpL5=L>h@#UjVT%{Q39iA58Ths0J3s|NohoLF&Lt!8(8c18V>XAjZFc|1vT%{s#lF z^Kb%2CZ>-cJ^%y|<6Q;@;r#qR4;}z*|Nr|B$h_ab1b6QI%fu2>dIV_O?>~RRu82tM4=f|&Ke}8;oX5+eBw}y?G6)5=s|Nnnr@aNATAPEpaEN{Mj=m^OD z&%p5S|G&Tg{{H#<7bL;LEGj9<&cFmz{_j5mJbLs9Ab?m{m{|TZ{D1lB9Z2clKfnI{ z`ThHs^2c9)q;CF`l>Eoa3N#g>nv07INCE^93jWH3lKe}28-VENu!{qxMpm&svFOiawo%#4hT5U&FS z5Yzu(KY%Xz`RDh~-yl6N|NT~b@qz9A2lm1$Hf43G`D-AmnZXVNY5)izCWas1fbRMA z8?52ykLBVV-&q+tkKFpbbOC=r`2SzOfQo=l0_p*hfB*gk2q2IKpzD7Eo%H+Hk6%A8 za57|Q3rv;f;d1r)FDv_xg9F*eKs^8f#KQ37)2~0jMR){${rwHH2k3S7pO3`Z{#jf7 z{|`16Y&=9YkOT-IMh0&|hF9+yelRe6V}O#tcxPkSx96}BCoe=1&?OKCkOT-Y05IoG U$(*n^qyPW_07*qoM6N<$f?|9Y@c;k- literal 0 HcmV?d00001 diff --git a/public/img/country_flags/ec.png b/public/img/country_flags/ec.png new file mode 100755 index 0000000000000000000000000000000000000000..0caa0b1e785295d003869330fc4e073dce07e7f6 GIT binary patch literal 500 zcmV1sCzZm}g`N!}J$oTi?|G$4gL7*^D3`7C}Kmf5Y{CmeN)&f@km*M|^ zrvE_l-~a!AA&BAspa1{=fXIJ9!9O2vbOQts3j+fX{b%^|8m0my0Yd-4N`WN9@BjaR z=no^SIM8~400P%s(0a|A0FHhM?*o#se9Q zEZ;b|7ytr@MWm#zEz$bb`!9d~{{Q>$@1MW_!07MqKOpw+zkh)g(B$8L|49h*Ov!x= z5I`*NZ%IAX?Fbu$UUAQCA3`wxix_2=I& zAouq_SzðCcuS!~%52Kai0?gF&VORsRAR2~rJG2PFT1^!)w@)C_d-AAkU2Vc75Z z*R<@y`9z1vevIh)-p7{p`5C+7f|6l;f1_&?! X)GmJPc-xs)00000NkvXXu0mjfGFPrC literal 0 HcmV?d00001 diff --git a/public/img/country_flags/eg.png b/public/img/country_flags/eg.png new file mode 100755 index 0000000000000000000000000000000000000000..8a3f7a10b5757b006948ea4436fb242d02dc9a4e GIT binary patch literal 465 zcmV;?0WSWDP)LAHVtk{r`=k{y)(2e*gi*sIRYISXlV_^=qKp{(!;n-+xj9 zUjemETFMXP0$m6sfwJP_;%#kh009JYeOg-Dy?gh5gTXH_fG|KLm<2Qhs6|CZ<>JMQ z009IFR-loRl9E6vpeV=!FaTTi8)D~Q7yv2;2q2OXK!5=N{?|@pNV(X=00000NkvXX Hu0mjfG@sA` literal 0 HcmV?d00001 diff --git a/public/img/country_flags/eh.png b/public/img/country_flags/eh.png new file mode 100755 index 0000000000000000000000000000000000000000..90a1195b47a6f12c70d06cb0bd0e4ea88d7bfb03 GIT binary patch literal 508 zcmV`hKmn*~pz%QT=MPXRKmaj;?1ifa0xhloNlE|L zuK&--1mwx?-uoV+`qwW8u#*4+i1F_GyFwg7fByafIr{%Uh)$p>Ae%w(|Nq~=m~|`# z{`_H7QhIal96$iEC^sml1*F~kc<(nb4FCQ91q_kDz!3TkBLDsd`Sky<|4qNomi`s~ z`xoeVfB<3v8uI)9A4wibp!A=AfB%5B0nwj-e?TNKsQ>@@`|oeijK3Q@{{o!^5I~F! y*$fQd7#Mzm(H|HCnf(WhfND7yc3x%x2rvM-AWsdQI)rrq0000&(jx%j7OGE_~DVuFcQkgj@33fJv($pjj zgoNxWFM>pG#K4X+%S_Ys!f>f$mib36%ekHNec$;y5njB{!+Y`YzVGwA&4mTVh%ikU z03gD2Hn&LPeNu%hDG7PUvzrphs|^(-as$IIo1LmPya%Hc0Qn*6qc4XX3oKoa+Z)_XBQk8 znPA)XelBh#6J<)fj|w>7X+~Yun^@Bp4$+N z6L8rb{%QnJN{fql*fJH1L*2YjUlB~CXS&&LY)1V3h&68|x1_5-(4l3HUgs~3JvLXI z$_D=zL{dTnq9RK`-w~w|sCYqqA;@OoAE0!{9Gi+cF%zA>5*8OAiXWs z!A~!@Tb_6WJ;mn(q~>CYJ~Oq(|Mc`miY)G1d$)?S_lf*=dz3nd-8+hwz5w#U=!7L- z+Ve0W8Werm#o=KvYxRVVNtM9!poHk%m;Y{gxKdXC|Y{ fc0^aUlspXz7vm>S7OoCUZUIwXLGJ6E%Z+~lY(hhH literal 0 HcmV?d00001 diff --git a/public/img/country_flags/es.png b/public/img/country_flags/es.png new file mode 100755 index 0000000000000000000000000000000000000000..c2de2d7111e3cb59cf6511dd2ab045e824bdb43e GIT binary patch literal 469 zcmV;`0V@89P)@|4`Xj5kLT%`al?B=W5I`&pe;NKW0^Ri&h`xRJ_x;0v zUa?=y?0^3M|NZ~}FE9c#{{3cP{Qd6}13&;Vf!z&M{pZWqKYu4MFm$tgedF}w=P#IQ z7-9gT-$11R0mKA$(qEu4%ok$*y!^wMRm*x;`R7|k6yu?K{s8?55I{^|9{?Tjhec2I zv+6&FhFWG_BbNVc|Ns94tNRJp!0`V!Py;{!F#$2enB#XZaohg-5%Tlk#oa&nzQW9g zl0Y{D4gK?n0U&@Fe=;yIr=|V7caH%YEYL84k`Tt9-wc2LGODP&y?7BIfLMT@X8Qey zK~fSFpuiXa$^kk7RCwBA zWQbH``0|MX0{;DB`1Ob3-!Fz=zZw28fY1*HhF@R=VQ?@21P}|ur+3w` z_wT=dfByab`{x&s{PXYspTB>919^Y{{Qd`I{N9v10U&@_7=ExZ{APUe{`KE~Al1MB z{rb!Jhml3<_uqeCzux)%bv7|NkFAvw@aK@N+YWG5`bsF#yj00sZ{|0ReUZ0OJ4u`~d&_lgxzd z_7grGtKje;)ax=32IqJ>VE_O6|Nr{|0Uz@6!2*a0?AgCSJ_s@V{`!jztXlEUzkg2h zOW%AK0ILQg2A~)NKmdU>0L=y=29PKt(~s@sMWK)O87MU|4G0ImPt*+y7s`{{I1L_{;G9FVHyv z0R+;(^!pEkq$JpwzYKqVGyVRp{reTr#sB4{{{Q&{G@Ah!GGGK$3=lw!Ux87Egcwk{ eXi`7`5MTfy3O%OUuKb?>0000SU^I57#IKohy|#uQJDd#=|98& zUrgMLRb~JG{$u$2m+|*M=0AU#{`@s%|H#1h=ih%I`g`z86F>lpk-UOTf}FCMD80)oDIYC+&q4vMR0s0&DpH|L=c>-#`S^3sJ`m zRPz_88i@Y=|MmYr*an6_AO0`^1P}|uzkh%JGXi;k8UFtPIt#4m|G)nbm0000FP2AE)Ir2{}>qlLBSs|`Qg(SfB<4)VqlOE;Q=cD|Nnn$ zna02W|Nj1E&`=Tpav_4q;M$#E00G4E4{SI@`q`VGKvVzz{r4XmU}R+c_2(Zz0I~c7 zs`v*r?Dty(;z&PFFXX zRA5t=4x{1SIibD)Vqy6A2V^D4P_SySA|L?j2ip1XFA)9%V~_%1r~w2J3=}{2Oiu1 f7(RXZ0uW#T>&I!FfdIJb00000NkvXXu0mjfj-u42 literal 0 HcmV?d00001 diff --git a/public/img/country_flags/fj.png b/public/img/country_flags/fj.png new file mode 100755 index 0000000000000000000000000000000000000000..cee998892eb316c3293ef2d52afec9218bdbbc03 GIT binary patch literal 610 zcmV-o0-gPdP)2C05Lr?a%1G+Sb3M_ z-f!*)-&mJ@lxC7weD@!u;s2li|9<}wjr{Zf&o8mqKR`Cn4*&rGF#yj01QaSLwCD}R z0w(ww8v*|PzTN}jB`Pj8{QK|!{{8;|gOCLd|L9jy6{oELG6Dcq@B)aDq496GGsCmb z5T7wXTnzN$?|=Wl{r{i6vr6{G)xV#={AXc)t!L+QBoiQjSb+Zc`=1dU2n>I~p8E|B z6OfY_{`1$ji1Pn`9_T5yZrhJfj0}g~00a;V(9A!7nZZWFG{8az7^c++|9dI@cmDl* z!Nvb)UorrL86bdIfbsI1fk}{;;V;BV|AE?oY(}v2K-{x07*6Kx`SfB<4-V2A|A=r2_C101z~ iU~vp#0R6xN5MThlzdwv9U#bcK0000}CO1*!he@c;k+ zfB*mg{rg{#hxPwIh8G{d0|o#7{Raep|AEAFCm#U_AQp((@4x;AD*Xo({rB(3@4q1y z(m>63@(hzyJPYWMEi%>@iRTXyxzU zAo>qT2S^W413&<={Q1WKlmMyz`(Hzv@8AD_R~~+N^7b200Z@m&0zc4{Q@7rNwftxJ z^$Q??fEu9g1Db8CAq;fMkDq@pJa`8*&sI~^TtyHla^%`8uswf)HUI<=3()%@Pl0^! zf8Ui?K|l%XeRX{Qd(n@juAHz(D*15I`UefB!>$cK-hRUqIf!|Kj}Y zKt+#Ue>r^ZHOMRf{y`i940V72V*2%m0pg)Q5O4ka4>U8*PAAEH2(%ZZ;ol#C0AiF(UW*)& q3=s4Oj6m#vP&UxAe?ZIt5MTgFMVEBke8_SD0000BE0lK=nzFYgc)d0A2*B+AFf z2joHok-@WP&j1351!6Wt`q9fjf1W;g`1ALFY=DuG5oiNI0I|ST{|2JJ|Ni~?`|A$_ zRt*pr0t5gt0M7pe4IJopi4@}M{rvp?{Qds``}+I+|3-D|`uqR;{Qmm<|NHy?`uqO- z{Qn;q1i_Qs0*LV@1A}N|@t-FT{{IC^{`vn0sPGp^)o&2vABgb_!eEtCyu9%!Kmf6* zGhfda5_|CT&#%8A#S0%rhKer*8VNG{57cZ3sU*g7is3Rq0I|G(Bf-nd3vr@r@vHy8 ze*OIQ@9-oMOb-A(eJ@7=Ab?mP;SCW2x*O<#U%#>Y7zqCS`2!F@APw*!ml9!S{vjhP z$_zA&0R;fLP(1(v#Q5^%OL#2G%0Af7VC%@R_vTF*lgG%);U`26kn-@hOg zU%!6+4+cOs(0HIde9xZz`}Onxub&LUB0x(30+2WcIRJn#2ut|?gWYu1Cf+!-K%B8# zdf?1WA}#uZ8oj7u>$I1i0Al&`=O0k%-@icgAIJnM0xA6maSq6BK-ECw|NZ*S`0Lj% z1_pot6puj;05Ax`F!=umqj7^frO?t|3^&I1kxUq9yECc+jQpY84SWH_0#pxl$?v~F z@*hy-KN0|X07U)z`4{NpU%#2aHUI<=%a31wK(7Du52Oc(|3O^?R1IN+RRjI-n*kVB z3=9AP#PZ|EACPLGJ%9cJNh|>9B%spYzZw7h1%?tp0I_@ndg9MNE>313@6R75NcceF zkr51-#U+7;F#`Sf7i0rK0I_`g_NQ&Zk)EZ(2O=d>QH$KN3zEi7S9u{+2K>GX4ds`2QcM z=+A$K-~a!^(JwH9Fn%*K{{Cdb01!YdV9)*qi~a#?`wdg{8%Z^Y!NB>w;@|&31~6!UgVU(k2*|8J(R-+sudaynhucHbwAMTnor{mwqO^w7JHzaBsT z{O^B8RYf5+LvDs&KmRKVd78=o{`1#HTiEo_OolaGleS)G+IQ#sUI`b*pv<`1zCJ=H0jd{{2S>p`ri%{LsXJ%FbMS z$#S`6f|?OG!^Jxczkf6Q`UNF{l0Sd`ad7zm>({^EzyAS6{{CgrkluOb3l1A>ZU2~A zK+FZ=zkmP!`TOVhpFbzBzFaPmD2$N3;+$pK?>zdet`f0002ovPDHLkV1gy;I?Vt8 literal 0 HcmV?d00001 diff --git a/public/img/country_flags/gd.png b/public/img/country_flags/gd.png new file mode 100755 index 0000000000000000000000000000000000000000..9ab57f5489bb9ebb6450cb27f4efe0cfb466144e GIT binary patch literal 637 zcmV-@0)qXCP)@|2i2MUNJEAGBA`g{QJZ3ub1IpEW7h{~Z4K zJ3#Qa=Q1XS-;5h0el2+U>(Te$zyAID&HDTIKad810Ad2W{jW5`@9y)AKr^5J{4Kqa z;cw*UKQW)B)-pW&@%z{RUqB7N{{H&&_Ycr?fB*vd;rG8kGw%JlqX`uK!_D~nn&&T_ z*-Q+-7;nUX=6mt$`7e;3-;BTi{QC{m01!YRC;j>JdmqEEKMa4Icz%Tm{+4F_^}h({ z_1{sye%WyRp84|E^4Gur0Kxx1e;6150*D2Oe>41%=Kmef^V^IA7&yOx!2%AYU;o*D z%dq`!;`!w)_PhDb-(PS30!;@9Adn3rpZ_$9NHVegX88Y?;V;N+#{WPzFy?-P;*ar< zJ?CFrnZE^h{{CWM00h;Fvzl@K2fHp9I6dqaaxb00=Mu XLcuQ~?TP?t00000NkvXXu0mjf`7udf literal 0 HcmV?d00001 diff --git a/public/img/country_flags/ge.png b/public/img/country_flags/ge.png new file mode 100755 index 0000000000000000000000000000000000000000..728d97078df1d07241ae605dff2f2cac463be72e GIT binary patch literal 594 zcmV-Y0^8x|9^h-OG^F+g7@$L?BC01YQ`Wbb?43;K=s|G$6#bNnb!1Cx>Qe>QfY2m>?Izi&U8 z1o&Rm*8l_%%a6%3nS?}u4*37)&;Q?l7=Q-<`}?1Z>;KQLY|KmGQ@ZWEch0Jnt zUmm{%2p|@g=ujpTGX@n^21dqzKYxO4`1a@2NuYivJ4XgKw*UYBFf%g!{qd7YP>5~& zE`R_4F#yj00OjT7{QUg;`}^~|xBB|}`T6Q!vcs262Lz;t$n|1+qbnVARhhy8{z5C(*C%JTg?tEV3%;s64O@&5h$(1-*>2%Ak`A87BFlP7^( gh&l)WvH=1N0MfQja}g1cO8@`>07*qoM6N<$g4hNuZ2$lO literal 0 HcmV?d00001 diff --git a/public/img/country_flags/gf.png b/public/img/country_flags/gf.png new file mode 100755 index 0000000000000000000000000000000000000000..8332c4ec23c853944c29b02d7b32a88033f48a71 GIT binary patch literal 545 zcmV++0^a?JP)lgG%);U`26kn-@hOg zU%!6+4+cOs(0HIde9xZz`}Onxub&LUB0x(30+2WcIRJn#2ut|?gWYu1Cf+!-K%B8# zdf?1WA}#uZ8oj7u>$I1i0Al&`=O0k%-@icgAIJnM0xA6maSq6BK-ECw|NZ*S`0Lj% z1_pot6puj;05Ax`F!=umqj7^frO?t|3^&I1kxUq9yECc+jQpY84SWH_0#pxl$?v~F z@*hy-KN0|X07U)z`4{NpU%#2aHUI<=%a31wK(7Du52Oc(|3O^?R1IN+RRjI-n*kVB z3=9AP#PZ|EACPLGJ%9cJNh|>9B%spYzZw7h1%?tp0I_@ndg9MNE>313@6R75NcceF zkr51-#U+7;F#`Sf7i0rK0I_`g_NQ&ZRCwBA z{Lg>@|4`Xj5kLT%`al?B=W5I`(ov;U*021*0XgD3^5{teN< z@cTDV13&-@;@|`T5QYI@3O)ok?1DO<2trehc#kXh!0Z4iC6of!=I9L4Jz5Qk(jP`l zJOKo8(qFLXAF#IH8`u5XwDI@PAHNy@|4L4RsD^0x1N0+605O4m05bkR14QCiMDQ;; z>0h$aKjWi;+@CNFzZm}i25JBZAQt8_hOB_!_dovn^Y72^zrTL{{r&6TuiuWpfB*e$ zwD}j1{Ph<^0%eu?|D0`P00`k|9}4iHT+`$2p~p=WCoxfpgkZGj{YEt g{DC2GLI4Ob02tU}a;hkw5&!@I07*qoM6N<$g4!w08~^|S literal 0 HcmV?d00001 diff --git a/public/img/country_flags/gi.png b/public/img/country_flags/gi.png new file mode 100755 index 0000000000000000000000000000000000000000..e76797f62fedcbfca8c83c51951680d6a6e9081f GIT binary patch literal 463 zcmV;=0WkiFP)VoB37QQ+R{;bN6GSUi z+kb{HA3p-oUOth}-@kwP{0U71P%%INK{Y@H82)oDp3V0Dt?T`39Pi$;RTl%zL?{P4 z2_S%&kW~Z0x6qjPzkeV9`s>}VU!Q7P|1&Wm)PrpR2q2b!5Hle5FfeebsWZ)5spT9vc*+3O2}Iw&|Nr{sqx*C2&8&?7c>c4n{QvVC zD9-TjFQbI?+i42`0*K|`>)%Y*uQL4o{r~rGhChE9{`~$Pz`@Ka$@uLpb; literal 0 HcmV?d00001 diff --git a/public/img/country_flags/gl.png b/public/img/country_flags/gl.png new file mode 100755 index 0000000000000000000000000000000000000000..ef12a73bf9628ff5a67b81bd980d9c5d2b2c0f05 GIT binary patch literal 470 zcmV;{0V)28P)J{teTOL@z0+>00G1VRSiT77W_YWkm2*^|KGm- zfAHXcOY8ruSJ7+$Itd_vn4oTd_U!+mLkz$F{Qvdq|L@-*^6S_C%a&nk00990)`(*=-xesBS%qG0|gf?f~#iu^9N|j9|i`10Ac}ZUz>% literal 0 HcmV?d00001 diff --git a/public/img/country_flags/gm.png b/public/img/country_flags/gm.png new file mode 100755 index 0000000000000000000000000000000000000000..0720b667aff506d7892c5c301af04e6bbf932751 GIT binary patch literal 493 zcmVwRhhvIeu00_fCKU~B)yH$s9sXS^B!W{?M(W&}hPbMwO z;*cg65E@7haJ!!XVgYOW|Le(9kkY?@fpY);{sqc`6amR!K*q2CzkUI^Y_hUI(*XjA zMdSH%VNp?r|Ns620Z1T|}fB<6Q zlw#oF`Oo_sVk+2%KTsoq3?TP6gz@)3Ki_`_=6?VI#CZSdeQ9y&f57m8xf%uh13`xW zAjrhTbmsgSfB<4-$Y)3kNW1sx-tWJ^f#}!YUqA$5fJva>FJQR-`S({vK;>fVMSuWe z0mcW=Ig;FxKxv@ppTFP`1*!N0BL9M&0|dYz`1hCL7Xv^5F*2kxF#KQuvOqEU3km&! jiTr^fV1zR<00bBS-TrJ5MX@2w00000NkvXXu0mjfGz`_@ literal 0 HcmV?d00001 diff --git a/public/img/country_flags/gn.png b/public/img/country_flags/gn.png new file mode 100755 index 0000000000000000000000000000000000000000..ea660b01faefde01ad2527a6abcf7d1a5c1b0526 GIT binary patch literal 480 zcmV<60U!Q}P)@|A6>41A`El{SSoRd}EL3@|NdrR`1}9=ZwUJP z@AvHwzkdDu1yn7|BMY<#Ab?oFX8(t({tZ$6>;L~hU=2XVuU~(E|N0Bk07O6y00G1T zbT=bV^`Afg|NLS2{ReI~M8m&-NE-fuGynt;*hzmtW+Q3%1=j#1fvO=I{`~y|)Bq4b zU?=?r84r{KY4``%041R|`~%zYhXEjfz)k`h|LYgXRlk0r+3@c_)IERx{rUUv4^RU@ z0D&|xgN*;p0Mzyy>QQ8EKn=iP{qyfH5CNS85I`UeOuzpyNJ@hA{P_#yFfjaPWc&?| zr{By>f0X$D{QV0G@4r9|{}=!Qi18~pg5ikaD#Jf9Xfy-Svu_Nh0nj)GNi#731Q-A_ W8E1tdJ(&;y0000P)fLk0D%}*I7ff3uKv?i+N*~ULWZ>4 zW5%k%a3T{@*`z6pma6eF$JtK+F@C*&o=d^t|Ns9GOCXH@*Z?CVuP#7nB5Y|1kmu|NQ^=?>FP0zrYX%2q2aZH;)P`n*#-K1r9WbfYOYN z??RUX1P~*`M}`*mir*mb{sxCVG>rbhqT(MY2L1y54rHu+wi6(L7#SX-$0bVa{(;3h egu%oB5MTiLH(5{VMZMqv0000u-`~Ig{`~p> z=MRwl_xtx>F!}G#@4vq{&D;bKKrBFA+{}CzK0Nsg1pog2{{I_D14*DX1pWH^3y6RW zSzcL&Zwvqd#Pa7K10w^wlkmIISh(4kI3GTJhN2$mzJLD! z0*D0|DC|s(0(}1j8UFtJ4HA_S5@P=M@9)2VV#30}-~k05FvNkXnV5ck`2-L^EDTKl zn1259n3DG7^QXUm{{H**3#f!E|`n6Mz6>fhdBj29g(UfB*aM-=Dw#|NaG$fByXc1LXhxFC@THKjjKQ05P&# zA9gbr+SsEBRPB^?1!&T?30hEBFhHFGv5AR^>DH}B00G4E=NHV45I6k$@0N4rAH*g9 z{zDN+_&*OP%Y{RC0Ro8e#fvv0A_7PTA~XKMG0?q08}8kE2oOLl>koag&}IJi^WT4% zN&g{c!yE%t3}J9_Fdy0V1t5S}4xV|TB*XjR%dfvcU;YDm6wdeu;Q~GU4JP)J`S$qz^!f=A3G?{>83*rF;63vB|NHv-`~3j>`uzF%_#GV;x3_Tu z05Jg0{{&$2KZ#9J4f5RdzRdzC6Ae5p=(o+T{uB7y^ZNSwN=h$cVmJ#62-nx+ob%l!TKAE1V~I16T$|E1+i#l`u&y!03t z7ytqQF#yj01Tv_Ick58z;rzG}_y7F-%)*RRAv1X@1^f8;`}+eA4+yBm#PKA>w6y_L zQ&a!|00ICp0M7peY0>I$KneW(?7rp&{QCm?`vUdU)NRiG`uYI-`}gbY@;Dai8vP{| z4h7uY=>Px#0*Hx$0pxvffc*LW3+PFp{}_J#0ty11^n2!vLjod>mX_Jy|Cz2{eHy3% zAb?mH7=HZ$2N_TWSP@V&gaITO7A`uvc=2f<_uIFxDk_pd4FCZI)bI_+mz3lOl7E0f z{^u`PGlT&Y`3GeD{rm6lUtS;y)Bq4bz=+dkVE6#Ehk@Y-82x6z3jhKP0OI&0DF;s+ Q-T(jq07*qoM6N<$f)`^cRsaA1 literal 0 HcmV?d00001 diff --git a/public/img/country_flags/gt.png b/public/img/country_flags/gt.png new file mode 100755 index 0000000000000000000000000000000000000000..c43a70d36424b66f1627216ad988cd23a4be9285 GIT binary patch literal 493 zcmV|9}7f|M~m>&%ghRD!%{(5DNnfNcHoNKp}=7 ze;EG#|IZ9j4hBF)cVB@t{Qmo2T96TF4?qC109^ty;2+Qi2B0xObN{1)Uw^{8{`KJ4gZ0H zP(J{D2s9NSfWR7{27t^!x8NVhN&g{E`Ueb#e*ggla?+pwj3Cv27=VUhwE^V&zaVoN z82$hR5DUYve}Dck14V%vK+Z)23?LgAK*7ZT@-zbjKmf4-BkT7+CNVw+pd~+kF)%WM zL>VCz0|PT7gS-eZm>Gd?16jcE4PH%g~!@=<9&!2x_@aEGW jS9>vVD)6R*AQp(xFW-Wt{{mJ2|NHk};Jc-)O#kPzME?5A`1{ZQ-#|To{!0im{$XGM z2p}eq*?*y`{{RjC%V7Pj<(}hvo^Z>=hrJ-xK=d0#0&M^~2_S%&fR6tAp8=>2$p8C? z0q7F5H=q9h`}OesIT^OczkdG%sRkpU6i@>|0I@Lq1-k@j0La}yZU2}V|JX_T{{Q!% z>EEtDfBydd!vuBJUq+w?fB<6o2X+#W53~WS;s2M<|9d72Yseq)`11cZJHs!aS-+wF z1}X*!ASNLG{ST%Ze}QTk7ytqY@{}>p)on&BOV)*xm;om>72$%sP!HhqD7ytr@1teTu4J7{m`^Wh2AGglm{~UiA z82u;s5_9PZ|FH1)~3de={)r1*?FgUtsi`kx>?C4?qC1 zfX)8@|LZe*d2K>lZN0{{I4*^b@QIr~;(tA5`a`e}Ddhy$ldQAPvkwr9kh3`~WiS7Zcc2 zh-#qDKOj^7{QC<;3||-k0tlpm>GvN7NlB2NKYyjTe{-?^h8PLd@aHekus?tQg2Ee! zfN=^CK#X6(5e!Gd)(i|h;JEn(j5jcXFhHq*fkB7?Aiw}&uW^ngBcx#f0000J&k9ol;AaCAG*Vvs6lsG2f+AJUecp&K4&zS7@MzJZZ+RCHJO2~-cn~)8*ZB# z%#~(Seaqctb3On>xdArM!+zLfe2=iS%3k1HK82I)yo62#|&;D2*%o~N(LQ$HrxFU=@<#wgQDty7s|5?>qxBTrc>UoBZ!}1le z#)a`Pq~$aEPO=D0fO80I7h5SSMqU=q48*j9Qb*%7#+Pi|ervSf?0bSFwKsAPn1FO| zKH_&kh#AJmvOUSnl~!1AmcaNJM5awz`0DF46>zWZuCh$z(7uBp0to4w2iu-uj zV9oc#M;CkJ!OT_8;~(;r&Cw`0K3r=(%@VWyiIA#;S}+n)^}q>|)QZ|IaYyyY!;frq z6mATysX~aM!z!n$rJ$=27fpoIr3iB{q|Gr32uDRa3PcNj==OQGHve|07^1DbtUgzuEQ=j%rDF literal 0 HcmV?d00001 diff --git a/public/img/country_flags/hk.png b/public/img/country_flags/hk.png new file mode 100755 index 0000000000000000000000000000000000000000..d5c380ca9d84d30674f05b95c2f645b500626c07 GIT binary patch literal 527 zcmV+q0`UEbP)00;JD`K-EmLvOuK( z0R&e6?>|)a-@i;iz|8zVIqAQ;I)|_@BNM~FU%wy-s0ZjAfB<3vx(uZH&mV?Ae;64V zIcsYEzkmP#{)7J;N}0Ju0muUq%~ z^Jl0Zz)k`PASO^y0(FCg{s2v4J zf4+RlSW)rg;lp2_Kl2?q^5yYkpazCNzyJOD%k=jzP%%INf#Tuc?>~%^l1w0DfWH6z z1E^V4lvz;l%d1x`a&jQQ{ROE8h7C|LKmaj5WMKG(8n4KVKd5of#=rm&U;y%qJ?5>3 RVzdAN002ovPDHLkV1mTk^F06n literal 0 HcmV?d00001 diff --git a/public/img/country_flags/hm.png b/public/img/country_flags/hm.png new file mode 100755 index 0000000000000000000000000000000000000000..a01389a745d51e16b01a9dc0a707572564a17625 GIT binary patch literal 673 zcmV;S0$%-zP)>fJ3En$GhGS>sbE%%m3$AD)q?8M9y>88-}kR7#RKlk!P~Y_PLuF7~U~3`~nC7 zF#yj00ZUDdpLsm{7ajP|&HwoK0Usg|6%f4L_{`Mi{rvv-`ukf=Ed&Gs-sA7L!Q7*a zj{*QO0M7pb%?Sw^g@yy{>ihEY{`vU@3=8@G0rvO$i3mOL`~mv-`W+b$Mmr&io5dg< z5v!7q0*L95jt`TzK8Kd(Utv)OSp_aLv){6ccV+Z`{Q2+asKUU&aO3`Kpz6wW8wp`< z28M3{0mSqnB#A*-c*8%1=RD#sSOwMznKA3=e&iEzwo{cA=PgXK`2OQ}gqId83!|%* zA_Kz@fB*n70M7pdECCwp4H&@R`1|(w-}M5x*74i)0}%fAt;XafA{48))#>Z>?CD#}*e}Ret0tl$#*RMZ7}Jl7Z|M45`5*URzH9L z{rmSnPy;{!u>dsyO%meg+00000NkvXX Hu0mjfN{&}S literal 0 HcmV?d00001 diff --git a/public/img/country_flags/hn.png b/public/img/country_flags/hn.png new file mode 100755 index 0000000000000000000000000000000000000000..96f838859fd2aed975f5f4134050fdbc0486ce1e GIT binary patch literal 537 zcmV+!0_OdRP)yNpn^YtfB*U? zE6K^g@B<)#SlTBTcsfda`|@_0R#{e$UZ3l|Ic6l=B#}T zWCl5lg}RFY8S^$g{qgfJOdc2ve*glAv3c@IFK6|y-~NDH^$#cn3{a5k!L9^_5>O@B z$^W^zSlTD;0tg^R28Q0WdbfK|zW)9V43odV{`~*->+kR1AO=tbO#T4}-G3E1?u#4x z0Ro5x7#++k42m+GppXWk{}2W^;6Y*k7i(1vOT1`b$6{=&w9#5#oJ b00=Mu*}Zhb7k&Za00000NkvXXu0mjfKokPk literal 0 HcmV?d00001 diff --git a/public/img/country_flags/hr.png b/public/img/country_flags/hr.png new file mode 100755 index 0000000000000000000000000000000000000000..696b515460ddb670acb7e9de4438aaf21fc5fb77 GIT binary patch literal 524 zcmV+n0`vWeP)@|4`Xj5kLU3fF!G{fyDoR{}_Q36Vv~H|Ns5{^Z)NZrr*Dqe*gae=g)r_`DNuw zfB*t(VEF(4$y0{Ee}M=nS{mB(NB;kxJOBSE{F_{n`2Y8>|G$0##TjK~fi?gH5EIz! z|8Uj6|NiY-l-<0|OxA@1MWlzkmOI;leLR z$De1;{4g{7^y$;LZ{NOt{rct07a;lk`E!5(f@%OM1_PkhC8`eE9egqWbga z&p;hdpFRZ$Adm)#>fe8W4*Ct02B|hR1*-n0rS<;(dx&bFRY1n$$BzL5h>;=uaC^Mc z(+@v?|NZ~x@4w%F|9yUZW7+yTpo@LH>e(bUfFghX{rmIxzpNnpimU?w0mQ=a>kp9s z8>AGffmuv7DrFgv!3yU7{`2q8f1ngFoPlxn4AoS-S!;e2821p2q{((sbfB+=MK@k8U5Cg$|VB~~? z2XKWZk_lAZtGhi{|56nPieMKY$Bq=4KgZ0muK;2JYWn}5;nka8K-GUCa!{rJenZIL z|9<}gF~mh#ftCOS5DU<%|Ns8~1)2?0{RgZLWF&|Ls)lL+iU2hL1Q5&LKMX(>AUTM^ zNU9+S#0FXN@8@rz^Zx+^5DWL07wmsTIe-5EX@IBzTJ`52%kO`z5F362$-h7b*KaNc zh6exv#P}EJiR%3Sk01R1^NZmZ(C**=fB*Xb3rzn04HN{CU^bJS()(Sf00M~R4FdxY z(0f3MKYtkh0!g5OAQFsz{e$TF`x|H}%fCO*7#IKo2o$W~FaxWA8VofRr202h8w1#j zz=!|{Ah3qte;CCj89_$={rBfLBSbS$5J>(7`GW}-*g)q41Q6q6a2)=FMdm+9l%onl dL?8elzyJ+{hsuy4pm6{I002ovPDHLkV1hP90PyYjz{{0J*12TXlP$`i71!VmC|LYf!%PK1iv=Z0I@Lq zVgLrRB#$I8Q2qeT`3KSlX8!?(3s3+U9e@9T1Mx3N13&;VGFUSJ^?=Y13Wr{r~&-|6d^ahvDC! z|G)qK`}^nrA0Ybu|2K$nd)6X=0AgWa1{?O`IRi-PU$8V7{r&w9sOb0ae<0Pr|Nr{M zEF}%J0U&@_82&N|Y=e#y>zg27mx!0Xgp5*S}vr z{r~fq0csvl>92pk!P<5VDfB!NI2#TFQ3lKnzfB%Al=06ZHfFW+c#KiRe{d<4_V)^&)A0s0pNIe5S)eu>r zF8~6F38(?TQZ#J<0R*xEXct5e0}KG|WIzExE=U%r7$AT^8h-rv@ecwRzz_$3Xaxu` Y0RLik?wUgPu>b%707*qoM6N<$f;0ZTz5oCK literal 0 HcmV?d00001 diff --git a/public/img/country_flags/ie.png b/public/img/country_flags/ie.png new file mode 100755 index 0000000000000000000000000000000000000000..26baa31e182ddd14106e67de1ac092a7da8e4899 GIT binary patch literal 481 zcmV<70UrK|P)1Ab?mHSU}=WzCQi??=KL1`SXRBmG?g! zeExSVgb4YXfjasA0Ybs`#&c5^XvcLUqDM3{{9AP00d^^H2e48-+%sM)d02u=%hct8G!N( z3;+QH((o5-_OE}xfO;@2_y=+i*h!3FCjkTyNW*WSt$#tPfB*dj3@CIxKqoQ$2DuvO z1O^6x00KJ+r1UogVe!Ksu!etsL5P6?Ab?navG)7lA4zUWkT?GPWdcP410y3N0|YR! zFo-FE!v&-P=p=vuVq_>~VE6=zV^DnmVAx)=U5ZNz6vaS)0m(NHWW2-wfs+9Q00bBS XO2cxg3=*#z00000NkvXXu0mjf|9Z^l literal 0 HcmV?d00001 diff --git a/public/img/country_flags/il.png b/public/img/country_flags/il.png new file mode 100755 index 0000000000000000000000000000000000000000..2ca772d0b79b255872cde2fb29060bbbbad950f2 GIT binary patch literal 431 zcmV;g0Z{&lP)WlqUuh`uiUU82D1+EBLb>EWz|Nj3k zj6%@>aVJ0ku|Ql5RsEk~{?`9D9{=ZO{V&1vKX2lHHJSgJ0SFC1p8y096I?Y|?05b{XcgblKTHZ zfByjpAQrd=h&?HOAa>`R|6Hv9XB30N3RxDY7$AV4en1PH(j<7uAT&Tc4G=&q@-F{c z8i9e$01Rv(35=ybe;NM%WdxES!M~uG0dj%y@b5pvikg1_0mOLw_HE>d#AF}?ph|!M Z0{|%qc@l5wel7q2002ovPDHLkV1m6PxaI%= literal 0 HcmV?d00001 diff --git a/public/img/country_flags/in.png b/public/img/country_flags/in.png new file mode 100755 index 0000000000000000000000000000000000000000..e4d7e81a98d705da8d7054e77e7d311805659678 GIT binary patch literal 503 zcmVl^KlW*80IEmzVa(K3*_6 zG7fg0I9Zj&0woGah`r_&Kwu3FK=xChQigwjfh>?7kc!_h@)sEWW@MKI+5iwhEdRtz z89B8WSj7JS|MwrL=l|b3uZ7Osk^B4auaUxSRgtG4v;Y11_x}$gi|9Y8?EnG9`1|i) zCPv2p|ADsrhuF4k`@Nr^zUpfTpS$xp!A}Wj4A3Yb2~_s}<0pUsVqyY2p8>1`g1&zJ zsVvR4Yya)fUw{4wtNss>0tLxGfB<5GmnRH!zxweH8<(Mq0N7e&^ba6_ z7#WHgIs!VLeti1p-=9B!fB*jb=l8$ge}LrQ-#`%%`S%Y9{re-sFSERHIY0ohF#KVF z2*K4Ml>Ykz*ZJq)UtlmW{9*tIAQm77@e!0?Mfa}mS!7%=x2KmY(S z0M7pbTSu9imq`K?75n7n_u%3MB_szB3#hl|A07@E6$<6-<>30S5egmg@cSkZL+Ix9 z0st`p&i@3O7{&wE8wU3C1oia^`T7CwI=ckcau@Q71iA1L?!*;{7^Nm*&$?GLJd|NjPb3=0E0KfCb4 z#nAu(05Jg0{{-+tQ26`)!^rI-{P_e371QGlF%9qW_x;Sb;QRdk{r*xJJ*zy?_rWgu z&$=$1lYjzR(%pFe+p|Ni$IOoG{eTR0XemVUia$~;LXXZeDO z0096o0M7peXKyl1N+90g@dyY5ARP|N&gc{s2^<#+#>?j*9u48*^A9U5-*(6(kqBPq zC^|QM0*Gbi#3NFYyicBf1{wyk;Wx-spzFb0kX0fvuWZ`CzyHEKd)}3G%ew&r05Jg0 z{{dZPvvzMc=Kqyj=IHYZ2nGGUe(f)J7ZwZK+v5TV s=!a5B6v9X#`iH^&14cjw13-WQ0BQ>oQ(TIK+W-In07*qoM6N<$f`&OQ@|4`Xj5kLT%`al?B=W5P+gNxB&nJfgpGfTOih;e`=>T z5jZ8;?_>v5xi(~iU^udv!6f5$jpNVh2O?$m1OPDr&i?@Y{r&#_{`vm=wBfh>{Qda+ z{Pz3$!R5rm=Ee2+`0e-d?)LGV)1LD5^!4@i=jZ1F2;wA$U5|F$_;B;f&rf1p(qbG! zCte(9VPa-y5lGpbZn4ZFhzcT9$vfdqmuG z@j$ZG>u9())mkwqmYHSd7eFi*FJ3%$?AX0~_kM%HFED^GKqQ#;=g)7T_f%9=fX)F3 zAdr)QMoCIaf{X{6{|BNG$o>N%f#5F;02KoS5XlH2zyJ$0KZ{``H1_}i002ovPDHLk FV1nFR>VE(L literal 0 HcmV?d00001 diff --git a/public/img/country_flags/ir.png b/public/img/country_flags/ir.png new file mode 100755 index 0000000000000000000000000000000000000000..c5fd136aee534ecb59914e336cad18d18ead2a4a GIT binary patch literal 512 zcmV+b0{{JqP)r;gUH{1e{Y(x2_S%2fSMQ?7@vH7`tSc=xS~J*|Ni>>`_JFszyAFKs{8d9NdA)L zm1AIH00;mv0M7pew_3Ln1`-ek5ajjb8VVZW^Whu|9pCfc910uY_2L}~8{YEX9t$4Z z@!Kj9D)d(L0*LYN-@lBEj6f&-|Nox>4F7-s`Ty{t|Ns8~x3>Pz!S){pfXY67`UDU_ zOc38f#US*GW&hv2{?Eqpf6;>f$N=n5fB<4bR}BO)G5?=F{eR-b|HMQT_5c3^H2?$< zb1geNgNn-kGiMln{`!CM;{TsNL8PAke-;*?JV+Z*eYOvhv$==KunT1sF?AKYlWZiGf7_{AKv_o8k9wMiBcC z1B3*kzkfmK*Ds)AfB<6r3XWMgVnF4hNdW;sfB^vU;z%SnI0)(h00004s{hykP}!$Xp8x`g@iqg4NJaUd z$B+Mm%>4cD_iu*Zzrl=O|9^qF|9<`Y547mdFIIVlOMCYL1Q5sui18rvfj0Ph3vJwt z)dnUeruXmP0|XEYv&@95W{1bGfWG{@sKWZ+FOVO6s`df7U=M&0& zKrFzJ{`2=AL>j0Rra@ocD)4hB}-f* zN`mv#O3D+9QW+dm@{>{(JaZG%Q-e|yQz{EjrrIztFq(O~IEGZ*N=lh=;=qSyMwWku z27eMOW_|f0uQeek^e_9g7KH|eq{JVG0^UaP2Jy4}|L^JL_3uCblfVD@mnS9t`~UCn z|MT(+KT|KOfz%~1cG~~^_2vEk{SFNQ?Cjg~|NsBWy<%2im{vp^YdeS=hk-Cyo6U`Hgs{l4FR(w#{P`G0;a3G2@YSmzI~fu(ZE1> L{an^LB{Ts58L6#6 literal 0 HcmV?d00001 diff --git a/public/img/country_flags/jm.png b/public/img/country_flags/jm.png new file mode 100755 index 0000000000000000000000000000000000000000..7be119e03d203695325568174b72522124bb2f12 GIT binary patch literal 637 zcmV-@0)qXCP){QLU}0{l%6`#}@@_VD{9F|q;xF#yj01gpgWJ+A*m zGUYZW{T>4SpXmF{^Zon(`}_X;`}_MY3j1pm`Wy}V&B*(tydCWT00ICp0M7pd0000m zGd#J&@cQ@tG$8vu6a54J`qTCN{{H)06Z&u*`VIU0o2UAMn)~_v4c^|~0*D2u;qTwS zKYspMz5CDEtAAp>e+Mf5)@1$t_wR2Fu3uNK{_0!&`}CDxK-+|V{{|`s2p}dP{`2SW zZ!oxe=XdSY-}fK=Qse!l!T0O_!(X}WAk`4?=MOLh7ytr@32Xz9{pZ*3U(#Z~DiVIl zOa8j^;n%JAKjo!$j^Xni)Y$z6WB&k?3JlvV7}WR}00ImE Xyv9Bjb9W)}00000NkvXXu0mjf@Xt#6 literal 0 HcmV?d00001 diff --git a/public/img/country_flags/jo.png b/public/img/country_flags/jo.png new file mode 100755 index 0000000000000000000000000000000000000000..11bd4972b6d5f134045d4e8ce134601ea9b5654f GIT binary patch literal 473 zcmV;~0Ve*5P)M00|Ni~>`!`VeZ#eh`f&K#;fdS|pfB*tJ>FU-06DIsWc#z@uumAs9{`&>~&q~MQB#;&cfB*tH z31sk|Jq+K!KjiuK-^&B5YLK~LCjkTy3s3{|pFco7yH$Tr@L>D>cm1y|D}MvS>F@7f ze}Db{_vg<)5c|)+zsmedM_Y~p1Q1BWd$vDo!X?isvq}Pk|KA^w>VH5L!1(y{_x~TD z9$-NK{r~sxzrPHB7ytr@v6F$JJdlAwh=Ji34E;f3{DCq4fk_4ifB*vkxQ1J~H9>i| P00000NkvXXu0mjf0T$ba literal 0 HcmV?d00001 diff --git a/public/img/country_flags/jp.png b/public/img/country_flags/jp.png new file mode 100755 index 0000000000000000000000000000000000000000..325fbad3ffd3075a4a84d8d898ad26ef7d3e0d56 GIT binary patch literal 420 zcmV;V0bBlwP)9whYk?f=!Q|Ns8||JN@lTD;`{R1ZWk|EGa3dAO8ObDh3E3Cb;oH_5X#1 z|NHy@|M?558b}5Q|Cf`4hZv9q2p|@?lb|{i68>{>{ol0lx`{mi O0000=G`P)0NEt6k^VGA)9E1hT9ocRoN>wSfaWv)?-raRm?)Slj<6Po6w} z{P+i;{`~nD6~!ec29@~tkBNo($fnHz0mS%{fq}QS{_m4#|Ns2?|K~SQUHy-* z`HU>AfB)8feAoK(-@hL}|Nr_0bQ_Dj+^xMk00K}f2RQ&hFc1JYLgN3=lsJ)M+p3uR zWC5ydAM*!ly4H1hsiEFvIU^1)HH_GYz!QMsIYt5i1c4ZMLC4DfztdU-q)WGxxg;|L zi3%uAJ2fm~N8&I2%AdL;`{4^9#;e!&D=C-&Lk8;9|NlO`c*HPy9@C${KeOWnB;`P2 zATRu9VP-jSWFD-S8kh{*zoh2aO#pT8A2W0o(w3 z|5bef!~)j#|KF3RAf-U``!@sYUq;#A42-}3UHtv;%kTfcfBpOQ3nmTE<|1vNq z0ns0zZx{dq2xP;5h!=kY&G_~A;V*`-zy9<8Vi5ZI|I4p`PkusG11$kMn1KNxfWUtE z{TpHc!>?a|&i!W8{>5bQ`~TnHf3N=fz5nHq%!z>% literal 0 HcmV?d00001 diff --git a/public/img/country_flags/kh.png b/public/img/country_flags/kh.png new file mode 100755 index 0000000000000000000000000000000000000000..30f6bb1b9b6c5bf355f67a17531fa73beafa6639 GIT binary patch literal 549 zcmV+=0^0qFP)P;@arD~1pHxO_zPr1&>t9y%wPZrAQpyS3=Ms1K-T|%K*j%o>i_=z2W0&D^Y8ax zhQELQ{rLl7|Ns5_-|C4+00M}Gf#D0s|6k8u{RAokD*W^JKSaeZAp18+HBcQ8{rdA) zTAYJ{;SE3lu^j&CtN66?*W<_k{(=kvTJiVSPc{h&pyuy)ZZrJ(`}gOM|G$0#rP$=; zY#H_d1P~L*>3>1SGXDMzbOE=49-9EaM0J&9T`emwH;=g~P!Ocy*DnU30tNVGcpOKXK;IXGH`G*aB%Pig@_cF{AXeP^XnG~{rU5QfdL?ZSim;?VE{Sy z7c(ax6CWR+s|%BWAkauYPfsQR0VXz9sPSMM00M{!7+@fO0fqklmFn#S3Nf;>{s#gU z7DjgV{|pRrOO`MKef9?wUO?vn1P~(w!@{x_lZQ{f0d@UhVEX<0FF08K{sNMJKnNIa zzrX(mdR{@6d*A9+009Ja65pra?7SkZU^!3-{)PrTC`>`Y0b%_6|LHH#J`sQb0@|>a n0T_AEh(trkF%3aX009O7j5IT?Rho+J00000NkvXXu0mjf2r}#E literal 0 HcmV?d00001 diff --git a/public/img/country_flags/ki.png b/public/img/country_flags/ki.png new file mode 100755 index 0000000000000000000000000000000000000000..2dcce4b33ffe1f40d490cb1a2e03efe22ea56155 GIT binary patch literal 656 zcmV;B0&o3^P)8t@U|NsC0_uv12fBrK4XN%za&+zB_ z{P)aXfBpaa=ii?{|9}7d|NHmt1seeZhy|?e|DWei8UFwK|L5=jKYtkh{P{kK2}m(L z`||tm?|;94|Ns5#-_IY+QnEm+00M{!r2OxHhJR4iK=k+TZ>A77)*#lue}Db^^$Tb= zko*TE|NI8J3LpSOb8Z9x2m%4nC$a{*Gqe90@enzQH`ta%tA$7d{Sv5iP&~x@8dMF~ z(;dfh&fyCHF#yj00oRR-9sTeJMNR(n^YxvS3U`G57YhIQc>VqR{rPSM@(uBDU=5F+ z{|5&5hj_*A{sI6o0M7pbe|x2IYDAmA_)R$v7XIy^!u#*~18PtP{r&xWnDPVw`hiji zey{rM^8J)$5m8%@0*JA9=57`qRaXnayHCFDJ@r1pR}vUfQ&&Fu_xE3Vu+-%{U$!58 zQy3!)avcufO?mzv0@a%gl z1HS+N7!IC&H?5`#AOHX{0M7pb00(zPR4BWp;5AJa{{8^XwcY>#`eSh{`1tyzm&^bF q{8L{tuE6I1;oe7AG|b!z0t^7P6ga05`yJ%~00001r;P)}L!W`0l>rF;|7So3KrTcC!ho;=0*Gbf0S0@;YBwbYEs=i=3_$ev|Np-X41fOr z{{tp}0~v7g%iT=?0mQ<{9!@# z-(SD~{`vL)_wRqdfBpaan?+XX#@15+0mRIp%kY{1qrJR?xs-YLhQhSVzk_f6;`sgh z57Tc3#$ODKzZsZ*F#y?2e^`Y0-&}tV5I|5HD)yDjz7nmS1pU~~aVF#A6wD&YtO00ImETIDprOD_2B P00000NkvXXu0mjfKOhx^ literal 0 HcmV?d00001 diff --git a/public/img/country_flags/kn.png b/public/img/country_flags/kn.png new file mode 100755 index 0000000000000000000000000000000000000000..febd5b486f3f90056637b23caa26d838fbadd7d0 GIT binary patch literal 604 zcmV-i0;BzjP)h(K@ANy8uaQvQ^_nWcs z*Z-GL)eL_?t_B7*Kmf7KbznHW;LqhtK<$TZ9ej=*TGLweOZn#S|EVB#AOzI#2dDw) z)4xDJ00a;t5ND<*{rU5ogY7p9)8EGU->gS|Gwk>cG!LX2Y%nmu8NlfEl`8-N#0ZOI q248)K1w0H4M?n#d6+r+%fB^s&Q!OA|2rzyC0000Cs@aq@DuRlQczuzDZ5@KTj2q4CTH~*`MftCON|DS>3-+w6c z9|(gO{~7-O`v>9vKX&5_Kmaj*WME|P@B8=S6~kYUnG7sU|G#_z>G}QV|KC3#^7rq5 ze}4b}^_xjb^7)+E00G1Tlwy4KiiwGVIVgltUY_yi&tI!o|Jl8p;n#15-@icU*KbCk z6Mz3^`1ON{fdL?ZnEnBs@%JyYzyH5mxBfr4|7*t%=AfYeY;6C2{Q{!DKY#uG1wwy- z+}}Xm3;+QH(!lWNFN3HElfM4XRjZk-tp4-xFo=l!{|(XrbkBdVP9XXJ&!0aG3;+Sd z1adCWtuJ2w-n@w=AmGpT?F`?)|9k%Y-_M^QML&N+RfBB+Dh3E3pdT0*fp#rf!j_o$ zUs8faP3>oG?f*A#7{O)(oeuE;(0C>wVqgFWAQqr4|9E)*J$m%5y#o|bz~KAxm4S=v zKP&4$usU!k0b}RSKS4oYgaHH)%c-;9wWL`3={r0|OreK!5=N4TMk7RCwBA z{P_JV0}}Z6?;n_iu%H+Q{s1vR05Jij`8c?M=-GouSI=K${m;n9!7aeW#m~(x$j!^i z%zXLG*~fS9it_V|2?zl-00a;V#NgMjUvJ&I^~3uQB4T37ii-d5-u?gQ&wmw_XD?o^ zUAsm=P*7M%NJdr`Xazt3fo%By|Nn;%AAbM-ZD?YwEG7BBxA%WR!T-FR|8><0Vxn$d zUT@yKdH&)BP#Mq$fB<3y`hl076R7Rgt5;mSyo`Gea7>-}|M%}Nf0%y${3VbO@hKwm zAWK?W z+TGp#<;$01Vv<1pdP-ancZ!Qkd3bmLHK?nrgX5I}Ab=R3zkQ1wk#LIP517FKVgLC9 eRt>}e0R{ktF&Q^6#MUGL0000@P)xg`upqGzh6Lx zERQVE2><~E((v!!|G%IA{@M07EI*V_ln=0RjjdN{oM|h)7EO z{{7?6U#34`ML_iD4-=RKMg|km5|FijfgS(|AV#2u+YAh13=HqUkqe1m1{eb(!T=Kl c0)PMm0G()MDW>>^I{*Lx07*qoM6N<$g4p`a`Tzg` literal 0 HcmV?d00001 diff --git a/public/img/country_flags/ky.png b/public/img/country_flags/ky.png new file mode 100755 index 0000000000000000000000000000000000000000..15c5f8e4775b2b68e0360c1f4ff1f37e61611276 GIT binary patch literal 643 zcmV-}0(||6P)0{QUt82Kj15^L?rd`uqE`oNxjFF#yj0 z1e^fI2i-9Q(8&Vs@%;Dq2on+Z{QuwM00sm0@b~}!1OzrH#hk+X+Tt}E6bb+U_yPbi z0M7pcvI;2tA|wYPA^Z*x0300&EGqR875n@A_WS?(`uvdQq*oFTSt1&p=b-!h{Qv;{ z0st`p&i?}PGCBeX39IA)-~tZg`v#`;6$Tz2`uqI%`ThF(|NnA-^wR?L!uSAshx_~g z0000205Jg0{{tif814-hz}E2g`1#%M`@GEN)${-9=jr|Z1Nim(?*qH%B39iMC&AR2 z>ggNf+PVUWW##cF5^jI~{P}h24a1+`jKBZ?zJB$|uU|aB82|la`WF`1`0wBUe?VZ- zRnf8uXahh1u`n?Z4sYX?@pO{;x_8g;0tl$# z2T+})81L`j|Nel$KOpz_zrTNegN!(Tli}h6pb7teeq#K~3=Aa(fB<4-V9 d!wLWb3;+YKCC*ol*cJc)002ovPDHLkV1loTFLeL_ literal 0 HcmV?d00001 diff --git a/public/img/country_flags/kz.png b/public/img/country_flags/kz.png new file mode 100755 index 0000000000000000000000000000000000000000..45a8c887424cff6eb0471f5a1535139b965e241e GIT binary patch literal 616 zcmV-u0+;=XP)g01!ZoKn?Fz`<^k#?O_Q1 z`=8<8e}=z6_5c2{{QvRuAM+0emcRf01JR%V|9}7gKjGLHfB<4)U|@L0AiL;yFw=jq zasNSbGXHKG{NMlfpWxpAK41Q^gEaj6FV4=$@arEy05LIu!2gi{Gync$`1$|;-~a#q z|NCw7f0xa__4fbPn*ZDO{onuJ|3JnA^#F|o2q31v4F4ql-&_7K9cVD)|KCjiesKSP ztM&iLe}uy71TZ z|DRv){|H?Bqoe!x=x?wkfBu3(0w91`fMN3Vzue;gQUCt^|MZVr;{RRU{|DCmOS=6} z@%2BU5C8a_|1a6~&;1YB8$b;J0mKBvKpP_e^#OhS_CN2(f86)~DtrE)Yx94t!v9OT z|MTDe69TJd_zQH>zdry01PrBrQvYt3|7!q-C@@&>{!_XBPnqZ6E6@K61pYml`M2uu ze+NdW22lI}1Q5_kK)3wPV4bt}e=;a?{xkdrMUmL?|7pP3c>kYY0b~&4U$AjN34j1% zWLW#P{SRLy(>r)v0s|Y${(}_LK*N79FfcIy1Q-CnX{(%t#68R664G@6GIJN-*24NrwB3rW+v!ydOp^Ef6{n_)(btIFVjHa=pdp6)} zz^!@$h^2tpKUmc4)64h&|Ni?2LVth#{QKwk-@kwU{{Q=j;qSk{fBpc`pWlD@C4}l3 zHUR_xF#yj01d}3g9TFqw{rUX<|NZ>{`TG464+L38761SLpR(uO=J){s0Q&s>`~3bJ z6bIb^kphT`;m^O{e;I!LWBmP>@yDNkj7*H>@iN-VTtHNrD96Ue^ySz8pMMxZ=pRTg zKmaiT)&Bj@@b3@E4ZnZ>72sjw}n?8s2>T{qV)lKMV{%|NQ?241(YPff@h;hz00Cp!a_N2HF7% zr}y9gR8D)C;wJ_Sx5=xYz5MWpi4mv-=yjk&KmiL7K#aZ&_9^w5@1DH=3l15e)xUl~ z-0=7B?|)4HL4sh3f5LL21siGr0*GY=!$H*K#ZR;BJ~dv8zS`wDeeIR3;>1y|KC3hK=RLTAp0Mf zWcc&@%EC-|_DIyI;S5 zZoa(f#*3@};Q;9GfBygi2&4h78pxV*WHQhn|Nk-k{`>pa-{0-~I{v`b{|EZ(4?qC1 zz%@X;G0|`0)6dV-R;K{9rJ5v|%9;KB_nVP{8R7~c2@pVx*BKb3t8)H6dH@UxP=NgY z{qNULVEFv`^^@@rIK+N~gX`}f7I~!;+fM-m5DPFa{(t+%C?E(7W+q^;{`t)a3di3} zzd^yz010JK%>4cT^&8LzfB<5=h#HaqkRlk)Wq^@D01#jR5K~0vg#SK#00000NkvXX Hu0mjf%Ubyh literal 0 HcmV?d00001 diff --git a/public/img/country_flags/lc.png b/public/img/country_flags/lc.png new file mode 100755 index 0000000000000000000000000000000000000000..a47d065541b0d998da832e1981b479097a9b36aa GIT binary patch literal 520 zcmV+j0{8uiP)#-NSZgwz{Qh@GSmETWyIzrrS!pkkqDDw`CEYY|A1Dxfxke?UP9EWUlaZ{K@! zXYTRdfWyFioqIm+xW_M=V(hYvbO?~5ZHkEt37xh?rY1@PqM%`P+i2w@_wXJJO#nzA zheNt~+w1B3H|fqGMx=x!lms6SMy;uqQ4R``NH&49%B_cditsDHI6DHXLNubec}E0~ zb8dy|txA`rTkZMN{rCM3FV`MqlfuwZrz$X1)+?ntF|UvTkD3M?FxSaem74ag}Vzy5sA+`s~GjvDl6>(E?=UGu{=w?r5#MJIwhn?GrT#s zeRSo}v&#TUmcjL&mxEF>2%EIxN+SI=O=izlM$T5JH;yv-C%^zTfK|9CLa`qJ0000< KMNUMnLSTZ|5$YcR literal 0 HcmV?d00001 diff --git a/public/img/country_flags/li.png b/public/img/country_flags/li.png new file mode 100755 index 0000000000000000000000000000000000000000..6469909c013eb9b752ca001694620a229f5792c7 GIT binary patch literal 537 zcmV+!0_OdRP)sI{|q~>>TZ2$qp!tjsj_dmuxH+6SkQv+K1^Dpafp!@&+{r#8W&u@@le*a|z zk$)I|{ss9HAb?mjKkZQc#wYOgBhb-*|NQ;?=l9<~e?U(E{r5MB3DgSYf*4|4g1Z=I z0R#}sfv@v<-|;cLeDMG8um8XQ{Qv#?|LOa4L zz5|hefaLFgf557NB#8Tm`R|`M3=9AP!~zPVKOloaN+E`UP5lQo8*B+s^WVQre?jpA z5I`*dz#16EB$cmd~05Jg0{{#R40Cvt10RRAw z`u+d`{`)-+^5ON`yb$y0|Ni^@{rvm=`~3g>{0I^G8ZGb(2;2e)q~Yi9|KETAVEFqN zi2na)xwe&yo%hS1uZ%yx{Q;WF%<}W+mp@!0-yWP|m)B$h2q2(_|NnsK|6hiGKudoE zUGn9{-`6*oEmQ72y~rUb^YQPmzn@?Idwh=T>wA{p3V#3shzY3p@87?_|NZ&@_cy~| zpmRZP`1kJ@JI^@8AFa{s9OeCZO+ts{j1?4b<=-Nd5zA z`2GL?FD{9%H@9*#{QvEce@o8hC9n7wpg7QIfB<3vS^!iHvfyK-U8V5ZH#le}4S|yXQB^Nt}W|`Q?5-JjKDn&hY;a$Df~n9-WqyG5z}b zHWMS`AAkU20!9tv|KD6JO#c`e|FbePFfi-*z4`Hxjg_16|9{rM|5*PsvHt$a@%k1I zzvO#v;hz8j#Q5o)BT__yf)$AVfxHBa6HpujodJ?%_y>%AV59*A7yy`5b5c`Z!JhyC N002ovPDHLkV1l?nIh6na literal 0 HcmV?d00001 diff --git a/public/img/country_flags/lr.png b/public/img/country_flags/lr.png new file mode 100755 index 0000000000000000000000000000000000000000..89a5bc7e70711575c1ee3b83cc2be7f0e1fb29c5 GIT binary patch literal 466 zcmV;@0WJQCP)2Y|A4{o z-@kwT`t|eY&mTX2eEM}Kp+i!_T03wQQEla((gZiC0cs^;{c3|jAkj>009Kl@aN){ zC);;k0UG-E&);Xo*&wq)roznr3pD=Ezu&)DC1p;}S_BY4jKAN$W)>8Nm;toyKW@Ot z#Ps&Y4S)b*xg;zq)7SR<*)x!NAa?^@4{|ZkY%l|8FPQu15397y$%U%{0*LYZ>zAxx z8}J(slm+_X#f@tK0mO1iR9wET{^!#tU}GSb{Q3uSG}s1+e?a74b~(9Y%Qpf95aZY9 zPuWDo(ENa58%O|%pI^NU5I`*FB&GkLM&}>Ys6}P~0YHEO0B+J}4VS0Fk^lez07*qo IM6N<$g3a05u>b%7 literal 0 HcmV?d00001 diff --git a/public/img/country_flags/ls.png b/public/img/country_flags/ls.png new file mode 100755 index 0000000000000000000000000000000000000000..33fdef101f74e38e2422bb85dc8a31bbf1da326b GIT binary patch literal 628 zcmV-)0*n2LP)NT^udI6UX~&u_xA`izWBNXkIq%g^6Y%Pu{8|3yZC zlYxl^Ab?nacHcU-^!@XD|Nj5|`r(zVwkHE4BV6^xC+~{3+xxCp=yQ1PA~B|JUd6PoU1p;PCnS`uh0!`Wt}y4#)fi z@&Nb%0tn=Xmv@c{N@*}KFh0M&kIP1>YRmQG4?lbdn);9N*SEXB6{h}Te)Ie9Zy;d! z#Q+chF#yj01pfa18Ye&*C_n-K{_XVnU82h1@caA!0{i;;`V*Y{7}WX#^!xhz{Qms? z{`>&^00Ic4fsvW@|G$6Ruf6C#{P_EiKfi$f`+Dz}{FL7;Z+`vz4fMjFe?Sd?fe5Gp zAb>y`UVr&E@!c&!7K){sO`Ozkh)melY+9 z5KGUl3lpbYV0iHf6xF}JF{n*t;A3C_dhHtn&^!?O1t$N2Nj?UE00RIWZBXJNY9>Gc O0000 zKY#!H`S<7dzuzGA_xCRl`Rmu;Um!M^l;`6=xPSp5fLIuQF#P%V7sv;y25A5(1xW+7 z{Q_w~Xakza@Pz>&fLMUe`uqRSpZ|Y=>VQIE8-4+ehiC(l5cdDyKm=3_5I`(zTN!l! z|Nj0Es0O49Xx6_!5M^MM5b`fj@gGk4KbtNx00a<=83P0Vn?HYFf{Xx4|Nr&tKga?w z11|FC_y0eCSvdcCFfafF5XlYnMRN&=-B{`>{W0U03nA0WvHB!TYz`r< y{$OAL2q4DW;E4VQBmbdt8IZ(*2pDGo0R{jiB6maa(%qQ=0000! literal 0 HcmV?d00001 diff --git a/public/img/country_flags/lu.png b/public/img/country_flags/lu.png new file mode 100755 index 0000000000000000000000000000000000000000..4cabba98ae70837922beadc41453b5f848f03854 GIT binary patch literal 481 zcmV<70UrK|P)?-#?r-wgj45C|ZESQtLMVW?~Zs{a4) zALIXj41fOq2a1uNeOQW%&E= z|DQh$fB%40fEE4z10q3;-;ClCKpOx8h=su~@}N>;ErcnO|V^hXG82+5i4Q+5aFU0|N&GK!5=N X;lz1sunOP500000NkvXXu0mjf*7env literal 0 HcmV?d00001 diff --git a/public/img/country_flags/lv.png b/public/img/country_flags/lv.png new file mode 100755 index 0000000000000000000000000000000000000000..49b69981085ff54568907cd51a56a1e5d8b01ada GIT binary patch literal 465 zcmV;?0WSWDP)TuF);jrk;v#5jAUY900)zv`N|Ns31p}&9s{rUUv@1OsF{`>=? z-@pHYNg(_0@82^wZ2|}&78a1v|Gz$Y3Q`J0Kshi8lm?N%fQ(=Ne*FS+xn*U6mIDM3 z3(!4({{8=rtQsf{G!?8Agn$gN2Dab7KQS->1Q6rPlP7uPKoY|E^ZWmwzrbJwx)~sVSb)*-|Mwp*NlCCVfB*i0>4ZfB zFhYR-gakS;`Tzomv6O+~6D%TsAw}vh)M$o8KMw-~K!5=Nd?C`~#DJkl4QvBtQTT<=hAW5C&pkLRXaMS_oX@ePCublh%7`S0>4^BHJ{X z&jSWU1bzAnAdrUt4F8|~c=q?-U!ZD;3Wy>I`UNEa{sJFg8g4-`_wR00IcC z0c`wlknw+DNZ2$-$7NDlT|NlUo0ap!i1F~k2r~my1Y5)izkcK~h z{{rRU9);Krw*=%9uq;py&^Z7B1lI8X4~o-~jQ<6)85pKOHYf~%iU9%$qyZ=)&LIKv z04P8aRsoF!DgoLL3cdee4gVMb0*H~J5hWs_B!uw~i3^Ex1_pot0|0+0kn{N-xWWJc N002ovPDHLkV1lkWn<4-J literal 0 HcmV?d00001 diff --git a/public/img/country_flags/ma.png b/public/img/country_flags/ma.png new file mode 100755 index 0000000000000000000000000000000000000000..f386770280b92a96a02b13032e056c3adfebfa18 GIT binary patch literal 432 zcmV;h0Z;ykP)@|4`Xj5kLT%`al?B=W5I`(ov;U*0{`Ko0*gOW1x?dnY zU=0kve*-lD1P}`lGXhluRs8wG@Eb_}{{H{>S-s!?{`@vR^^5K2FR(pO4M5WY0*DFZ zqCZG(`2G8?)UU4`zrJw%x*-cw4MBhY08Ix7Ah47E{sH^x7s!U+ztwL3`tbkv-#@=J zum1jzWCJ7ENdN%^b`n?!@|4`Xj5kLT%`al?B=W5I`(ov;U*021*0XgD3^5{teN< z@cTDV13&<=05Ky_HBiN$KMcQtBo?#b8i1w)1P}`YD=UMn?0*)P|9^oV_=9jUlG7n1 zgOt?2g9iZui1GF7*Fr)<|ABx33~>V{CZ_AxuLA@S%fEmBAbhajaRP`eP%%INfiyrk z1T_G`pFe*90tjjYTn_{=GBPrt03a7C3lKmc4KH52_yY$2zyM+rgbiXafFO_o^aD@| aAix0StzUbk+v2SN0000M*00(~<{@!wBU}0eR!0_)M!#^$%c|o1wA4mpD0t66HlA)zv9Z3HD2a;wKuKxf3 zKLhV?#{Ykr|9@us&njQ==l`GI|Ns8^_xsQFIm-b82&94GKf}}4zyJRI4@Cd}JZ511 zyk#BtzrVk|eZKww|NG~I-~WGu5Q~)bF9rsH0Ad2#1T-6>`p@704FCVWdiqyE_S>%? zzn?t&`v3p0|G$6zgQ7n`R{;bN6VP4%{xkdmsRoGvjXbc2Q*ib#i+$e?>}LD_@7KRy zzyJOE_4n5=pu>QE00u!g^X8F}1){Re7)$pq2>GzRP>hz5WF0{Q`{ z7%1`&WXbP;Kox)fFb41M1d2p!76A1_RfAN3oCFX+Kn)0M$tdvjY9_8yH^9QVjn@nfZP*{rmI(|3?O9b~d@cVAX$tK?&3V z5I~F!3@gDg2t{v?r~d;6^$&1NgV7;ZP#i-L5C8-i0C2iwRaxXp%>V!Z07*qoM6N<$ Eg62#MXaE2J literal 0 HcmV?d00001 diff --git a/public/img/country_flags/me.png b/public/img/country_flags/me.png new file mode 100755 index 0000000000000000000000000000000000000000..ac7253558ab939481a85cc06dcc4d73503afb9f0 GIT binary patch literal 448 zcmV;x0YCnUP)l$FJ^m&#tWvBA4C(n)b76qu^ z3TjKJi*u=MzAs;^(tHqT@6cZ|8AHxz+0T%zR}I9mkc`8faCz48MN^H2?$<3((yN)j;^`52o304M1f80R+|X9}Iwu*X+N)@cjDs2c+lU z?_dAGBv9wyKfl@e{~Tjr001}1@4rADVDj%Tpgtwu-=}^u z00a=o2DtG+^6#HtKYxRyQB?o^{pa7Gzs!FaUNHa!5W-0i5^f%h1nT_t=O0kRUm#-m z!vGLKP#fTS#5u+P{rv~@0nkXGhChFS;q~V)5d8o97pUPE13&;VK7Rc89~k`k^9M+( vx2Q8b0Y@nl1JFDW`UNKcfk_?)fB*vkB(P&2-J7g<00000NkvXXu0mjfGX%sy literal 0 HcmV?d00001 diff --git a/public/img/country_flags/mh.png b/public/img/country_flags/mh.png new file mode 100755 index 0000000000000000000000000000000000000000..fb523a8c39d40401b9abcfb144a73cbb2d76b286 GIT binary patch literal 628 zcmV-)0*n2LP)qpJCu1p{#SASZ=3QjbPhlO05Jg0{{#T~{M_a8tGL(@4F<^6===Qr`uzX+`vVjI z>RSuFND|!tCprH%UjG&-`Tqfgkh}r_F#yj01OWa1_W1j+!`}P+|NQ*@_xS<*{r*D) z#ao8o+xY#_%t@QS?0~yfBW+Wj{_zyJQv%*f2f%)|)v=^uaq0&4j6{l|Ybrbh`TZ`2ITPyN!~wBya;Q+wXO z|N8qsP{n^H7N*~zApZOB??0dhfB*t%@KJoPW~Y4ZyXB%oU++J<@%!g5F#%3te)iws z==lAQ0jwGr+CT&T00a=DyL0x5XB;1||6pKv0FF^K^bd&vjBEyg00RK!=O6aq+V@KU O0000p}y_ZxVsQQo9l8qD!tQ%&&F2zEbEdU-v3mY$p-gb*;wwp?LFG<9EeNpZLj7Q z>zeacpNZ>XJG@0bcmXcALo;Ad(L@#C92p0~G#aM!FfF0T7^YIJVVFaIl|0gRpSyF_ z@0dgJ{oT}qqUk#4;-a-U9Fej5EJ`tIE9)E!qDfL@GEq>vEDI}q@P8EmenIHxu!*Cc zLK?@%1j7u0A`mPlm`kyT;5daWs;EH!+LV0IWO0Zyj4+sHxRqia#e9ki#cu?^6YQn9 zOfZ8&4uw%r9nR_}FG?tJ1sBpnAsdGMh7pA$Mlm3ABvgz9aj&GrxaT8{1^YB+UzPEM zQQ5*0;(QnblJRNh>E*%16wccXl466KPu9Lk=M%$}%9~Z3xs9na6KbZ+^U;AoQ+JVg=BO3kgY$Vuu?iP6r(sQV=EH;Iwf|hcN2Nd zPl_EfW;$kS3zh=H4ojC&X!7Bd!`~OdX{VxLp z5dC3f{KL%rhe`hT|Cm4jfB*jf2Sk3^vlaWpqpU2@27mx!VE|eEAE^2d5dHlPWd8sE>;L~hU=6?i|N8~f1J=Os z`!`SnKmdWv|I6_A575egAcQa*n+Bj_fB<3vdK75(@4vtQ{Do`yh0yRHtQw*LD9^wE z5I`Uef5B$|`u7W{2T22%M6!Vq>?D8y0%`dD|M#!IAl1MB{`!Yz!@u8fUYwuxex*z#9GlH2{4I3~mO10Ac~g+V6jVB)KI)-uUyEi4o|t-;5vX%A}^sE-TCS?;peW@7xO)zGwLT z>;Lb2Aa%cf{|B;v0lB{!Wo3aj00dxJ4sHN|VGstA{YR$4C2D_2_!cBcz(8l;%*ju; z_5-pDt^fjRVEX=(;rPk#zkV}({q}dw+K<0~Gco-B2UY|%8?FIpIzRw{{P+Lo&pY>j zzJB!i)2Bb*-v4F%#qjU=2Y>(qX<+*Ohe1*jY|LMVKOmiqU?UkIBoO`m3qe4|00G4K6*VFmu*EK13J3rK Z3;;9iRuWt9^;rM_002ovPDHLkV1h$@)rkNA literal 0 HcmV?d00001 diff --git a/public/img/country_flags/mn.png b/public/img/country_flags/mn.png new file mode 100755 index 0000000000000000000000000000000000000000..9396355db45a8ee040c790782209868acaad4b85 GIT binary patch literal 492 zcmV@{}>pU8D26lh`@M2^zAEy;6E4#hyVhJ1te8n&0xjw|I|OmAOC9q@qGTx`1kMs zKYtki{9$JJ&B6L7=Kt^CKt7QCvS|}Q0D(0y{Qoccl;O=^hIfA-|M~k7Bn3tPfBpOS z`}Y4|zknP@Sy`YB00G1VQttboLEtY##9yH3?>`J++5f+PL6rRiQNLgu1_potVgX{t z{eKx$|NOuHhk@zOpC3@OA=>```VZI207MK7009KDfg$woe}>=xgMa@Q`TZMV99$1f z=+7UZ=>P!)^22||KmS$!{Ac;a@cR$YT8L7JY6c`57{N{g2q3VN{s9Bw7X!l|paf9r z=TC&I7$7zP9Rm!dKY#u(FaQJ)NCWfVKmVWoW?%y` zm}dTA_#*kA1?Z>0QqoLZAnySq0pv->KTM2<4My5DUW>hHBSpkmCRUfavf4zkmMxWn=;={POe9 zpZ~vq|NHajABg<@_xGlmn*aic1*nOE;s2AbPk~B-;NSn>+$?|p{{I~>&N%tu*Drs5 zfBO0B*Z*HY)v`RYKsx~fhzW>+Cjb5S_s>6&>Le+qufKnBv&bF%YWVBlABO)w3uGC7 z{rLq%Ks`WL0R#{e(EPs)fB*akI^_5N|9=)g|N8#t9~WuY61}otf4Tqs`!(tD7m$X( zzkdG%X#fZyCZKPCW&?Et`7z=QS^~DI8Yx-=nhgK{7whGvYgWDg!B8&G1k~{7FHk81 zKmY(S0M7pd06qXVA~x>%?)v`v$?y7WENVL!I|c#<=Jn<;5-<%03=swpg4MkH|N9RH z59P$=0*D2u3CIB%01Ag+CC5rQSvY_G`T70NcNRvLcR${}{{9+hIZ*cRKadRo0R++j zv0Gk5I)>DQ+oULEUYSww-@{VeP#&z`*?j4i%tcfB<3v#u_k;CAlSmszIWE zes6rZ@!!9HKqG(r{Q)GE1e8GG4GeUkhF=T-0mR5q%E0gm7LmVTk@^EErhg$tKMw;y afB^u%K|axUkwLit0000IqP)p`2X)eQ1zd`|Kz0p8A$%R z`bTZnZKVTj3KfeK`2Ky*5B&e@&)?sFfZ#7s13&;VG5q=W=RZg_5SU54z5Mv&imk%` z|FW?#NF2HHZpKX|PKHOnfBpIO`|qDW|9~0*0*DFdzkh#$Hv9ux`j_+Xe->2}ZC)XM zHV0!dKPS0Ur}$Y}|Nr_6wBa96o`K;HKmdVl_zUt7(16dMgtTSeTz>rQ?Z?ko?mjsC zklD%d+V79-zd^=BZ2$-$7GT%`Z2%eY=kMmT9MVkp=Y%mFes6g9+4CZ|=Y~?>`*tz_ z9Sja7kOqJN0yzojravHW{k?SeQ|&okK5=fKkARLe6n%c?uEf?;0ubZ>f;9XA2p|@Q zU%!CHiwJW41sn7GB}e}J|9v$?qW1mmH(Bw2pdk4RbO%twKY##YWMEE2jz}>2 jgG53Y5Cqh~01#jRFwa04;J&RL00000NkvXXu0mjf4K^ZQ literal 0 HcmV?d00001 diff --git a/public/img/country_flags/mq.png b/public/img/country_flags/mq.png new file mode 100755 index 0000000000000000000000000000000000000000..010143b3867f21e7791b8254e806b325c13b2895 GIT binary patch literal 655 zcmV;A0&x9_P)$g8u&g1qF5h0Q6aJN-{1Mbz`Ie0O|k$ z{sM@ZfzzR@(T$Ir&B098+eukMUeMl2+1^x^fq~&CAE%jZ{^O4ipMU-bgNM&P{`mQajg9%ivrm#DT>t+4XJGgP z5I`&pzyAFG^Do#{Ra%^vg_(hwneqG2-w+MozW@IH=Wj)-86P+6=Wo9l8G*Kb0|+3- z*{jc+=}JF+`H7W<>HmKQUT#*P1{)(8poaY?9|3*x^4(W97N$Rc|4EDS&R=&5Ab=SE z{{73y$Ox4F`Rgyx2o@H`|EK_{=f}^#%*+g|tSlctd;ka_mU(L~nd(ak@UpY9GCq0n z(2rN05Jg0{{a96b$W_S@ACL(Rxa}M z`G=gy|Nj54lTiQv|3YG^`}+MH7z}h_G^4TA+~V=RqI&{}iQ&&bpkF?J{dw&CQ*JI+ z5dlsHn4|vw{rBhJzZb8+p1k+fF;B_XE! z42<9q0s8Xa-+!garV_&3Uw`~!Vgv;%KmY+HvKbgYfiyF)F);jMVE6!x-Ip&{0DU4U p=>-f&1_my$6wo1$fFwYG0RX)13*@;vt7rfK002ovPDHLkV1lt{Hh%yB literal 0 HcmV?d00001 diff --git a/public/img/country_flags/mr.png b/public/img/country_flags/mr.png new file mode 100755 index 0000000000000000000000000000000000000000..319546b100864f32c26f29b54b87fe1aee73af21 GIT binary patch literal 569 zcmV-90>=G`P)rBb0vs`uywH-=91j zzd+y*NDt6q00G1Vbl+cwzkmM!`SbO+m+YTk|9<^u`R%^_m*bXSzgT|#{QE0P`4>nh zQ2ZZA13&;V0loO=-(MgHX#3gMzn3`u{`KzHucyC$Kl}CT<*&8wzt6n|ss8o%*Pp+C zfTjZk5DUl#p#T2<`NjV0^vyqym4EMy|26&E??3;3WoiC?{O#A4)4zWSfZPMI0U&@t z8bCe+8UPFj-d`E(e|zZv%GLeN@c(z$rC;+8f>rHtN0D&|BIS>OtI{AOC zx$tZGDUcCBkzc|7XAeyp!)#=hy@sHz%Z8Nmi!G71?uGb{rk^vkcEsO8W!D1Ad0T=%Vj%gtVfB*vk>3V2g53(}_00000NkvXX Hu0mjfpCtxQ literal 0 HcmV?d00001 diff --git a/public/img/country_flags/ms.png b/public/img/country_flags/ms.png new file mode 100755 index 0000000000000000000000000000000000000000..d4cbb433d8f9fe49f06585dc46ee15593e3e621c GIT binary patch literal 614 zcmV-s0-61ZP)w!6%f1G{My^>{rmp(`~Up={s8&+{Qm#<{R63-jRF8M0M7pe zp74|h+7SfEy9M#{{`U3;5)b(M0y+2j#`QMv`vWF8IQ;$o{QUp?{QmLR&;S7V0*Gk~ zkHv3!H5OU9Kb*Y(q-ELEH9vC;U*%N({+r?VuV26a{F4-VqagC^H&EN}zd$De1Q633 zj~FHafsLPJA96}RV-#A)X~v?X^MOAye@1K9u&05Jg0{{zeC zH7h(V@9+2F_Xpti2mAc}=j!&)<^S{h0sZ;_Q7ob~2($C-{v8Cq(%>lF*}4LV2^e-j zV}AbzqQAd@=ogUu2Vs2v^{02G<;{1(|2aQ(EV23Z2N=G800M{wXv^O}|A7VrH9+)$ z7=NG)mcRcxm)QV402KN07wDuv009Kl@CPU%A;t|Pff9c}I{*Fo2PXf3L>PYm|M&kd z6T{yxzy1Ri0|XEw14AS%MzNxQNDQDd27mwq0OfZ^Ej7^!+W-In07*qoM6N<$g6Gg9 AMF0Q* literal 0 HcmV?d00001 diff --git a/public/img/country_flags/mt.png b/public/img/country_flags/mt.png new file mode 100755 index 0000000000000000000000000000000000000000..00af94871de66cd0fbf0ca8e46dc436d66e2f713 GIT binary patch literal 420 zcmV;V0bBlwP)Io>l+&W2PtRx|M&0z2M_*#`0)SN z@BhDl{r~j~sG3n$7H9)N0I@K!uxMy#e*W?mp&AJO{R1fisrv=e1J=Os`!`SnKmf7) z1AD;K%nYO&458rnZ>;h=xCZfJy-Z2&4h30>ylo1|%C8 z!A=4QAQtS#|N9SD2R9xVP=6R000Ic%Boqz*|DhQF2dLpM!{5I^H2?v`f~*=Iag5B& z3^Fndyu3hzLFn&a2m+c75I`Us-n@D94+enNf~Z%o__l08i(?1?2rvLmwOi|Xk;8TX O0000wT1SlAdCl&Y)$gVg>5qW^#Y{{Qp$|KC6V{`~n5M8AIn z$$wxH$UeVm6F>m5>|$VWV&MJy|4IE<4N06-W9 zfN4+=|Nq*iiwn$Q2C&jbT&$=TD3oCSlHOGFpRx(;BY;>KBo9alsWSh04>T2o{{H<9 zq`@}){__Va_~#GM1}QP2gACgN0*LYNU!Z^U|NnpRpWzq7|3Cl#|Nj5~7gmyqN$LIj zQvdWzhA$B4g=Z%5I`U&fgJtk z_g|1x{{H;?U6oxK=&at?J**6@V8K5Oe}Dh~1#%?NbbtT?*#Hy-y7l+}Kff9N{$pgg z_WK6Ie}+7v0!uEdpa1{-Wc>4s0pu$NunhnK1op#kuysJ&8U8Z;`FH#0jm^LQ>Tv2E z__*)izhD3U{sq|pR0FmFAb=oF`osMFS8?ijP~d>v`s?4X6Tl#928Ghke_*>=zWoA* zI8ZS_0I~c5+3<%^Qj!Z4rNibE_6+kRtZU3J46>yw zfWS`r^B2emYGC;Nhw;yEHn21ZK^Q<1%m5k+3<-b$0%`dF7h*inus>j>Kov-;VIf zD*pWek$*v<0SxiK|NjEzfQkVEh>@X`f#DO--wX`DVCWAL;}4AS4@`0~00bBSj-y@M TF2~k{00000NkvXXu0mjf^ET$> literal 0 HcmV?d00001 diff --git a/public/img/country_flags/mx.png b/public/img/country_flags/mx.png new file mode 100755 index 0000000000000000000000000000000000000000..5bc58ab3e3552b74d990d28a0f500e9eb6209dfe GIT binary patch literal 574 zcmV-E0>S->P)LFc1LT4cKmY-iGBrkbGB7ay`}g

Nzrc?B z!}RAj&|v@p#KiFL&)@$*g@6D2{reke@BjagZ=QH_|HA5tt#{7v1Ul>guV4Ru|Ni&u z_rKqNfHnXG5DUYvUm)ZE{sF26>H%tC6A=5t#K$G4Eg-G_A87rbKVS#^0WyF#00a;d z1Ca3#WHt~1jRI=eclPL04k1AU{avRH{sTG*Y{UOQAO!R;KmY+XFaVtnvEdic(+vNe zOr8IIc?-17*~$Z`|L?Eg|G`fB4^+g!01!YxCouq>54Pd=FQ6a({TC7tjr5F#2GXxz z|3N{=01TJ^fBykB00a<7!|#88#Ce2)8ovGh$;ikEv=>5x7)(s8a literal 0 HcmV?d00001 diff --git a/public/img/country_flags/my.png b/public/img/country_flags/my.png new file mode 100755 index 0000000000000000000000000000000000000000..9034cbab2c02704b65fba6ecc4a7a1c1d053b6c5 GIT binary patch literal 571 zcmV-B0>u4^P)Z-xKO+5@Bcl`=5c~|G$6#Vc^TNX8-{JF#yj01a@w+002>mTdm0Dxpa1{C#)An)W@eTA0)PNwiNI_f1h_u_DTrb&+z}xv;{Wj zFGj0Cgf$G002ov JPDHLkV1gu%1+M@A literal 0 HcmV?d00001 diff --git a/public/img/country_flags/mz.png b/public/img/country_flags/mz.png new file mode 100755 index 0000000000000000000000000000000000000000..76405e063d43f2f3b5b9cae4f76d9f1c73cea25b GIT binary patch literal 584 zcmV-O0=NB%P)d!0?ZO;SY>NM!#Sr3j;s^4&s;y01$=&U~zyG1R{bN8&$-J2AZUB z{O~2s*67}BSLujJh!l{(TM_&V!SLx<0I@JIFetqFAHVYHe9OP56#xDPD){#oDEIpx z82$bI>))?me}4fPvfQ#j8vp_bq~ZU+|9p&pvv&RstuJJil>ZL~|9}umff)b({bONa zxh{1bAb?oDF)-ZV`m5;k=ljw>l6BpeezVN}{rlPPKR~N~|N8X{NCKH4>bI<%?516t z00M{!=o3jd#=5m)yzyNB8Nca#eH--U&Fe3(f#?+keR}os(@P+F`}Hl*IRF8~0t}ng zTmN>%etWlrgX7)b6MtD}{$cp_8-#v?7>pqD50GU3{f7;x0U&@Fe=;yIS7rUbaG!zu z&%cJ+j{>}Z{{3SByBg&1|Nj_3~Dr2e;Gh>zZrofD5`!lF#Z7{ z2B7#~hM#}`y~Cfd!0!j9&~4{}>qlfDw=_GI6Spo7yiaSL1(R34j0qF#yj00RRApetkm@ z3-<^I`04Bn_xS?+{Qmv@{`~y?GBy}bOB@O2-v*J4|MK+z`}_2=vH}QbDjOT??*y+^ z9PHma=D0k1DE9l$Z%$6;=mbqObq>D1hX4By|NHgp|L;GHwRPtz;{gH)sDXiz;U6Qz zZ)V2dKoY20MNK$ASA+NcQ;z)f|Igq2|MmO7w9@DL!aL%O?v>U60R+_W>(dw3rWqbw zY+vs`7YYhj_p}$}TRP+a`c40T|N0L!J1pWwjPJac>kgmX!|;RwAb@}x{`~&)@5xJE z5fO>bZXLnj?>WlS|KEEGbn$;~p|4%Vx0QJ+&gI^Ic|%b^5f~5v0R*z)@87>RPSSB9 z(mdPNGA>^D|K~4|Gn`ysXGCuKcCvq8=a*k!egj?m?>8{m00Ic8fvd1cUx@iTXM5HE z^H)FyGjo2eEq-FG+;YA1)Puvn7=8hj{`vO@7;-=x00M~R?)3!82NxK-n*V?K1yZeJ z{HiGaByR2;1J+usHUNMy48Q{FH!M8N)Pwp9Y)wTFo81A^+}-HTA|tp~ zO4-IyLWKVGUAVv#KrFzpU^vb8&*4vW%&*_Sfp-5+Oa#jR{(FIy5oqwQKVTa`BqPut z3=9AP#KLU;d%N^MnR|>Uj{MoN{TEQv&!4|iQhqnS=35v13ux)DKYxDz{qyJVA7#|2T literal 0 HcmV?d00001 diff --git a/public/img/country_flags/ne.png b/public/img/country_flags/ne.png new file mode 100755 index 0000000000000000000000000000000000000000..d85f424f38da0678471ef4b3dc697675118bc7e0 GIT binary patch literal 537 zcmV+!0_OdRP)pTI(!tjlO;rIXl@1HV&)cpmDGW-GR z`3+L^2aNvy`v2$G|KGnEMPz|W00M~d>&=s#l8OvKbwEpiHvESG2>tK>|GyyO9|$ut zvpzk42q1u17-V!9I61+pfvQ08|BZwH@16Yr=jVTUlmA*S|6zcEk@1g`6+i&7`~&$E zYA%%V^7j8zTmS$3{hyirzos*?2B2bq00L=%yMh7Y5+V8jy0-s+|NJkm_8$o_f}Hjb zAb?n|yt|^zto-ZmuYW+t0)q+&<<KJ>BgrQT zRQ>1Q-#;J~AoS`6pHRCwBA zU~p!DquG0BzkdJv^XJbWKYo1u_VxSs@1MSW+P$@#L70Jomw|zm0SEvBhy{qNbE|>m zzkmO1v}^=;1(+C_zx@3C{m*wPUMbGM98Q`}5v}3B|NZ&>_xGmUO#lG|(!jv*|H=EO zfB*gc{`33)|Nj{n|Mza|{r>BFYe?(AfB!%K_3?Y%L>Z^l>!7{V2qIffI$#e zz5f4~?wt{_jNwA=k=tvV8m;04hxH1ClNsp|jaD0d_yUND;Sa;#|9^ps{`~n3bQJ?5 z!?D}Pp1*tk?A@~oJ0>vvX8@W7)C|%C)&LMdARGSt{R`y$`S<5P0|@TEy!-p_?>~S2 z1lj|1A`hhCuT1`^U1^@yK06yzfAV)~#?EnA(07*qoM6N<$f^cITh5!Hn literal 0 HcmV?d00001 diff --git a/public/img/country_flags/ng.png b/public/img/country_flags/ng.png new file mode 100755 index 0000000000000000000000000000000000000000..3eea2e020756c41abf81f765659a864c174f89db GIT binary patch literal 482 zcmV<80UiE{P)E-@kt#fq(zr{dlL|rVbPb$^K$sU||3VAQpx%4ArjH z5b1ya|A`BUvomopF#HDspdkBi_LzW}KMcQr|NHa%@9#}BHvt3?3s4hK`pMU)fB*dj z0+88E|KR{Ac>Ck+U;lpn{`Xs!M;2%gKmf4-G0FZ zKmdWv|I6_A&;LJv{(%shYM_N64Szv401;3FKmf4-Jp?oxsQb@fum%PU8~*%)I0>i# zs2CuCKpOsn%?64Bg>X9QKhy?*00Lh)xZD#`v33$e!wuw~|G$AI0?h>~ z1_&UqlR!#;Ll9OQ{s1-n2ZahyF+c#Z0Amdp#**BUAaDHn%gD$G3dPzV3};X@Gl13bFaQJ? Y0MmSSAW;3b&Hw-a07*qoM6N<$g7#LQDb&XuwlP!fzJKM z`1`}(|Np-K;|B=>MS!;cWf13KY+LvSAb?md?)rCHn)&`aU^p;VI>1V00D#b-){yc34R8!1Sqb680000@|4`Xj5kLTv#?55wQzKoX38|NjLffBpXj6#2y{D-E;(Ab?mHL=FGT z$TP6>fK>na1GWK({(upf0nrSU5*Gz(00%w4e}91F-@gjtJbOE500a;V!?RECzy5mt@dro_q~YJM-#~SLe}OeX zNT7y4|NilCvNABd0|+1%hF>6clER!2lYo}|1|zVkK=SXOfB*mf{rC6ZKVbL)odgg- vjKFAPVE6?_e~=h|kVO7~qZmRm0R$KTYszPUy89K;00000NkvXXu0mjfB$dbi literal 0 HcmV?d00001 diff --git a/public/img/country_flags/no.png b/public/img/country_flags/no.png new file mode 100755 index 0000000000000000000000000000000000000000..160b6b5b79db15e623fa55e5774e5d160b933180 GIT binary patch literal 512 zcmV+b0{{JqP)O=a{vExMP2%`MCSoB^FIcLe_%lf;|~%E5I`(IQNh}3Ao>6Q|DFUXMn*>AqQd`w z|1kXd^B;tM|Njjl{{hM0zwd6?1Q0+hV1xeud-4=Wy?p-%sO`^#2S61Jzk!N?s)6X& zzhA%p|N6}=D+{y%Ab`Lc{sL9~1=0UN4*CD*7s%9KAf+JHKs~=eB-8KTKvw|-5R1&; zzd&a|ob(5%^Z$Q=wHy9p13+aOpFRNu5F>N&`Tk_-7w>=n{RejQzkfh&Kn{rf10?_b z{tFTZibx5v&dxav5I~H7|Ney-|DWN1$%1FyagzUW0464;_wU~W1Q5$TW@eGxtUvee z3vAf*8|igK9~@*rr66bh|NrkNM8z+V zAV?>O@ek;bKfu6d00KAO>U2WB)$@6+CBT{3ok>^7H@y{~6FRKmalQ{r8#S z4^Xx6Je&%q%J2@E@iKAb=Rv<(vO+e^|@#56B5{*~0Mg z>UitA{$OPI`~Tk`F#7!u z%=q{3H<BYX7n!%ts-g52=?&#%8AHvnDo=g*(t zU{@(f3Lcro01!Y-3_uV4{%5PNloM>zp5gQNALI8Qf4=|z|KsPsAHV+n`1$wy&wt;4 zfzU5-=mP{0FqD1)U3&iJ)0b~Q-+l7x$B&=CVSxxV`w!S3U{3%6(9;Y60R+_W0~jFx z85t(6Ir{$dcL`zMKcF!E2U7}WK$QOlX#fZyMg|5;)aZo7D4hMD0TloQ7ytwk}@P)zKX@%&%DST%gMqDF&nJ<9}^SM9e)7=i1G97Xij+!pc8>s z0}TKw06GH{9{>J;c@WiqflO8wrXNp!0t65X1H*raF>uuoM}q1_&UQ zzyE+PV}l7nR6|1@Xe}f}K_mmn5Fh~%KtLN9K+(a>4Du5*FzA2|{m;zE0Q4slD+}03 zKxJTefLQ+k0*D1@+|Oq}Kt_TL2e}yHXa<;L{(_vY&wh05Jg0 z{{*4}t^?F62l4F%@9_Wk_yrLV`1}4n_xiEzG57obO$p>A_3H5V5SZl@9UBk;0Q>>~ zF#yj00jE58tx;oDUfVAO7ZA^g)(IQ}Y8G-;ZB^s|2r!a55}@Ef&J{K=uDyPxdE3 z4FCZEF#yj011R$m=m{0X((d>7`QGgNy~*X%==~K4{m|+BO$p;F|LeE!5zX@o6aMwK z|0Cnry8?)1@m-dDW`RF^3@85Z{`vj)H~*iTuYX^D_V?HC|LG63e*OOQ`}ZH9+h%{4 zR^kLX2_S%&fc^l6#;?EsK&pTL{q^hL&)-1T|AVLlN&WsC!*d@f`}fa3pay^dVgYLS z^XKpHUw?lCNw6Z2x?f-t$o}(Zg7`(je_wy{{yg~C5U2qlfPfml0Rvc4oChck)CmlD zkWL^2sNoMV9{$c|wex0wa`2z{-@pGE82$nT5F-OaFayIEq`3SAj@n=l%a5DE9w9oI(Q6o;?Ey zAdrT?A3yTg*!;YE_t&FGSk*xQBO~LVKYsuMhzTgoEGG70+cq{OrGLMF<5CR)KqmnN z5F^kJDrIFqfoA{t^Z)mszkh!J|NR@N^WQHZ-f!>?ZqK&!y&{zFIzP0wn(b{R37FH0BS}Z;-(ZzkW0R z`o;JgO8$qifBy!003d+C8bA*C^$+NzU%!${e*gOW>-S%fvmgwhvw&=%Tb23#oNZ?S z2p~`>{R0^fayL*XP!U2MkOb-c1#;`ZzkmMzW%$Pc5I_(c{;*0)g6#S8SB49$^)FDx zACSQydx0wd{{073{+Hno13&;Vu4Mp{AoLGRCWbOF`~tJSfrb9TxDaUu27mwq0KDvZ UcsT?Vy#N3J07*qoM6N<$f`X&bC;$Ke literal 0 HcmV?d00001 diff --git a/public/img/country_flags/pa.png b/public/img/country_flags/pa.png new file mode 100755 index 0000000000000000000000000000000000000000..9b2ee9a780955566cc7dc2f59ce175f32d3731a0 GIT binary patch literal 519 zcmV+i0{H!jP)|lVPPN>g8sl5Km-s#EI@faK0YA%?%RKmGG2!N|AD;!zX}Teef|3X z_wWCI{=muCn>GOi5DUcMXP^GBIsf;|&;M~|Oj3M||NsC0d;h_|&!7JP`u*?sum8V( z0aY`}$^xwd2q2IKptK|}gRT_Am!FIf)j$AI2PS_*H2ec11_potVu8EfUYUvEKf`}G z`1lE^`X5*+Sm$r%-@k!200a<=Rv$}~=bu-f|Ns5_|IfewfBrH6Nrpdvs+^pde*a>f6qSrfAZn~Z?Fa+2||AuS*t4lqXH%-ra!^K00G3r z@ax~7e+)2nAoSxWko^y??$?YNza~t8coi7j009Kl08{<<|1U847wF+XP}P60U;n*# z@9*{N{$9Nb{(u|?WHSN*&{SqkP0pSkP;|=6L&63i zfEXEE-vV95@Z%rDuRkCLl>P7efnPxL{(%8d!yj-G00Mvj0{}mHT?%@XEt>!U002ov JPDHLkV1lK4=}rIu literal 0 HcmV?d00001 diff --git a/public/img/country_flags/pe.png b/public/img/country_flags/pe.png new file mode 100755 index 0000000000000000000000000000000000000000..62a04977fb2b29b96d01ffef3b88b6bf2ff05862 GIT binary patch literal 397 zcmV;80doF{P)@|A6@UbB2E)`X3x9D*gjYK@dOyv49j-R|ARv|Nb!om5Pi12buAo@&A8j1_q}8 z|NqCt{Qv#?|DQiV^2?@8009Kn!0`Y7lcx-S{{qo}h;n3b`}Y4|zksS4Wo3aj00a;V z*zEsM)xUqEs|Es~YOn@|-@kzx00M{wh#7&Zfj0j6!|?mhe@uX+0cadR0D+zK=P$&S z1T_5l12i2VfWS`r`v>f&U-&gJf}I2qKwu{UjsNuvF#Z0+ASnqp<}V}$85tQ7(W#^a2}2+PDh3E3#;-`R r2#VqJ=b;e{^dbb<+Crtk03g5s0zF}bJ8sS=00000NkvXXu0mjfKVzU% literal 0 HcmV?d00001 diff --git a/public/img/country_flags/pf.png b/public/img/country_flags/pf.png new file mode 100755 index 0000000000000000000000000000000000000000..771a0f652254b4e891fc73910aab38967864da54 GIT binary patch literal 498 zcmV3lobsI zohi_A`bB&J!~)dH$ngL7lczwX3_#UDxxarw>LBRX?|;94{rmL`$Yzn1{l&ll5I~Ht zU%nI;6$RPw9|VBf|1(_vbYnFmA3K-0+yDOt{~_StKSoBzkDopP1Q63dnCt(82%zd$ zpFb}6I_2!oo##J&nDhVtbEpQW0tSEpVuGrMivIupee<8UpWZS`Uj1hH_v_am-&g;K z1CTfW0R#{WvT7jw$rQ2Wy6P|4+kZd)xq2}(*g=aGrk)Yxu73al#Db(Bq?4D0-N;b? z5G()RfB&pB<@s4TkY)e;`2!F@EcYcO{->q=ymt>64xqsK^^5b@FIO-F$h{9?`~e2* zUv?FhqZcm%1P~}#|Nj2NBq_=8`#0mCzd$+0-@loGnqiRuWPl>)F9-k?0|XG`aR!E8 ou!#JF#Q1|6-w+1S#{dBa0Kx%7Vg$%BF8}}l07*qoM6N<$g2}Akn*aa+ literal 0 HcmV?d00001 diff --git a/public/img/country_flags/pg.png b/public/img/country_flags/pg.png new file mode 100755 index 0000000000000000000000000000000000000000..10d6233496c10e52ead975c5a504459fad68ffb8 GIT binary patch literal 593 zcmV-X0Hv=@BjaQ|F{1AU+wpQ{$Kys{`!CTH>0fVKL!SX0Ad1a`1cQJ=-<7ptZmH9SAnX3 z{Q_yQ`~BbL*ME+m|K)!Dcl`DLKS&U$0U&@t8vg$M`{&P}#F!W*N5{%vzvln`{r&f^ z{||ospYrSf)?fc0{rZ3A*Z(w-2B2bq00L=XVPVP4%$zZO`i8aZ($dmav$E#>{B`Z@ zum6{R{h$5&|A}A!9e(}a_zUO_pbY>4IGm#s06-9cg3a4XeYH!D;D>_*`V5R3;NTtr z%*@^Mq=>M$LXVh`5jChqDeaf800L?F_U+sH_3M8E<^KOyQ&ao$p literal 0 HcmV?d00001 diff --git a/public/img/country_flags/ph.png b/public/img/country_flags/ph.png new file mode 100755 index 0000000000000000000000000000000000000000..b89e15935d9daf25173f89a36d8111824fda5db5 GIT binary patch literal 538 zcmV+#0_FXQP)N~0_Y@w0Al+04@f;`V3_{@|8#9F=K6XQ9UY*azdwKd1Cc)=8bAo50U&@_7#Kb? zFg*PK|H1$NTnr5Nxw#FRn^&xvtbK0f`#-;cuKxpc)t_H~fDExeq6ZmH0|XFI1NZ;` zGXMXp{AUnj_^>}zDz4B$`$1}#7U0X2MOU=aELU!7sY=Kr&1{QvMCSsj$|2dLpM12Fg)7ytqY zsKMy}yX6c^FZ@4woIyZ63`(G)I*8l(j07*qoM6N<$f~Z~XumAu6 literal 0 HcmV?d00001 diff --git a/public/img/country_flags/pk.png b/public/img/country_flags/pk.png new file mode 100755 index 0000000000000000000000000000000000000000..e9df70ca4d63a979e6bcea2399263c081ce5eaeb GIT binary patch literal 569 zcmV-90>=G`P)`{xe>ko^7oADI06=l8$gn`UeR2p|@S@&Et-|M&0Ts{jIsi2(+HrUEqpC9EW^)CAOaJl^^9-%lBC8BHP081)#ChQGgo zHUI<=3(zGH+B`^Tu)Z%{q{PpYS&%;j+KluCr=pK*_e;5D)h=t+bKO_wx|Lu6N(?HnJ zN6`lu;-7zgR^V0GaA(7wCwqWF2h;!%KrH_l5Kj8_?>EpHKuhXR);;uSzaAK9KY#yZW@T0p zQ2G1+FVF^n0AhUp{`o&J`0?k5YKbbt8;l4>j(9ExfB*vkhsr>Vq>*li00000NkvXX Hu0mjfu=^7c literal 0 HcmV?d00001 diff --git a/public/img/country_flags/pl.png b/public/img/country_flags/pl.png new file mode 100755 index 0000000000000000000000000000000000000000..d413d010b5b097c4e0a4604eba86dad79567ed16 GIT binary patch literal 374 zcmV-+0g3*JP)Ab?mv3iQ8Nu5A{|6C_|Nk>HFfjc`0biay0|+1% z2B@O{$c+Ek03##gzdwHf0*Hl3)etG5lK=vU1?W~rxN0!OqXDP`Ab?mH`1u+7`u;zA z_W#cxhChG)!_n{G|9=A+zrl>(AX#bYe+w4^1Q6Is4VEzI_51&? zUm!LT$@u9L&^Z7B1a=Zw2xL66B2?87%l|Mi00a<712fnG3~)u5+Wr7F{AKw27w8;- z00L=X`u&GNQW9*;Uxq&*os3{386YGO{rw9;K*az7#P}5)xp2gQ%0-g`0)PMm0MPYZ UsK>Njp#T5?07*qoM6N<$g4HXSwg3PC literal 0 HcmV?d00001 diff --git a/public/img/country_flags/pm.png b/public/img/country_flags/pm.png new file mode 100755 index 0000000000000000000000000000000000000000..ba91d2c7a0de26e554979f6351d42a1a4e22de3b GIT binary patch literal 689 zcmV;i0#5yjP)#D@XkmAv!xDMMW8-mEZ&mK%|%B2@pa*J{pUD?(_ahBqlG}*TVt; zF#yj00{;H~{{a8``T70x^5Ww5-{JLFN2dS#-{Io#@$TjE?&8zZ_V@JO>E`A8`2YX_ z`~rx{;oS`3$G61pUY_{!-0$E2{{CbX;QPSJ^3BEStfg-V`n;-q6+;t+Xyw zOy)sS^8$#8OF-b~@87@v{bTs||L?z_Zq~=&zGIaZd&b4_{m<{;|Ns5^`sL5xKflEU zUjXHSP67x3F#yj01nBVe84eEr00I90|NZ{}FelkBB-0BC^O2xB85HWz)f)HsDi#s! zMLEolpK<^Iq5=Rh0M7pb=>Px;h=2d#;^FNBZtPGJ5bKP1n*{>+?(qio_5k|){tF25 z`1Sjqtv%A&PR-7P0st`p&i?}b008M5=kFQg|M&R&{r=?U4x6PnhWa{`B$w z?CApk{rU?E^8f<&008;|hy@rKe}6M^a>=kt2+9et{QJXr>zQ5ua`W#$8JQS={rbi5 z^Ctr{)6XBj7@3%V17i>%fEb@MFnj<<;{gVSbOv{z8Q{qN1r~!u@ISB;HgNQF0R$KT X>6#2<48ze<00000NkvXXu0mjf$fQr4 literal 0 HcmV?d00001 diff --git a/public/img/country_flags/pn.png b/public/img/country_flags/pn.png new file mode 100755 index 0000000000000000000000000000000000000000..aa9344f575bc92f4c1a5043e6e7d0a8b239daa64 GIT binary patch literal 657 zcmV;C0&e|@P)$VEDzr@CQtS7ytnPF#yj0 z0a0(~eP|Fd7x&oU`XvhO+V>M05dgm54eRCe)!y3s`vCp^_5Az)`uqj_{QRhrlmY-T z0M7pd#W4OILofm6?eh2PFCO>E*7Xbx0@lgN z0st`p&i?}F01o{F1pxvB0QdR->g@pe`}O_>?Enew{R9F2_ajBO3l7@{2K78Gz9u`z z008^~05Jg0{{!Vds4FN77!dx<`5YAi^1B#F4D8&$5I)oT72fOU?&Jm;1k~m5*82ws z?b$fe$;$!&F#yj01PbEP9OVUy$0!>9?*iHF&;9=9{2wXIjNS}D{P+g={1WdM4-6DF z>gCe?_5uI^`T_tk0M7per>)2uHb&;=(#87${QNQl(a`kw{Py}Y1nctu{`UwC>ihis z2n-DS{QU(1_5c9-0*DC|aR2`P{`;Tl?{DV6zkmJy$H?%Xjq&fFKfk|zXRCbma58?r#f z00G1Tv;`<3F2eQa&)+|P|NZ&>@AqGj(m#Ly0)zk56?XydpHmj-@bfY4J_0oTA3y*x rGBBhgM-@1KAF z|M~a-&+mVKfs8*8^zZj?AObQ@tXl^VK#U9w|7)um9zFT5!1JoGnu}M2{r|uJ|AC@F z;y(;9F@d=M{=L6-3m|})KsNsS&&$bh^PO0375Ag3EPt6;7?_!X=sz3*^#j#0Gyh{@ z0SF+Vlm3Z`F-Ar(?BDbM+y#c5od5B%>y$nn=KcNZ|L>nr$AC#T8JS}%RsaMLPy>UR z34^BAe{Qb-hmSD)`~82~jewZHH;sOtVgB{~2T_Lx z^M7~ue_6TzObGu%0nl7VM#g8?t^ouP$PYjMFazDG!uzbThlgL98zlelKgeJZ;~z*Z zSPxJG!?$k$0R+;(`ulr=6P)M00|Ni~>`!`VeZ#eh`fLE8Q){QmQQ=eGY$|NqCtU}^v=1iBd@fS3^8`SJ7r@8ADF ze*C{^(f_{w|JZZ@y#o+HED**2KY#i6{~yEeU;qEJ{Pzp|pOucqNnlF=0*DFdB%s0n ze*I+l{{11(um4^iSXF}z{SS2YKY#!N`GNV*AE2Jys=p_Au>JnK{@0e3zk%WO_xG>A zzkdJw^XDIk{pa6bWqzfjEk^+Y2&CaX+n+VzlIN6JC4s^J?+-}zKcEU=eEj+Q{|`_P zFrfeb|NHmfUxq&n00G3<$-qz^$iN`P!0-!({va{_z!?9)Bm)CLfB^vHpj0t%_B3$- O0000op82)aAGO;3n0AgYI#891D4N?IF|Ns4E{L7&Dm*MXp#y@|U|NLS4 z{rmr)KmTFmmra`h0*Hly38?u0llM=#nEx6H|J4!x{U`A6pVzdvv{Rd?JIrI9L zs`Fos+0`Q4a^C}Q0+k5A1KaQ$>Lf<6lK=vUh2aka(D+|}f9<^YM_B9kC$A?0Z|}3+ z`ptao7t>dSY6f5^0o~2O01!YR8-T_G^}PA>yZ7X8cHUpx=KlJ_{+sC?ST#h$AD{+c zSpNn30U&@_7``$5{_{tYU-I{#-$2g*Lz_YNHw#c5FVJd`!65YaF9ZRd1Q0-s48{x$ s?-&?Px# literal 0 HcmV?d00001 diff --git a/public/img/country_flags/pw.png b/public/img/country_flags/pw.png new file mode 100755 index 0000000000000000000000000000000000000000..6178b254a5dd2d91eeaa2a2adf124b6dba0af27f GIT binary patch literal 550 zcmV+>0@?kEP)~nkZ zGRMDC%7~XK0S0Tx8Wd7-QG59jKr9SQAk|O4{{Q=*0jSzY^rW5C9>)KFc0aM(`^fh9 z|Np=Kfe=u&EDs~YzkdJ$!~!z=-~YcrJO42FE6xsAYGq`2_Mbt_P-yG_e|4Mhx&31J z|LZSE!|%TgKvw|-5ED@OU$BE2n0|Z9O=M(v1mpqrFfl%gR9?uy{Oi{rkOq*RfB%6# z1PCA|pp${Bfd()#{N!P|4fj41<69Aq$E=K>;Tryc4F(7xpbZRv|AD;q^*8I6-->Vz zfB*5n`o{nM2Peo6f5F}W+3*h_fS7<7;^vxc*?8?`g7w0kCk@cHxaHDByoH$n$~K0fYN5BQR!wAqjL2Kmai^lz(OTgcO%Q oK%!9YKcr{|u^AZn7ytqc0H_5zuk@Q*SpWb407*qoM6N<$f;OS^T>t<8 literal 0 HcmV?d00001 diff --git a/public/img/country_flags/py.png b/public/img/country_flags/py.png new file mode 100755 index 0000000000000000000000000000000000000000..cb8723c06408828ce68a932ff472daabecc64139 GIT binary patch literal 473 zcmV;~0Ve*5P)@|4`Xj5kLT%`al?B=W5I`&pU%&omV`KRD53KJ$2txq_ zg7fbmh|k3I@68*40Al&@kBy(1nSq%Zss!k?M~{APS@-YvZ#JJG201x|0gQ}qez5@r z5EI0wa6>`h%cteq@9Mue|Ns2{KQdro`19u4 z9*F+^0|+3-w;#WAv9bOIX#?r_2Xj8qX@CF21pWab0}C_Lljko10*H}8@M3d^)Z>@G zf!6*0!vH4#gGo5!|DS*V{{HzdC&9L6!fAj26p=v+03Zy*=I{f8|9_}7o838uP_pBd z=z9{Sra^iHOE|+IC{ z-`~G~{{ZRVfB*dX11A6d`CYbrEkFRVK#Yg0UeQ$d`q2ZhA|Uz&)bIy{egR1#X&Imb zfB<4bR{ihazwp{dkXn!mkfPsU#_wMs1hfID1|Wb~;Esc6TU=EJGWFLlunkbN!Q9_} zL0)432p}e)sSwo=4bd$f5a~a^fBpd(3|0LbOac`E1P}{Q!+)sq|NsB}pI=e{a>g%& z)4?|U{s;0mNCQ9sG5v!Z57O~Jp}QY!5N0Be0)lgG%);U`26kn-@hOg zU%!6+4+cOs(0HIde9xZz`}Onxub&LUB0x(30+2WcIRJn#2ut|?gWYu1Cf+!-K%B8# zdf?1WA}#uZ8oj7u>$I1i0Al&`=O0k%-@icgAIJnM0xA6maSq6BK-ECw|NZ*S`0Lj% z1_pot6puj;05Ax`F!=umqj7^frO?t|3^&I1kxUq9yECc+jQpY84SWH_0#pxl$?v~F z@*hy-KN0|X07U)z`4{NpU%#2aHUI<=%a31wK(7Du52Oc(|3O^?R1IN+RRjI-n*kVB z3=9AP#PZ|EACPLGJ%9cJNh|>9B%spYzZw7h1%?tp0I_@ndg9MNE>313@6R75NcceF zkr51-#U+7;F#`Sf7i0rK0I_`g_NQ&Zji7_t0d_@@ozArL?Su`s;*Q{AKpRQ~TD44PX+*q00taD0I@Lq1k(S2nt`tR{g)9*1H=ZfhCe_Je;NM%1^NLXfPkU&2dGq1gcanC zzsyWb(hLmW7#V^70~+_6f#nZ_(jSmgFakOWAb=Pd7`8Gn`~pjZT=;x%FbfOAzdzu3 l1(F{a{)I#17yR-T)##NTdjqb^wzQ(`1@?t)Ix4MUXz556teM9A7Ic zq_@itH|pv>q+zrjZJ^Hx5bj=fD{5McI3ol<@^-l_@~tZGV7p>1CU&qG~{YccyC-q z$8~P)6sG{nMmQy85K$E6L33rja$x-b9$ literal 0 HcmV?d00001 diff --git a/public/img/country_flags/ru.png b/public/img/country_flags/ru.png new file mode 100755 index 0000000000000000000000000000000000000000..47da4214fd9edb383687c1d4f84fe8b42a51ceb2 GIT binary patch literal 420 zcmV;V0bBlwP)X|NRSO0LlM<{-BURBqYRGSojej zfLOL~|EH_V_~;P>Nc10*D0|Jxss< zFi1)Q<$$6LU}rIc*dU*QNFV}+9T))>0|XG`SD?F)5CbX~O$rDA0t^5@iDe$xIAIn5 O0000N_~0!B1ZtR02zJmAl3i>|Nr}+@!x-t zvcLa8?7#n*fB$3p|L;$z$Rx)9zm`4EX`B2HAb?mPX21FhQ~^@@2dMNf!=L~E|NQ+A zWdHd6KUK1c=hsc9f8Rnyezh^=0R#{e(Ek5GMIci_s{j0D`2GL?@4p}ozrlKzzsUIh zhw0@n(cNzYfer%*AeP_1{{8vS`0Fp&84w#lmi+$#)ARI~X!Y&+KyeWsW`<9n00M~d z^G^m==F0#79{mS71ZX;t`~_q%fY`tPf!Mzw;*3o4FJA8j2p|?NkT1UdXZrG=;TOYy zh@M|qNhT&CJ~jq`0Ac}pl#vl6#K7 zFA)9u_3syi0VMzY2QmHtx&Qtu@&7&0&HxaAAvs6^7=(cU7?W7&6Z-$piRe`dyDK`^ zN$WO$zWL=wEu!PO?Vu9@iVSM&8cWvf2p~p=WCn&G3_$lI&>tl77dYY}Tp)vm0U*Et X__=7oxWDB`00000NkvXXu0mjfV`BRN literal 0 HcmV?d00001 diff --git a/public/img/country_flags/sa.png b/public/img/country_flags/sa.png new file mode 100755 index 0000000000000000000000000000000000000000..b4641c7e8b0dd79aafaa73babdb525d3d2dc6a8e GIT binary patch literal 551 zcmV+?0@(eDP)4!1u&@QJ10&`rk^HbMk8Ee|uIe&H zS+;4$DbWCt7$DsBz5oJg0IGiW@flDZ69W^F_wDDmUw?kFvatRB^XvPsAO9KtfBpI8 z%a6}8Lb5=`00G1TbjJVx|9}4ZVWnuJEUfzZ$EVZx<$*voWzTGcp~$d(=+V{_?X+ z3y#kPS_RYq5I`UeU<=TsD9Bk~@o?qi&=DPd(?v86a{(`&&3I*E&g8|Wo?Sn;v*0j086Wc~d66UYT>00&^E24DBZ$$%)DV9SRUYEY{Wn z85tspN%!Z^Q&_X+!-^Gee*b1;`1hkUW~HJ2(d(c7|`w2)m##KhU^#|s1eh2YzMVW-Z(;)C{QJl7?-Rp6DTaSP8U8UK5I_L2Ffe?nu66~hU^Xzg zV`^IR|NmbGhChsd{xGln!xZ`Z|DQkqe?!SHn>GOi5KsdU{eSZ0I}_6%bMu4$|G#2j z_zP0e_xu0e-~WI8{{Q>e|6jj=B8;-KKpOx8hy`TyKZd`5nT(9?%F2RO|M~TQ@}K{E zz)C@?e?v4d{QeEp01!YxcLP=bXJGj0<#nFn|DXSVfvSK1-v%}nhwkOu|GvKejg0<-0ptJwK&1cy1oE`H<`Z?5M+~$7{9gq& zn~(3mv&(;P@Bi|05T(EW{(k!9>4UEid{_Ab1OPDr&i@1e0Q5gX)c>UW0I2)_6d3&w~i*%}NQ0G05Jg0{{#gAwk6j%|K36Wcrx-Z zDBuV5-R-~I;@8;l*yr}=_xShx{`~y_{Td7!{r>#`1^@zxvDKIVAJ?B1c2ZXvZZlkB uU;qZ}2Vg*eW0eWa_yywvNgf7(00RJ0?>)A@UfIF`0000`tbb&P~tyW8d(D~GxMiUp8x`gh2bB=pZ|ZjUUI!&{yK1G;4d%$ zIuA;MS%3cg{`2Rrii*m`ix&X`h=t(`1IH^)u0vdsLXv;@{sQG7;18Jf2Mqqg05F6A i0*HaZmVqPy5MTgGElxU<64PS<0000a|fPuet^$h(7pHv_{jfB<3vn*8q{15ov!pR5qo|Ns4BVf^v!57RHO zo?l?}`yWs<(7C?=0*LYNK?X*pGKOD3v;Q#s|MUOfZxH$qCjb2cBQWFtZ$@_cPkRpn z1P}|u9|n-AAQdnT|9?TXAyk7H4FCQBl>h_~&`AvcLF)bhMS(&{8jwh^2qVzve;5D) zh>=0RIIAq+{+o}$pxEl(3%2T)`P!1-fBt~{@Pp~sA7J?W`}60ovKZgl)=2;X#KQ37 z&mWM7{{H^+4`$e}UqIEMAo>Rk|KALM|1kdh!vsX^3=B^I0*D0|DL|!?{M^6*VPKPH z00z{*-?BnHps)wJ`QKllIe-8D1EGJQNCgNWMh3}bctpOPvlbWzK!=K^+cJPc;};D5 g19O2S13-WQ0NlBGh$rR(5C8xG07*qoM6N<$f_QiWUjP6A literal 0 HcmV?d00001 diff --git a/public/img/country_flags/sg.png b/public/img/country_flags/sg.png new file mode 100755 index 0000000000000000000000000000000000000000..dd34d6121073fffcb2fcb5b9402b3e6361cded35 GIT binary patch literal 468 zcmV;_0W1EAP)dtOYis{Le*Dj#JuDs`KNc)tl9OYUmifJZ zA4uJ=-~WIA`uFP>kj*SB3$z3vfIu4l|NYA>FaP8D^M5;ba1<8<4f|GC2UPm+H&ER# zkRGsxe?Y{*01!YRC;dKnkj2`X)zkCW_8s5b+Wvn23^5y|0jv~A0#!5q{{4%A0U&@_ zfL8pya^=_l{r?#l8F+XY{;)G~as30ko(ZTFY%s&0Ka9WsFiJ`S6$1niy`;Hnv5 zfRT}r0R;fL{~@sn5I`&p|Ni}W_wFAQ`~gBVzyJ~jk&qMs5MThRyiZo6SsHx+0000< KMNUMnLSTY6dB!#X literal 0 HcmV?d00001 diff --git a/public/img/country_flags/sh.png b/public/img/country_flags/sh.png new file mode 100755 index 0000000000000000000000000000000000000000..4b1d2a29107be96413eb86e64a75ac7a3ba5793d GIT binary patch literal 645 zcmYL{Z%9*77>Cd9?woAXO+#E-F%m^1b0Xy*Qky9{B^@hJqB6}jOKKPc70u1CS|Ug( z7>SA^1`5-}4+VvY=3G*k2%(8O!OWTIn!4?td(P>GANugV4?p-l@2B^fCONIbD;IAODX_{rV|BCn_NC>%qlWoHrzH=l|0Y^Rhgkwr%>N3 z(d)FjlCqjgyY4&yRH!;rb)|Z-v~HjxIkvar`*JLyzxBc-B?Ix`3*qGz4q3JAd`#LY+Xw^k(ph!n`d2H7`aI`Eh(LrOLs%9g zj93;8ws%s88WHkIqXqnSf?YSjh=@dF-}4L7dS0HFB@iNj8OY*&4>%Dn8t&*i)aXz6 zSX_wQ?~e=9UcwhrAtAf8XLVoTbE5+<^|-KK=D&>)yX6u!zrPCrbEr|4Yi(XyIGTQI zFEDsraAY{)DhUd*DN;Q?!uSxvkoT|31dF#>2L0DGeRcNZNehm>xm~}-9q?gtV@Qz` zv-lB19|m}3LHcg92}TUOb+%v(0bnUhB(5rQI9?ZY)h~Hw=%2Au&~WB@t;^kVE@F0Y z%=8f1ZN}R1MniiNxkJ!a;3!XFerfimE2A;1XJChGXJ=)MAVRubE8WFo1T(1Cmhdfa ztzC{Qms6asjkstFkFp5L#maeek84Y+NtW^Wf=SRytjpC1=BCX4NH^VxnQ`+YXocAv zR?lKskkKZN7D>{S3>4;4+gPYYq0_5iq@jsB^}M0yMT0|p`lM;R_dwbVrBg^4RRbsq Y$WB%-43-yHbAJTXS^1gPjGK@C0`m$%7XSbN literal 0 HcmV?d00001 diff --git a/public/img/country_flags/si.png b/public/img/country_flags/si.png new file mode 100755 index 0000000000000000000000000000000000000000..bb1476ff5fe8e0d3af4fc6bd11e513d95fd9cccd GIT binary patch literal 510 zcmVm9@#;PEzrU@Gx(rNA|FHo^Mn<3|00G3vEO>ZAhtlWIf1*N!=PZ8p<;&lH z{~7-L`S<7dzu$lU0~x=8==VP$LqeQ==EOq)0mSn6{g=m2e$<)X`O9Zpu5&$Np~P9Kn=gZ0kf<1I-4a zUtk1c`~{K>fBu3(0U&^Y8d(1RW7^4V^6LZG89>!QBmXe{`pfY9FT=0Dj8O6)!|#7g zK*az7#L~(TO=a{vExMP2%`MCSoB^FIcLe_%lf;|~%E5I`(IQNh}3Ao>6Q|DFUXMn*>AqQd`w z|1kXd^B;tM|Njjl{{hM0zwd6?1Q0+hV1xeud-4=Wy?p-%sO`^#2S61Jzk!N?s)6X& zzhA%p|N6}=D+{y%Ab`Lc{sL9~1=0UN4*CD*7s%9KAf+JHKs~=eB-8KTKvw|-5R1&; zzd&a|ob(5%^Z$Q=wHy9p13+aOpFRNu5F>N&`Tk_-7w>=n{RejQzkfh&Kn{rf10?_b z{tFTZibx5v&dxav5I~H7|Ney-|DWN1$%1FyagzUW0464;_wU~W1Q5$TW@eGxtUvee z3vAf*8|igK9~@*rr66bh|NrkNM8z+V zAV?>O@ek;bKfu6d002F*|Nr~{=Pyu%qcHbB24G}l{PX7zKmY(S0M7peLS*D^UKK~y z+6(XH|9gD^r>6bY*OZ>+`2GX^{Qmv@{`~#_{QUm>{r(dW1b1xK0st`p&i@3&%J4-( z6Giy=|7vRh4-WrbUHx`??d0*<@CX3>{QLa=`~Cm?`~Ld;{u~zu0R89!i0SVi2B1U! z{r&fU#+3gbKmA|6l!@v8=U+^J{{8>`mjUR&KfnL~{sUtC1qTg400I5L26X!4i0my} zo;{uZo#CfPX{7DT1DwDAf(-fnkMZ|^ra%9I0mS$d6bftr0mQhUfkCOfiE zM%HqlKU{maemr>kAILSo!3gZ8-+$N@WcKe}3J^dnRtyZx@9+FOdynDWy$P0%j62V; zZC)?>`}hA}zy1SB2;@n0006p>>h67sJD!pI8@uK6+#GPW%ce@CEFZM znTD)%K!F4J_qpc@AQp(ZPk%rA2T{TB=kLG2fBygZ4O9eD_xsnsUl6u5yEM=SfB<5# z6|+@gR}o|u1R4pn+XF1YrQf2oylS zAbS3(sBkhcTmc9mV6grHN=r(zLW3F{=D&YK84x>x_WT70A}|U80*H};VI?9O(a}F7 e1_Utx1Q-B;QgQb4eH!Wj0000PEol7!5I{@}e;EG$`~Mq){{FvoV+so& z6Zek;JYV*``O13q$!wq?2!V|I&+z9TKmf4-@!$W9Km&lr1Ie!(IeUNcyZo#F`{&0p z?&{CqnEw9%50U)~((nf$fIu4l{AK07*qoM6N<$g2hARp#T5? literal 0 HcmV?d00001 diff --git a/public/img/country_flags/sn.png b/public/img/country_flags/sn.png new file mode 100755 index 0000000000000000000000000000000000000000..eabb71db4e8275a5bfb7b1b8f3a8374d50da95db GIT binary patch literal 532 zcmV+v0_**WP)6{xSen|M~Ox&!2yPfTn)={d@hr-#`EU4p90fz>J~+=oo+i0%`d3@9*!wzk#ZO z2WQUEDXOGe*gO;$t?-?1~^z5 zels!xNuY7SApXOk^arFAjDSu82p~p=WCoxfknArIdOZOgs+`19}IpFa%0{xSUi1L7diFBr+f01$veIX40Tgn<}np4MoMn3N`o$god9 zrkzn;+j{#q5F}xeh49xZuI$05IKi0v3Lq917Le*!UxBJYN`Vr8|1$jj50VD5fvSOw z-(ZF~46zx($8#~-kUKOh9u19TNY0I~dG z`1hBA5y<}g_y4cI3}({*g*gAq^Z#dL_#dnH|JmpNJMaAe2etv|3x;0|00G4G>kr86 zzYquf`+xk=e@^EAQat}9xc>97{@;He<|?3qzo3u+2q2a}APrzwf*tnl_y6c6m z5?7266~G|=0}2mdsDK;{bP_-SF*1~XW%vY(NDyK`u#uw~6h{mUJPZH<1^~BhckWKq{Jk|L6Yw|Nrm*vcLXk{sL#*k)f0!)jRF?KX6FHLLC_7zyJOQ#RJf}AnqSv4E_E0 z_d>^cfB<4-C}v>z1dGUDNRj#r9OZBkNc8hC00bBS^Nnc?6(4BA00000NkvXXu0mjf DM=x{`~^7feaw| z@9%GBrr%pd7ytr@3Fu&;D>$G0;d%9&ckUlP_FsJLznOpkI<);49~%fU|NOOg<1c>p zU%YI;<@gu?0tjRS1JJ?0e*b^_hvDg;|Nnpg`SWY#%HJzjff!!CzZWj}wP?w&U%!5b zMg95nhXEjfKpOu4{R2W=zZm2}8km@V1qJfIu3a z^ZvQT`ny}__wU`%Fad@Sg!~P3A(#OSI%WR9r+OFw0*K`e1H&7RKZ`vj#ee<&!}=E_ z2M*0Ye}N7Llfc;c2aF@2)L(`#3;+SdxR!w-xR`AxVx?>``QKoX4p z{r>$=Qj`^FFF*h>?mzuiS(feBuYdplGyMPm{~rVab^il$89+1;fd$yvm=2wM1rR`t zfB*hvWMl+7=|4yZ16bGpcem@l{$rO?srZisfXY67`UDU_ED+y9gdmKw*T0{<`x@v9 zlkZkl}Tegi}1FC>)y z`~`;fe+5al?K4jS1P}|@5C%z67NEr6KmWj?{{J^f1u*QH|Ne!BH7L;kfnp0FfEXE= rk23safJY>Z`~zeELt=n*00bBSq*!cC{}>3t00000NkvXXu0mjfg_GrH literal 0 HcmV?d00001 diff --git a/public/img/country_flags/sy.png b/public/img/country_flags/sy.png new file mode 100755 index 0000000000000000000000000000000000000000..f5ce30dcb79b443ebc1615fe4889cc26e2d762b1 GIT binary patch literal 422 zcmV;X0a^ZuP)@|4`Xj5kLT%`al?B=W5I`)SK7Qoo3V-qKd5l^# zgv$taFhBq?zJ2?apPwJU>mjm?jEr~g+yMw6Mj$IKE$!aDd%wZp7Z^YoAQH^_^XK=U zKYvwJR4!h;2oOM^VEz63kEEm|Pzoptl!JgjVAdZn_zMF-#Q*_BG6D!N0MDLEDh$KY Qwg3PC07*qoM6N<$f z|Nj36M1Mf!|33`>e*gaqWCO|HKwFtO#a&sZ0t5gt0M7pel4t!zdFAutyc%F2ByLSib7d z-@iY8{9s{WsjaVha_-{UlG^LGvEO)A^**!$6$1ni3ljqaFhGRYPy6$qf&JG{kKey{ z|N8ae_ixs}{}``bdDhkT?#VOp#VvoBm>H#|fo1~)5aY*A1~#Xrf1jTKgW~`H-wePI z{Ri~aZw6pM{r$!84-_k)u=@Ltg1)Cf2{xiF#iKZ95W~`fDE7~ z|1pDN1{?_>vH!pR*Zxoe2p~qo;+=0k{eVOy5dHe~@Bg1a48OqWACLsaJOfY!7)!uZ dz{3I%U;rfUVNTmRI(Yy9002ovPDHLkV1m_xKPvzL literal 0 HcmV?d00001 diff --git a/public/img/country_flags/tc.png b/public/img/country_flags/tc.png new file mode 100755 index 0000000000000000000000000000000000000000..8fc1156bec3389e54d3c5bb8339901773a881e68 GIT binary patch literal 624 zcmV-$0+0QPP)Mt z2Fbqp@bUfi_XiXZ_x%As_WHU2H1hfc9TM^w3-JC0{^0Bg`~MgK0Qv%mWs{6kqN+Cc z+b3Uc-enXLWmi^s_2~6APR{Qy8GilxQ)j#p({t5y7`^LQ4&lw9Z0Ra5^`SSP!?%<&3?fU!$2L%N9{QCa@0Qv%mW$D2m zA^~iFet$Uihac>@UpF6ozx?O#ufL4HfBpRUndtzp5U;>@#TUPR{|1KG4}bt-0)`aO z`@jDF|MM3p^6S^{zdwKf{q+mXVfa4fgq7?27ZxvH%|34Sm*EEk!#{ukVgYLS^A{)$ z3^fq>3urc&4I=;kz4MwgW4X`2zkh%J`Om=vauPrQ0X6*k{pY`=2p7%A_P)?P zUwuVdAkKe=-@h3}_C6p$AOxEwQo+UIeHT5%mg3lYGL;@HP(LjqG0$?6F}(Ht8A z0K*^*BsuTDFa|;zm9Mc;PRcq|KMMBO%8|{GkrU*a2x&r-3HS3`xnqW1|R~39RolBu`vAj19S<{HjoWK z4L}tj8-Qm20eTwfuYbROLyY(bwgDi3SQvf*HG+%>>H*pSauQI_zkmNAP67ugNCPlf z{{97O00g6G8FFbZ~XHln?_bqJeH^V)*lb;YTJ&0)m(r7ytqc06HRil3NF`RsaA107*qo IM6N<$g5!7R+W-In literal 0 HcmV?d00001 diff --git a/public/img/country_flags/tf.png b/public/img/country_flags/tf.png new file mode 100755 index 0000000000000000000000000000000000000000..80529a4361941e01d1def5d581bf2847cf99fef6 GIT binary patch literal 527 zcmV+q0`UEbP)KfiwinSYl|od6I(APxT+{=azr6>87_{|pZw{QvzM zh#vfW|M&NwU%!FG-`~IfN=xzq?EwhD!Wi2C0Dv&)|Bp?Zs+hPi0R-LQn75%cY-O8s zAPsw;Q!9X27=Hcx_y6DDzyCn0!4P6RP{Xg^fBpb%_yyDhbQs7f8-Qku33L4cS@IvK z8OZ(n2kOgTzyGb@ek0IRW9ouaN6$P0x*s5bfH4)06qlIj4>;1{Yyp4(0|4dQTo>gF RMrQy3002ovPDHLkV1fdR=hFZH literal 0 HcmV?d00001 diff --git a/public/img/country_flags/tg.png b/public/img/country_flags/tg.png new file mode 100755 index 0000000000000000000000000000000000000000..3aa00ad4dface0a9c23744ab451cec0443f187bf GIT binary patch literal 562 zcmV-20?qx2P)@|6mN}b_RwI3=Dr@=ogG+VE_mq7La^(HIVqv%*^=nCj$%1|9}7Uoj(8m`RC6+ zAo~61@1NiQe*Xrt|Nh=Ea}z)Sfi-Y&{FjhmFg0aZxaj}$=L~=U)(L)O`TP6-zu*7= z1Ib@N^zYwqCU)6YF9v`BVgi}{>lcH)J%gU!|J)p)>i@rg^Kt&>WCWsLoQyxgk29dnvBpN5vQb0*LYVQ3gi&tp7jnGyMMp z@*mKbe}4Uk_!#8f-w;8De}5R+mEP<>2M|CkH{LKvi2wQh{WmZi827ytqc0C8?ZF&p#S!~g&Q07*qoM6N<$g49|K A6951J literal 0 HcmV?d00001 diff --git a/public/img/country_flags/th.png b/public/img/country_flags/th.png new file mode 100755 index 0000000000000000000000000000000000000000..dd8ba91719ba641502bc7ffda16c25dc71b2066c GIT binary patch literal 452 zcmV;#0XzPQP)@|4`Xj5kLTn#2^O%5QJf({+!S7KyMr1NSdb0?wsyYS6cNVdko7wub89?$EL;)x)00Ic8;m6; z*ldXL$Yz5{poPB}7`^}m5DPc2nu@9r4=+1782$pC1a=$HjDKJ@i17#P6G=(#Q>V-T z0*LYF&!5c9%>VxV!xcCX0F(uW(%ZLh0Ro8SA3Hl!NeKhQjeq|#{05SL7=Ql;MhF4{ ujRQ&nZP~jQAb=RZqDCa1IEDa#00RK>mSUL#9?hx%0000(5Af|u+{y|g&!S9Ft%(8imf-XR*|Ns8{XO&`< ztN44b9;zJZ^?&~W0*LYB$B#TbJpUo){|D*(`>^x>w|gKB?9z<#wQ%E^n3(S0zYh>V zEO6uD3`W^f29Wmu3`mZF`w$?27#UO<(rVJ~y}I}N&+p%VfB*XP3y6U1Uw^?QNbuL6 zzrX+d{i`Cbal7Zfib@b53s27mx! mU=U|u;Adc9XCM*)1Q-C-v^a{iiydVE0000+lNu{Qmp=|FW6D0*D1<1Ovm9m%kYQ|7p*Yo3ikU z7~kiESLFZv{{Q#iA0zqwf4<*Xu-*CD8$MYncA&8U0mQ`ckKy0n|9}7f|MB&2MbDkQ z1j{KKBL!IA2(Z7F;(zkuBe#mo9TT;SzkoLU{s(jwKmai@{QdX$=kGt?etVi7(p9*e z(|o73K&5wiaQBkXEB9so{`tH8jIplb^d$ZPi5h|uLD4*o5RnOS|{Qd{@Cr~j!0D)}y0}8T#$FHit{lE#dc*g~k_utsL zxj&Wpto`+k?f2h5mv;mM4gK>M6!-uE1hnA~P_?8O+wWh(`!4wX{Cn2j=z=is`~Uxb zzx=@K_DcEUJ7IZAUZAu8{sn~+Kmai^Fsw$4O9qB73=GeJhA}V*Ffg!yW&gnt6F`6g Y0QOrYft$MNbN~PV07*qoM6N<$f;FEW z@b~ZkKS1)&|37~i{`~p>`}cn&^2?@800G1T)Nta$zvt>t<757@uK4@^{4bCmpb7~3 z1tfp{|Md&VW|Wov$G`v(Kp+kO{{26+>+jbuf7A2+upj>Wf7$Q%RxoV1$N0e}Fas1Q1BW)@@rKJa|xATBWA`D>(WW1JFHv zzy7cP0k+{MTm!^O009K@!>LoJmn~avX#S_9;5YZ-KmTX``u`YaHjD&?*dJiPF#rS* zNW;&cKP?=87yABYZ~OCq-%n(9P{tpi22h9t6$1niNQ1e-pD1xDrvBdy@BT2zfI@>2 zY$OAO1fsuxAqeOsfB<52Wnfst00RHO2+qJn5C9Nh04p0wT74^6IRF3v07*qoM6N<$ Ef~oTCkN^Mx literal 0 HcmV?d00001 diff --git a/public/img/country_flags/tm.png b/public/img/country_flags/tm.png new file mode 100755 index 0000000000000000000000000000000000000000..828020ecd0f6fc73348373c9e7a235fdced09de7 GIT binary patch literal 593 zcmV-X0VKiqu&g@GZ_kb&V31Gky(*8`UselaloV1SZ886W^4001!n&i@1e004Y* zk0R&!|NaLC5DIk$@CyeC@AU9ebDYiJ)zswL{Qdm<{`>p<{rmm%TMiuO{1voVt6d+$)WVk%^O){nwx0zkdJv^%rQ#Z=eQ%0Ac}Z`19}Y zkI%oqe*ekA`2WvehFzDBOo*xZ{rB(cQ`_&qdGhP`Z=jyvAOx}jAb?nae){+CFDvWc zZ(siLa{c@D=g;LQw^HX+sY@u`d-M3!$G1SGAWQ!Ifj9{ufLMTG0<_`#=U?|Ye)D|< z8pH7G?=PVA*@stt1I-4xpA0e#40jk9 f${7d%fB*vk3(!F(w2U8u00000NkvXXu0mjf+94t7 literal 0 HcmV?d00001 diff --git a/public/img/country_flags/tn.png b/public/img/country_flags/tn.png new file mode 100755 index 0000000000000000000000000000000000000000..183cdd3dc98c6957bde83f375a431e543a3ce9e4 GIT binary patch literal 495 zcmV@|4`Xj5kLT%gT|L_r@28Q3iff@h;hzaCwplYy<4FCT9KXmB7 zhQ|Nt)Bg(#gN*m^{Qvt8$Of<_K(zn?!~}BDpT8hi0uj&>pxItt|NHv>A2{&e(C|Mm zFGw7s;m;qS=>P!)((wQ9AF!W(0sZ}7M&|#9_5TYB{zpfHZ20g2t^wjCfB*tH2_nSs z8?4R7=Kq6S{f9wP5^T(0hI#XVIvK%6GC)WrB}fdh6SO{nUiXv3iAlW}aH!uJM5J&?vILH{_iZHeP0c!XQ3PuJ7 zfB*t%VEX-sK~fTI%wL8-Af1e0BN-qh5dHlNK|sX-0mS$fH6j^sMDRaYQUC}r0RA0L U-~OCsp8x;=07*qoM6N<$f)6&Rz5oCK literal 0 HcmV?d00001 diff --git a/public/img/country_flags/tr.png b/public/img/country_flags/tr.png new file mode 100755 index 0000000000000000000000000000000000000000..be32f77e9910c0896c1ee8e7ed4f0edf815a517e GIT binary patch literal 492 zcmV@|4`Xj5kLT%yy% zCxE~j7#aURc>+`lQtjr(@bBOM9XtMi`UFz<>-YcPzyAOF1!ObI$^vZw2q2J#|6qeb zs-vSBOie-Rl$HN?bb!_U0_g#3VEFwTr~x2=m_Y6Zss`GqsL0^t^nd#F|4B*zH*EO- z_3QuT%m4H6g6)A?0#pkSKujPf{rCwIb#eU5`( z{s2t}2q2J#|9}60{Pg58FvR|AYlEDjruP5RC6KEgJ^H_IA4CJhNdN)F0>q49OBog~ z{?Etvzo-c07$As@{Qu?4|3!FoS~(Xu$ujuK&Ki|5;f$7pYF;Nfxh(xqFs zZUMDjzjMbfEbRY>5C0Dx`v3aP|JSeozj_5kj66I)?%V+g05Jg0{{w&k0RJ5w0P^!X zSy|WJ-R9=z(aMV97z{4myy=pj4$^bx3|zy{hYtY)h`Ao9!QA5iyLbPuTmkB_PfP?Vee~$j z!-o&gUAyj)n+wu&>J&%=KmU)%j{yP*q=CWA45Z=rFNTYkfO?!$Q_o+&{s0WlT)E;? zRQUhj|NkdWF!J+%fBF<4fS7B68jOwqzkLe|6QI@S&;S4b-#s()-1Y1C!Qjl5tA6F> zpisYc>&x@!00G2Y!@$5~Xb92(47=Z8{{bD%z~Ggi4>bPny?b}>+&OjWQc!g@0~6Et z-Maw-h`Ac5K~JB7o9jOp7XvpB0}lu>-nqlT$mpA&|LpDCZ{NOs`t<42?c44-IbZwx z0Rjj;B7x{N1H(%OhUW|nPgz+XA3Vr_1a9x%4G>@ep<^{rq?UTJ00000NkvXXu0mjf Doa;3l literal 0 HcmV?d00001 diff --git a/public/img/country_flags/tv.png b/public/img/country_flags/tv.png new file mode 100755 index 0000000000000000000000000000000000000000..28274c5fb40e5d3bacd7c05d9a1b8017eeaffa6c GIT binary patch literal 536 zcmV+z0_XjSP)mzpv3Mmw05Sgm{~xFtLc$sUu>nR##!sI<0R#{WMEdpj-yJ7j ze)|0V&%b{_!~X#7`1>EofS_N0|E)GoUH&SsFy}2m05O4F@}ffYb$qvjbfSUipMQUU zgY^9U_4m&&5D8KZ)HC5u^uad<48Q&W1P~L@hOIAb7eDp_T4W)^*Xg48{r4ZBJ-_}k z{QCPJq~Q-pPnz693xQJ%zkUG(5aZwHtqd&u|9`*u&+TyOvq{_Ghd=&6oB?#w{~Yv(xYPgr|NRH*haKioowt*& z{dC`3v>70P7=Ql!$;`|Q@(GT>fdHT^FqGcDeG3pkjNS|k>E&sEfzAeo{ckY&=g(gd z`CCrwmxA8!XLtTy-uM6CzrVnM%iVPzAb=R%ks=j>et^j`MFs;Wh8c4i)_^1XKP)u> a1Q-A&>r+tW$-Nl>0000<~s!0-E7R}a|Ns2?4|9|}gs%Dgx1=;`*KrBFO{{8#+``2Fv2B6@-@8AFY{`(K80jdYk13&NH4;SYnOGgJvc0I@K9VW@Vk1}Xmk4~YK$|NH0P z-@pI<0D+Rop8&DnA}oIvEc_iD{O9-Y-=|4+U?1uFgf@9*z_zyJRJ z%fS3QR^zuR^Y0tyerNXndi(YlP_?gzto2+5fB<3vIsj-gQ0X6_*?&Q*d^>boB3^zoi8Kq^tbl{qlR!{9jwQ|Nij< z=(=A?F~8j-MSe1{Hl8Yc@zaXo%Ljk}VgUvb(2HJbe?0kqy?_3vsPXrUmq2rWE6M!I zE@4*I;Mw}vw&7&iFGdC>zW)rr{s9CK3xfnhrsi*1wqMhx{@J=6sPgxpU%z6)f4fBr zzxpd#a3tyYE8}0lKnA+|-+zWbe*glA<)R0}-=}{Pnt#7~1q_Mb;=;f3i~oshNgsS> zIr~z~(_bRL|NZ|1LO_>;A_pLVSoBpHQd9nLa7ao@{lMMx#__lBLe9Cjx?EiU z71{s)`Oon8FXP|;z>s7BItd_v7-5mj(Adb}701wVj$!S*>kNN{fN~5FV)t0eYh-DxnQ(+0I@JIfmA>F`X8wD?>`2h#NWRRzyE`zA&Pzh5tt#% z!^rUCA3y+sZ1@LM1hf`pHc0ia|NsB|1uKPX0CRu;1-SztfLMSo`wOxir1}rsY$VlC z667idh7Sw?0mQ-}a!Q0#;n&}vAb0=!_Zy@WNd5!6=O2ju7s{65Vq=gx0uVrqe;+b1 z$mIY3|KLBvF9x7j{{!v#g_UGtQhI;<6hHv607H@yECdWlR7EJN!LomVp$!l~APvkQ z4p0cL2#YobhQA>6hXEjfKpL3-19eKmL_s!=;22W8vpXP)6RGKQPvx1q)UK1Q6rb{|u}Q zRsa7#{m<~1f#L7}|GzNK{&HxZVEKJ4>jAefr9{u_M=MTegAo-i2ynN}MJ5kZm z42KRel$J95{>|{~*Z<$Y8RdVo9Ap3pAeIsa2H!t_KmPs=bot-kKnMR0GBy2s>sC=w zQFmwO;@`jj{`v(%zkl=p`BTQg01!Yd{Xp;j{qx|@pI^U!0X_En_iwRd$6{h)tX8f( zaOhC?&!0e%U%!3phy1#1%V=eK#YHXK~n$!{#(ib5I~Gyfqpu4^P)1J@ZrOUii!$=0Ad7ci1(2A^!3kwFaT->5i7Ttz54h6XL$JkA3s3o z=g)t?e>01U3h&zo5I`(I-#Hr#t~u}$WXZu-Kyv1Rm&rE5wcB1YFI@@=0EYkn8G$4- z^Xb{M0Ro8S57@^*QJ}#<$Im$M^4iKw z00a=@`Sa(sw6y;I{R{T)e~6KA2AmBHF`#D(ii!XNh(#nN}rF)(}plRy9vU;su%e@J`J?dSji002ov JPDHLkV1n4Y8}|SJ literal 0 HcmV?d00001 diff --git a/public/img/country_flags/us.png b/public/img/country_flags/us.png new file mode 100755 index 0000000000000000000000000000000000000000..10f451fe85c41c6c9a06d543a57114ae2f87ecc1 GIT binary patch literal 609 zcmV-n0-pVeP){qW|y?pud`Sa)3|NY&vWd%S0u>b>P!2!lUe;6EF*#G_c zFVXVt@6Q{uX@40W{p0iY2Aa+A^Cu7i8KT+YH}2j52q4BskM2rJ$^k9;2Xxc_|Np=M z&VaLlA*IO5FlECMfB<5VUNC{tBZO(|zW*;@GJN;|bTJ71`0*d;`d`2P!x=ymOA`2> z+y@9C##^^8%gd{MW@Y91_2d742B2~OQNf=-zkmD?Vqkdk_wPTUNeuu2#KPTG{_;O4 v7C%8E5*DLB7#Kb?Fnj}}-(W6879hX?8lYRg`Y`<~00000NkvXXu0mjfD6Jtx literal 0 HcmV?d00001 diff --git a/public/img/country_flags/uy.png b/public/img/country_flags/uy.png new file mode 100755 index 0000000000000000000000000000000000000000..31d948a067fe02d067a8c2e69f28cca446bc7c57 GIT binary patch literal 532 zcmV+v0_**WP)_vmzq~N}&z08z z0*LYY{pZr+B0$d}2MCnI@DC;m3N;oM#uMkR0R#{ugY)L9Y<*xj0QCR^`!^)W!R$Za z5CobHbl5+T3;%B|S`QFFjQ1Zt|MTw;G#Vi+hCg5i(ELAtfD|ak8UBG;ObiSF0R{lf Wla#5zB1?M!0000JMe1P}`Y1HZufM;3|NZsr4^XwNEI-g5fB<4iWtjhPj`qjL zFGT_P{{Q>;-(R370Ro7T!5$Ws$Po%5A+Zb!3j_cNFaSC{Z(fWD@s$7o002ovPDHLk FV1jsy^u+)G literal 0 HcmV?d00001 diff --git a/public/img/country_flags/va.png b/public/img/country_flags/va.png new file mode 100755 index 0000000000000000000000000000000000000000..b31eaf225d6fd770e0557c2baf8747c91ce88983 GIT binary patch literal 553 zcmV+^0@nSBP)|05Jg_ z#sD@3Z1&&(|Ni{{_Zx)%v%Gr!NnLzkb;C z@#QWhJ{xI9$+*)bNX$S?E90e{K^EMkYok78VXhR-hjM0*K`g z$j^Tmm_>oEVgxdn{xJOg$-v0L$jHRN$OsH@P9Y&+^ne@=1^@xXcy~X;zaPM$WdOPj ri2i^{AeYDB@IM9-1r;P))4B5XW#s0WPsWLbObPT z00IbN14#96puqp%TKvBf)PA*{`~CO-uM~~n%6z|m{re4Z55!3T0R+GV5`UO(`=kK4te?c|?1P~}#f$jmC10=!0@{8gBFK@Zu z-~WJALqveC|MTzfA7GIDVE_mqkOrW8BqZ2?K4D^#WMJTBVE7%b47BbKBg1c?EHl#| zW&XdQF#ikE01!Zo3=CVrargu%_x5NZPzVyc@Ms2-@*Gr#1lu5DU{kh6%sopXB}uDxAo`@Bt_a*7F+) z{Q{%kjEu5C8vp``Wse8Lf5q7h438n|z~nEOsZga5l7ZnjPy;{!05Jg0{{a91!FF~d z;^O)I`~dv>{`>s@o}L{E2?GEB|Mm6!N=n`P`~Ld+{`>p?A0G$7!M*~B3FM_eAU;q6 zNCH*w+4I@c^Ny?Q?QPpWZQiU1R1HBuWkA0H1P}`l|NYDG=gP=Ihyj(0CItil0R{kn^jdV2 S*Eqib00003_-&c2+@O*Bba;fM%rBo$$qwJnuekcf#0k*RG7MM`2+5Y7dK4k?R% z*@ue~6f;u_A~T0evP6qS=VxXnGS_mt>CSmS&gmG`kLSL)e_Vf_=c&!fKB7`4C;*C(7s2O*P zOs1$U0urzb3<=a`d9ABG4Q$eK<6~5Cg~V%B8`UqrfRSN8f&?QTVICs^VT=&p*?Eut zt4Ur&-BL_ON(Z|Xfgo93lf|gRkT$Mz-7^OQoD^XMF@}g>R?uoU0094KbXv)l?aF0E zh@GXQVr04m@05R(Rjxq*p|CCIRfv;QJmH1kf!hsqX*sha?_l;f%e5i5I~4YG#H4;6sPG2&U~bv( zNr`eMXGIX9?wP^zPQP(6+&$05Jg0{{;N}{t6CR|M}we`P%>f+X)5&j)Fc04NUj< zI`HZ2Woe@k6UF22`V|Qe2MTi(6bb?WF#yj00*Hv<{QLmnFd+P z9@fS%;@9^E|H1b5`Vk8^2@Yw;#}(}C_yUN9iGkt%y>Cxm>T2lO13mifZBc!n`jhHL z!MK>$6Z_d%|GfHMapIVnqod%T-`{|q1_&S~IR%b4Z+;63vi@iI?>jBz=eJjkjQ{>J z{QUQa@gMhBW_GSe5B=V~`@+h~@c%z3lmG&V@$K8U{QUg?|NjRX|KX9x@@rf{MxIY^ z?fG>3gQ&3ruaE-Yw>fW_PfAH~0~Or8dmA8tSRg?T(ZDLQOiOw%6RWM9%$i>kkAMHu z-oH0qLBYb*loP0ck&zJ?LI42-(g0BnBsn<*tTlsx%zxbLe*S!R<%*E3>|Z7(9Y#iG zpdch>0Ro8e`Sa&!arp;m4%k`<1H=Xjf<=G;Aix0Vb{`Xo7A9K&0000@|4`Xj5kLT%B_22*h{r-RE_y1qN|Ns8=|JN^|IHRmA&<212VgZ}|A4N4# z+WpUe-rxU^{Q|504bi~x`!`SnKmf4-F(Xhl(8fQ1fG+vp^85d{-~aW0|NsB{f9tRR zg1`PJA~XO^2M8drlm7gLxRT-bpa1p0|1bLmQVm3Azy5pw{{IQ;B%q-{(*XjA3FM@| zf53hMJK)W)|CK-uK=ku}!>|93eu7kk5yVLV0R(o^Z;+)RSAk6exg_KF|98LtFaPy_ z#c#OrKtum9FaQJ)NCPuC$bbfbRI>n`@$3IHusZAC|2cmD|MKhqAD{+Mr~sV<5I`Ue zOuzpyNJ@f@0s0=KlTj9AB*>SXe;D}wK*A7+fQkVEi190GMB<5K2mlB$03I1qT8uIj Q5C8xG07*qoM6N<$f}XY6qW}N^ literal 0 HcmV?d00001 diff --git a/public/img/country_flags/vu.png b/public/img/country_flags/vu.png new file mode 100755 index 0000000000000000000000000000000000000000..b3397bc63d718b344e604266259134e653925c9d GIT binary patch literal 604 zcmV-i0;BzjP)7(YpZ_rO%ce~L0mQ<@*ghnz= zZvXM=_iyVze?clB=ogUu_5ar|Ae&KE_8$WSKmaiTHT?Vg|MaE5OE>+VAUAUr;?j4FCZIvLVX%uQ>mocOU*NTK^}%njy93_oP3+fBpUa=kKpye|`ZO zzyE?r`Mc+5Xcyo_v6>E-#|Nl{r(LkAS94bRQz)6 z1Oq?-fi&zp{`>yDUnV+0)eJF{zWfaN_0t9DjNiZhfxY|}ME?2z=O_CghC>Vh0R+`!~0W%8&aG{{H+4 zRPp!M??1nPG5q=k)Wabw3seITKp+kO{xLwc{r>&$?_UrCGJgGL;^k4Qujka#`tk7L zjnovNAkb9+0R(gvNCCu>f4~1QF);xde}DY=^Xr#I=|`5hhp|NZyp>({@(egUPqbabS$v+tIbNu;L>xVSv%=>ggR z5I`*V_U`A@(fj`3!N1?XfHr^}17rYw{OcFcC69Z0BvMmfELgy8V88%&5l9HrLN(5b9xs8l&q^Gm-@&XkD1Q1BW|346ee?wJ+H2_`p`^l3V z+1Wtu|3AMO{`_T8Q2{Cj2q2bUARGR$NlJo(=nv2bFrDDw2U!bb{QV0-KNuJQ0*G-b s1H(6@xcrC2{sj~H2V-zBFaQJ?0G2^Lae{Q+uK)l507*qoM6N<$f;6K8u>b%7 literal 0 HcmV?d00001 diff --git a/public/img/country_flags/ws.png b/public/img/country_flags/ws.png new file mode 100755 index 0000000000000000000000000000000000000000..c16950802ea95b40a4e024be6cce870b1991f40e GIT binary patch literal 476 zcmV<20VDp2P)>E69JA<-5ee*C|6>(%ey48MQ<`3*E1sOaY}WDP*a00a=wNxxYB{9yb2 zOU?HFZD2h;3gJl2w`NO~f5I`Ue%-|qnfGfh(_6MlpFT>xzKtBKk z5J&^l?>`KZl3-*0GW-GQWCR<@03m_s?_UT4Dh3E3#;>Rm$$$~H$WlN65MTgr(_Ikn S3@&c~0000=xie?ZoIZW}({TJKY#xC z@#FjV?*|SXNJ&ZAzkmPUy?b}=+__`Nj&0kvZQi_j_wL4%P)h=Z!r1m*T3JtfNUmdS)dI70mLHE z+xy?%p5eHez0mS(A>sL-rPOy{yGk_s3 zz{tq>=+Ps90Al&~?;k`pNCN|I|RMxbJV00L=%3E(v!XamT}{{RArWx|9$7Z{pWi2U{IKN}lRCnF;x&=P&!(V3Rv&T;|GP6K500a;d#1A^U zy8js%fB*jT=jRXRU*A|5K#2L*r(ZvQ{P_O!=da%umKG4l0|XG%NkAKznV4BvSbj4y zawHV~fBoj~uiyXJSQwZYIoR0QIXM3O{tc7`Dgy{0# literal 0 HcmV?d00001 diff --git a/public/img/country_flags/za.png b/public/img/country_flags/za.png new file mode 100755 index 0000000000000000000000000000000000000000..57c58e2119f402072640ca758657798b621f3fb1 GIT binary patch literal 642 zcmV-|0)737P)Cfuwe;F7c=r;qyPX>nX3=Cfx7(O#Fd}LsF&%p4OfdL?Z7#j~W z{9>KL@b8bfkaUokt?1q(EJu$q{Qdp^&mV?AfBydlG2rBvO`8A$h{fmzgIW0J+B07s zK74uV#pQB`MAdI!SdN|k|KJ`--LK#OfB*Xb>lcvCC@cGqfdL=@05Jg0{{a910Lj6A zAt&mC*9zzO1pWQ@b1`Tn1`iV6=KuBe|Nj2}{{8>`{{Q~|0SOB6tE&PCq@k>=Oh@B| zgDu0v2b@p;u)h2C^Y4EK4rY$O|Ni{`3qc?^$?%A8m^2X}fLMSU{`~p7aqHLD@0rW| zzIWc|c>Ry-$DePXKfn0(_xG>AzksTL{re5n#{Tyw!wUw000L?F`}dEV6O*(3zu6Di z9{gne@#pKG|NpqSh1vf81DXBz&mW+Me}8zmL>PD&00Ic4!QY)xLzl7RCikV!EWdtz zoorLj&BXoYF88-DfB*gk`{Eza7yo#;S!C4G00M|*elvr*8B6X-zBgb0FtGkEHc8mM zoOScs_b*;Q0~!ksq<=swfJjA!^Ww!_00G4E&x@hvA~zQkvxU59n3VsHd7Nj?ec|R~ zkmuqAx#JHo0{#K*`TG}00$uYDAb=PdGJ(Ek5Vx?d6PS=4{E7kSFNS|$^b5xL14ayB ck_{lh0N_F{UmK66LjV8(07*qoM6N<$f>aVd=Kufz literal 0 HcmV?d00001 diff --git a/public/img/country_flags/zm.png b/public/img/country_flags/zm.png new file mode 100755 index 0000000000000000000000000000000000000000..c25b07beef894408ae11c3be294d6e0eeb28c0bb GIT binary patch literal 500 zcmVLz(k0+Rp#|Nr~{@4x^5fB>iy$Oe)U zA3pkg`SAPSuU~)vaR2(F*|!oPfLI`Azxw(Lr25~#KmY#x{rl(7|GyyW-|wp|{||Bf z=)Cuc_0O*fYWnY`7ytr@1!(pEfB(To0uBE22c#NA{{8#+_rKq)zyAFG`zKdY6euFW z2yzlY0I`6SGJqWbGV%`;{r&ytA5hcp-u`P`AAdOg`t|1D?}h2=^+y>10tjRS&?yMj zfBygt2HEouXg1LCzyJLD1w#LR1MLR}1V8|>0KE@nffPa916Bvu_V4#Epof105yNkw zzknJ50tl=DS->P)FK#X6%e&u3i|I74$@9uP0*YE!se={)r{r~?r68Z&3 zzZn^2KYiK`5I`&p3=IF7JHD>uaSofgtUAf*-w!6nU;qF7`S<(x|35&+uYZ4j|Njjl zS;T)|pU40ZKrEjoRWbQ1@P?oGFzcPCjLiCzcU8ZC{rmejNW<@6e}U*1kPT$~=Kk_~ z83O}A05QHjx|ikdd7h}_|NsB_wl9j+&d<}^`}fbEzkmP!1q4tEL@O#P9zA*#Ab?m{ zv`qe9Isj6Ah@Z*IdsTw}WYHgB8-D!*lmGvK*?)d>i2vThzyJ_HEdLn(@uk-N0|t-& z`$Yv&?#y3UfPVP*`ya#a|BS!>G5-DsFR)1c1&eHCHYDzO7ytqc0P0;>l>h9)WB>pF M07*qoM6N<$g71PE`~Uy| literal 0 HcmV?d00001 diff --git a/public/img/default_banner.png b/public/img/default_banner.png new file mode 100644 index 0000000000000000000000000000000000000000..e7cfb2150720ec724902e44c02f6dba7e719306e GIT binary patch literal 9831 zcmbVyby$>J_cn?G3W|u7q+$@GNQZ-X0;Uh3>;p51%h>t6TT0gvP*&z-$|76%9CoV1jf5)RHuHTeFK01y7< zN=Ye$|4!H|N!~y4tsUV7g;OS?a-ujmg<*sTPfkOb!9Yq$4hP5e1`f`1UmTo0c=UW0 z2gl(K4$izD4vs)94i1G)VwIvG{D7w~BPoV+jQ#VrHY)<2oUxHox5vS`&4~Sb!Z}mK z5gy_{la>?5pF2ZyhK0ySwhvljz>yXcRdya;`r)FcygA;s){7=!nmE}MN*x%;K$ZDs z`j#8!LX4EHj9ss0&X0stY@9!@)maVTx0v~1?BoX2&d|svpdW*xm!7P7& z3;12B`OmASY^Z;h@BXu#{_wkEO9QqoiN89b{L^$$APXKj^u_+K_dC+k{?q6G_4~hH z|IfRB4g9~u{ZrK+JNoAAtN(22uVQ-O?|}!i{BvOX$|S!RLs9(q66h&3_;_uo2DF(Cr ztLpz2VWs|472ELt6tNBeUG;ave;5C}`@5y|Kt=K2vzG~ANs`Bxl$7kxRV9{}m%l?v zl&x8QdvR&$iM2H`K0ZDlAK%-eqKntBUuR-vePC+J+|kitGt)zhsd6CP-QBIQpE(9rN_<4eK{+X=j1>+7#0B7A}<*wSihXzJ?fZ0zlQ zii4;**4kcf^`jz=m{@B*icTBiK@%xt-UL>UMdGQ;=D+jylQZh14<)fZKhx^+@ zPi1R$!iIF*&Mgg>K6v!#f<~FenVUCnrWcr7S=}DYQ#W|MUy00BnH?&ipGhXsu5~4o zi|0~w$3We5=mL4<o0#oOudtgsyR)`+e3{3>|L0FVB6?w()YMe&%jPpZ zNneYM2;~#`ygOsqIL%VT9z{jQ#01&T^aMn)ss_p@37oTL3_j-+EvR_2>I@uJZ5<8tO25dV0q1KA5y^Z*NaiND)qRTpA`PQ+uyb zdePR_7G*h8>$V+uo>qV$NxluOuP1cq_x+t*f^T_w`8!8UgEwx=+C-bzu}qA&k*9Y{ zrO819Dm(jvw6ruz3@Ixc47x)sG_<$h~IuXw%Kj>HKU|h%x!IDLL7tB zD$rLmY)VL3lE~4HLEl?Z+*UH&h4$sqdN#+Eky{$&R`2I%VY>}&mxj6VqNz@k&m{Al zTmXYo3cD)AjG`+y?!MeDUKSD(LU86xU$d$=l8ls;F)=Z*ivvxB=c86+7|>U~=8cOl zmaFofQcX7J*kNs4oL#?}AY%BnuIFwh7_EU`oU@+ovMD1PY^vo`>Vpt+WY}Ggy^__< z&4K2leBJswwF;Y5>H0P}sX=ewG8CUhSX*1$PJSfmwwP#ayuWs^-ph%e73t{gYyn&F z)hXa@Pud;Sv<|>vwD6+O;K8UJPv)Z0kAg3Acg!koR%~oM8=ILi=|5bYnz~+hv}a!I z(!26GGf4eZw4a~f&T{n%SGflvA))yG))JLkbbdvJT30-G=cLn98yhsA#~v4Mr|-!i zdxg8+NF}AKH)Lbq#%1rNGbVP&b8`-}ol-~^G<94#EU4Q5_hvs(^cz7jmu+SKfi>j9wGOgJGe4Ua1^rMH$0S< z?z2!9vYPISYaAZdi1PRbds}Eebi4S7h6#aCK3GuYK572xt>}&75RtuE#?ESApX+T% zPtQ66LPE|3TKA1vMZ9P{T$rP#H}cUV1|63vGC9ep%aYnhvqLX5c98LxLuvT00y4?o zK)ZW*$XQVX7{j3#wY4pDUhn4Z_QYSsvU^{C{i@ayTJ(_!ktA<&QTLqiG92y3k&$F?qaGF(mgY8kFAMg&m00^gqr<&3 zy&J|cUv%`QW@e7Z9Nv{sBEuqFq=|WXc_V6iqExUA<(Z^-3>ubOms|ZnOds}kTwJN& zDeyWZr1#uOZOL2V9d-AqsNfq3kFXE3UpcdXjgylU=jX~wSZpk1MrP&}Zf>$KSqhR1 zgYUVk7tIIryb}@Ua{ReCz3g{9>a0|Q)erIbW? zB~SZKUb%9`KQK_qGC@L2Y_(e0Es8n3CmDXpMD;jyxL6lV3mAp(_;=5>aVLN$1alX5Ehn%SrxXo`|BqhzX`Vt;~A>w=D*BBiN_cmYbZlWkGc#p* z;h8`m3dzcD(nB+#LuhJh+Bi6<^4epg=kAJ>dTXIU+gYG{5tTUe!Bm{G9B6P9H{!WW z0cR|SOLBg1Gr!!@yDg$T5&joHCngvj9<;W$vTQlLxp2&XMSzP-xua-nw(o(U^J>H7 z_5yhK42u-lBl~4ji{(S49`oVm=-ihqaag;M{D6!vb7C-ZbDBB@nE?1y31W%vVHm5cbPlgj}BZSMzRJf-?x7eydf8#@zmXJnVEC@hzV_& z^f)R!oB@NuT+$qciDFNpw3plljIXuR><(o{&9MLA>#tYw|Li3HZ57y7{5B zcOM>J$ORBDi5Sr=d78T05Ev9h>M-B0=w4lB`bA=|h}Yt4xOWR9t-{g0+e}R1b>|a! ztySE=`4Q7wz1c4Yk3&SwBinJLy!Rl%ULmehRY&Lcn}~?a}Y-ccxOQ?e#sld5##BFg8FI9mrod0HZ}@%b#?4Z|oSJ_@|u3k+0wmto;saaQEgw@P~xIsE8F-SL5ZZOwz#CgL0W z@dkR*M>gROFV-TNAB!0oWtuJg`r$OnTz9_yXfIcKskgaVjCtR#HJB0G_hRRESw=vVrLuX`&t*wVfR-F#M4Cu{lB(k+Iq|mWTo#>{( zyM2T}^&g4@9&vDRpsY;+{!cdcj%XkI`mbNI`Xa_ACI*uqUk8RQ%+Ck*3hxuS z)+XC4v{cT3s{tp$C@f6J!^5-s>sMneyFoBd{l$wHS3BA2vmIuLEWV;ehj$zu9noUo z`HQnRLxO^uraEJgF8L56Ak>R|*Gn&V382r@@G%%+hIGAX1(M`9H_s3el~gpa+`iok zD{V8~Mb*+aQesN#?(WXFtsLt{ymA;59sR1mUzvvAt~pzAGuvTdFtw;isdNU|78h=i znIXUwN?6y6i6QI2}NCXsQ<|vtN-5S^s1^{bL6A#KbXsLhNceUnM z2Wz6+#hK*#dXdJaCK5WjBN1LDM;n_@U4k+)GLJWRtw!rYepEX#a&bk{t0~`_=}l4H z-1XdF;$@x~{{Ed4E!zh;lNr1};Vqete$(f}a{y&by_fFmwaeUQ8FL;L9{Y}B z;^NH0%d2BeYtsqV5AR+yY`0e+@E#x6*Q$0rr&^%X@{*9^0yXu%$nhw^I2U_cPa?lh za4^w8iW9_GL6iTk){FU0m!_TPp-!MbFwgY`E0?_$V7W@#d0g{{sLY4o}M0vu_V{0yGbrx zGGzZg4^V&-$;v$Rf9`FRLCg;frRQ=0APor>74wv;cCKoHgpJLendFy}1L?r7Lw?mn z+lk%F`jQG>8ym1jM37$QdWu6qK~Ygz`JuCuFgZDSb$xw5#eQS9{VX9Nl^PLZ;-Q=z z(!C3acX|@8!5LTzw9VLki{TRKwZfb7iT+QTUfC|QqmE?M)#Jp43M^b}>3|1600IS| zAC@fSrqSPbottu^O&VA)W|b)K_`-cXASr=ixvhP~GG*Akm6T)^w#t%ExoWKY|@?NPB9x3_?P*FrAN`olQ&M%duKcTH4i1 z6ciH>jsi4^OG}@5C?hjAHT4n<-Tvl9Tp1oWH#ax~Ed|-P7H-r<|7;E*rQ~`V9$6T} zrW*=^LR<^RjSEFrJMswB8NL81)Y0L7I&39Ma0qyX&FW84tr};Na_iC7q`OX!w6ubY zia0P3%5}_CgD7B!4_%xSNIDPb)&`8}674p4ghcj%@TjPlB#aVLRQvDCN8a$1Y1pqw zD{eAz7f2{Xw}B`GvO?4z&0WXEd+zRnz!X}oas9oGI!>NEd5Jg=Am(je9ue@#M1F-i zn40rS?RG|D{|L~c=H}+7%O}waoZHDaP;_J$FP?-67FS8%F>QnOL0B$Ayc^ovfsnv%xgUo3~`pqx_!Yr z#ZBi5cC?f!oclM9%VZOvh#E@`vUMmue%zNSLy`V|2mqPE_mLq2T%@VwbF9XL4SuNH zKntO~qxal+CK>3zzP|pY0F>nEQ>T7buhel4yKif8X}J7zVS&{M zbLGLSY;x5a1+Y%#$+593Mi@|Zo?t3$C0s;juyOUk45(gfNBb+Rb%}9tag;n3v5}Ua z-fQy8#&MkH;Tevv3e!2T|C}KW;$Y}_FS~IUB|^83282LWkHf^IBwJ8aTUvvHgPVaR zbvWsrd-}cXeWWeyZu+nQ<^GA3EoSa5(+;HW$oq{8N&B(iO`NDtx#d`#?j(JqX1bnHEqRL*o!!xo;0N;y;ff+XEGA zr-IX@M+_n%vxU@`=08c)W%W+n13r)$jMdZ9^Ll~slF=5xIS#~cdvTgDBdI?*GSU|i z$BYA%86e%vy=?14I$qOpCdrD=YQR<2?VCNXt*uSdH4uqRPR^fq{U~r&vbn+ar25eL zcpl5k5a}sv$9(>*)ZEg-Eby+lxHxUX@<$aJJ3G75pa>5SImC?dp(x4rDq{`sjpO@E z1)#s`HTs?j49(eE@H>hHX)Mxqpr!};qsoCDV#sA#eZ8Fa-dH z5gjp|U<2r-Wb~>z^d}*t1V#LQM-|wQ!O_8PpjQ$zBZ>~GrG;3!whTn#;XX;*1Vqg2 zj*Dt7ZODbv2VQF@80NyreLlSH_;pu6;Ne{}`R0Z_!lIt0j_z)8Ev-cT7}*_8x9#PY zSLdiPh zR8B_k1S`W`H>ChFw(#M`V~ZbE$c`#D?W#*`wHt4w#Rt;ijAZT%RS|ove!!poQC;3& zIWspG0FFd!&30m9!ljcwq{3+>Inov)(_nCOQrcS}NMrQ|Oh9u32RSU0Y&@6r@DBKZ z7Oej}hCBh5sua!7#FQ;@d;}KS2$EqYLQ_wV!MHd1{YMg>()4*- z*oY{BBe2B2LIX+hwmj7WI(-8J5_0kzMsbgoly-V@Nq~)ft#K)iEM#P4WY=rJ5wB+{ zDJyF=FQ|Q$E*WMwLxL-V%>t0sH-AXCv$yZdd3@7-b3ipv%V-~R0F)et1a&*(gv_0H zv4LNbSJdm*-n=8u4~Dmb!@~TktE<}qTkRjr$KcwsSh6tE+1P+Qe|T z*GzXp+j;@l4Ss(0;jg+Ln`9Efl&K#J&VrT_3~H?&xJitu;YACCkvQZM*v$tKIol%C zg3fpFV3%?pXI*7rz!@AIMDA^a)YS-cWq;Cm%G7V^gx=2v?}4LoadB~_g8t=+2_K+F z@CX-qJ{PhF$7i*!7lhbDfW3&REY>)71g7R68XE05Dv5)Y;L9w&y#TAb3PKospmjW- z6A)?o2L(m!ML4Nbq-M@Xc$fqZgSY(O{@?HK~II+E+wc#oR3ote}hfS?uYTd9AG|2If58Wf3Mm@?qBnRn)Fe3KT zvgu1AUS3`xLNN*oYIQ6L8TR@Cild z1#2$O$WrIEaTa=Y#$Dj`>`u#CvPlBSJ$r{`jQVT(uNUG@2A$0laF}P{;ZfPlMN^QG zNmuS#BgG{pRreokf{2EtSX9UXWneX6Q#qbwj!w*y zyc<|!^xnipR3+qg3`S~PZd5K3k|P?x2rEegyfaS&SBm8{32jLvM0*~(no?vKDkJ68 z)v1Jce&PmIel>=zD+J4s@h4%-peN%h14ULY(M?Jr3(|cc5Xacf5U9U8Pa6nGZHvVU zb2x?J;D5<)X~bG;!9q8+wpP>|=_wx;$i&=Eby^wuTp()LRT0l^P7Y$%?rrn?V2pR{ zCZ!_O1_3$sdXt4PrZ5^75fPEa;Zm8+l)O%IGP0Mmv$K~+tzIc!f4#lbn9g1rmS z3l{pR+*-GI-{A+aPAoW#eEKFv%V%@1Z~u7*TEFFNR|0QX%pn6_^kAvQw~N(|OZShD z4!iX|rXdmlb$DPRP!Z_>BHirw@`7)eBSsF6i^V3r##(WAuACao*UtT>TLak?a*$E5 z)Y^&MH2xg#2QNq|KiQlg07`T#Ej?Y%juuWkNThv0uf(mCDMX8*`WMzt_( ziWeC8D=^DAO#8~=-ewcv-KSbNr#ageMq~#o0s(?4qRMH-%I;?U3Z|0O@_SheD6^ER z#u*TnfNU=c0wuTY_&&%}7pSP(E3V>2w_9CcWqr0Vc&;sso;M)<%*e=yl&ows@Q6%_ zE9noD((&McNkWE4nnt|4tE>6>3EVqCN{WxgGp^sYnOr#Z`i2QFDB>@4VJRVG9umGI@P$N_KB+2`jNKpMqpj z!r*&NgSGJ%Y}y!bF}O|;CW2kee67kCA3sV}R8&X>px~f?{ra^5LV7rTQ(|{sK(_7l zO5OhL;S$sIfXbM-xBy6UrbA!NC5;lguhXbkI!Vavk9!`}{bz+Df$*-xpn z{W+u?uC?QeQGaPCdO&PDkwF!?PfG;1vbzKNmDXv5hf;rrdQ`4KzgsQ$VdhfA`)bW zbO%5_LHzjr6if>o2`i5$G|yx4^*Jm)5N)y@XkG2u{wt|VmL;kE{> z-#OrZ42+DpyjGZdIy!V592}66B4%S_gG59+Fo$d)Fc5|jfWiaG+$Oy{Z{Z;%9*mXy z9-oeZ-u?XWu~!zr={Pw#ftZsDE)ujNKe&>B&9#@5a2XmIU14J*2B#?@DtZEX2!}HO zT;nH93IsMDwx3dBaOXmTjD+NM*(wbJ`vt;%xU&LxIR1JLcUmZsu({0*a4Q8qe~Lti z{}i$BfAVnF}ZXUCq%uh%G?mIs|N RaFYv1T3lW%|GwUf{|8Kv98~}S literal 0 HcmV?d00001 diff --git a/public/img/icons/bumplock.png b/public/img/icons/bumplock.png new file mode 100755 index 0000000000000000000000000000000000000000..2c4e279377bf348f9cf53894e76bb673ccf067bd GIT binary patch literal 379 zcmV->0fhdEP)RB*?~^j!LKVQ>(O&A{Xr%)RXLn#U zs4LtZ6rCMFY5|B2$)yG$6aaIFag2M8cWWCNBiU1|W- zyiQ6=N|BF`kAaDai2=xG`0?Wh!|T_t8SVoC(4e5Qva$;>Jpcg&Hvp)a2Pk=7Sy@@- z*RNj;j~_o~`1)zy6m@c{ye31$*dFjG=eQswvW z-wd~I-MaVr^JfJhn+=3P?3*`lJ_8vHbe#&&D>*QI00G1TV*mv$xwyC(?%uu20Mz7@ zlauoh$*T{Wo10x8K74phK|ukm7A64@K*$D&g8U4$4aC0BfCfNfAlHG^09`8z69)(& za2Px~lJx)hU3G?+A34E(_R~JY!2JI=!~eg3823wDIf1k%1H(T?1_l-mkR&jGelz_2^O>Q^^(#>PH(1M^ z7019dKmdUa0NDgm_~*?k1{QV^21Z66ph8v#22KH>@IMBIZyy={e!9)@>-$rNe}BF) za4P07{Qvb6Yydz2K@0#ok>MZ6re6#nuAT+@lbb<6%ZlOgj&7iSF$Ot1SB4Kat^y5s z&%mo72z2mYhX22Rfeio%AgBSq!QS`}bgF>58UquX2m=G-KZd_Q|1w;D!^t4W@QXn} zS&`w#S9YM+fF(8)Og=00bBSa`!iy8(2_#ButL^3%VaH2WCpD^U)OZxp@C)2#hU)y+@T%ZNzJigNk%37 zz-WYJwT%teVfiEI+B*@v4ey@58(ld4VY_&5-ox`e@AKg+0U-I`y79bmuw_~y6+4rZ zBG5EdFDS+@M0OSE`>d7SUDOzKZ&h*4eB1iX7tOd9RiYtW2mQ--bUahxr1`i{RG@dM zL#}_X=DDO1{;UI$pFu=dLYT_=5d8WC-sLfjr7UO-HKMAwa=!>)kEhvuwre zuW3yF@ZxFCkI*+ad|5kOX%5zu8IQjhan)UqgSrFGA_0nQFn@Z08DSEUToCSz4Z1ls z&fDbq$T&7|6iq$_uDI$@q1_kQ@dfqk*0>{SDL6V)94@)ete)j++*>bIc9sj}Y;R1o z#OpH+Yt-^4wfv{nern^iVag8cqJz)ZpL`QswZ|oA zOU3F({X1SPKk~x6XuEsv=8XAw)8^f6+4yAPz9+8P8=W)OPu+UIb^XJ*={IAh-K<&l zaL$g04k_y_qL(?PteLvye)pzF(NnI6PrMd7;adK@Tb|h)ymQu1+Hhz2k(WyjJzsq2 zdCS`S(UY$R^H=O_U9Zd{AvPgH+2>Qg{FGCIEF}EPEKfGc2i>$IdJCCsWznp=MJ7c z+N*Qm@ag0F3LOu)IlDW$I=wxbEQ5lAE*Whq`ozW6<)yH1Q`f3lyV{mb+t$~|*Rg)} z^8AF1l$@k2w##{WiJ7Un$=U4&7w#Rrc=F~^QN;((-aUNu@+t50-#>o+WM^SJ`17FR zLLQ#Phk^!mDIq2uU5y`;A3qATojIZ76gz``gmBZ9{g>N-4pc31jVMV;EJ?LWE=mPb z3`Pb<#<~U;x&|g8hNf1==2j-A+6D$z1_nnZrhGurkei>9nO2Eg!xmQ^AD{*exD6$l jxv9k^iMa*ndMvCA%^;R!2Rl6n>S6G7^>bP0l+XkKPgggY literal 0 HcmV?d00001 diff --git a/public/img/icons/unlisted.png b/public/img/icons/unlisted.png new file mode 100755 index 0000000000000000000000000000000000000000..523575306d53eaf176b4f9b260f0d36852565596 GIT binary patch literal 657 zcmV;C0&e|@P)>>Oks2`oIPl8XEc) z7Z-oHdiCnNh=_=7-rnASQd3j^Pnt9-52PSJKmR|74-yB-uUN6-AxJ$)AIt?^O-)VT zCr_T74pjKb-rk;L@7}!}=gytu04X?h=n%)+wQD(m;%Y$oCm?ko^&ov<19WwDGt<-4 z-%p!1tt2QYXs4s2<1aTixBr2Gf&W1YfV4h{4-yB-gVcf4gYO zty=YV-MV%Er%#{$zp$_nY(Pat1(**K2g!rffz*TafelboQ)31?&%n#e>wZc~%9rfy z?C(JQGdw&z4x}IUyRt?v{0wJ6^R0W@A*Z8= z&I?WjSJx&^y7yBz?2M*SZ18QRLbAs(#0rsh%4F{kwbCf;j=k00000NkvXXu0mjf-HSTy literal 0 HcmV?d00001 diff --git a/public/img/logo.png b/public/img/logo.png new file mode 100755 index 0000000000000000000000000000000000000000..4fa86ea8d92584fb13b708ac7a0f0a544d1d7b4c GIT binary patch literal 26829 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 w`)khr{mdKI;Vst01PiZC;$Ke literal 0 HcmV?d00001 diff --git a/scripts/create_admin.ts b/scripts/create_admin.ts new file mode 100755 index 0000000..77c7fe8 --- /dev/null +++ b/scripts/create_admin.ts @@ -0,0 +1,36 @@ +import { loadConfig } from "../src/config"; +import { initDb, postgres } from "../src/db"; +import { createUser } from "../src/db/user"; +import bcrypt from "bcrypt"; +import { read } from "read"; + +(async () => { + const config = await loadConfig(); + + await initDb(config); + + const username = await read({ + prompt: "Username: ", + }); + + const password = await read({ + prompt: "Password: ", + replace: "*", + silent: true, + }); + + const hashedPassword = await bcrypt.hash(password, 10); + + await createUser( + username, + hashedPassword, + config.roles["admin"].rank, + config.roles["admin"].capcode, + config.roles["admin"].permissions, + true, + ); + + console.info(`Created new admin user '${username}'.`); + + await postgres.end(); +})(); diff --git a/scripts/migrations/01_schema.sql b/scripts/migrations/01_schema.sql new file mode 100755 index 0000000..04b9532 --- /dev/null +++ b/scripts/migrations/01_schema.sql @@ -0,0 +1,76 @@ +CREATE TABLE users ( + username VARCHAR(32) NOT NULL PRIMARY KEY, + password VARCHAR(64) NOT NULL, + rank INT NOT NULL, + capcode VARCHAR(72) NOT NULL, + permissions JSONB NOT NULL, + created TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE boards ( + id VARCHAR(16) NOT NULL PRIMARY KEY, + name VARCHAR(32) NOT NULL, + description VARCHAR(128) NOT NULL, + config JSONB NOT NULL, + created_by VARCHAR(32) NOT NULL, + created TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE posts ( + board VARCHAR(16) NOT NULL REFERENCES boards (id), + id BIGINT NOT NULL, + thread BIGINT DEFAULT NULL, + subject VARCHAR(256) DEFAULT NULL, + name VARCHAR(32) NOT NULL, + user_id VARCHAR(6) NOT NULL, + tripcode VARCHAR(12) DEFAULT NULL, + capcode VARCHAR(72) DEFAULT NULL, + email VARCHAR(256) DEFAULT NULL, + content TEXT NOT NULL, + content_unformatted TEXT NOT NULL, + files JSONB NOT NULL, + password VARCHAR(64) NOT NULL, + ip INET NOT NULL, + bumps INT NOT NULL DEFAULT 0, + replies INT NOT NULL DEFAULT 0, + sticky INT NOT NULL DEFAULT 0, + locked BOOLEAN NOT NULL DEFAULT false, + bumplocked BOOLEAN NOT NULL DEFAULT false, + looped BOOLEAN NOT NULL DEFAULT false, + reported TIMESTAMPTZ DEFAULT NULL, + reports JSONB NOT NULL DEFAULT '[]'::jsonb, + bumped TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, + created TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, + -- Optional fields, non-core functionality + metadata JSONB NOT NULL DEFAULT '{}'::jsonb, + UNIQUE (board, id), + PRIMARY KEY (board, id) +); + +CREATE TABLE bans ( + id SERIAL NOT NULL PRIMARY KEY, + ip_range INET NOT NULL, + reason TEXT NOT NULL, + board VARCHAR(16) DEFAULT NULL REFERENCES boards (id), + appealable BOOLEAN NOT NULL, + appeal TEXT DEFAULT NULL, + expires TIMESTAMPTZ DEFAULT NULL, + created_by VARCHAR(32) DEFAULT NULL, + created TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE banners ( + id SERIAL NOT NULL PRIMARY KEY, + banner JSONB NOT NULL, + created_by VARCHAR(32) DEFAULT NULL, + created TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE news ( + id SERIAL NOT NULL PRIMARY KEY, + subject VARCHAR(256) NOT NULL, + content TEXT NOT NULL, + content_unformatted TEXT NOT NULL, + created_by VARCHAR(32) DEFAULT NULL, + created TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..eae8f8795f708915ea49ec05d076228c613c384e GIT binary patch literal 6148 zcmeHK%}&BV5S|5;A~E5h3CB&mk_Z||h?fTO1+3A78l*zBv2+P7av&r<>kIiLK94iI zTM_~uyci=h$;`JqKi$mNX15CfqB9*d04f0Bpc3XvXf_DNN!KK2J%mC(LqP;X)CA(S zY0!-*~k2187_y9uI!nswAP8pOKRGXbaMlzNrw zeBNj`YO-Seu2O9c)qX13pa@fnXi9~>VhB@?erfYui>X0V4nnVt&#@~DdqWX=b@WS}4#G9amKk6M zRvE~fX@$=Ji|_CMt3^Cx2AF|=#egVuy>16vvS;gBb9B~9)CW`&%1aF{OK_l%V$7wZ dxQeO;{gMWVuEo?KdQkXBK-0j68TeHOz5pMGP8|RM literal 0 HcmV?d00001 diff --git a/src/cache.ts b/src/cache.ts new file mode 100755 index 0000000..5f0676d --- /dev/null +++ b/src/cache.ts @@ -0,0 +1,89 @@ +import { Config } from "./config"; +import { postgres } from "./db"; +import { User, Banner, Board, Post } from "./db/schema"; +import { createHash } from "crypto"; +import Valkey from "iovalkey"; +import SuperJSON from "superjson"; + +let valkey: Valkey; + +const initCache = async (config: Config) => { + try { + valkey = new Valkey(config.site.valkey); + } catch (err) { + console.error(`Připojení k Valkey selhalo: ${err}`); + process.exitCode = 1; + return; + } + + valkey.on("error", (err) => { + console.error(`Chyba ve Valkey: ${err}`); + }); + + await populateCache(config); +}; + +const populateCache = async (config: Config) => { + await valkey.flushdb(); + await valkey.set("config", JSON.stringify(config)); + + // Fetch data + const users: User[] = (await postgres.query("SELECT * FROM users")).rows; + + const boards: Board[] = ( + await postgres.query("SELECT * FROM boards ORDER BY id ASC") + ).rows; + + const banners: Banner[] = ( + await postgres.query("SELECT * FROM banners ORDER BY id ASC") + ).rows; + + // Cache data + for (const user of users) { + await valkey.hset("users", { + [user.username]: SuperJSON.stringify(user), + }); + } + + for (const board of boards) { + await valkey.hset("boards", { [board.id]: SuperJSON.stringify(board) }); + + if (!(board.config.unlisted || board.config.private)) { + await valkey.rpush("boards:listed", board.id); + } + + // CURRENT aggregates. This will be used to calculate the number of pages and PPH, TPH, PPD, TPD... + + const boardPosts: Post[] = ( + await postgres.query("SELECT * FROM posts WHERE board = $1", [board.id]) + ).rows; + + for (const post of boardPosts) { + const timestamp = post.created.getTime(); + const cacheId = `${board.id}/${post.id}`; + + await valkey.zadd(`posts:total`, timestamp, cacheId); + await valkey.zadd(`posts:board:${board.id}`, timestamp, cacheId); + await valkey.sadd("posters:total", post.ip); + await valkey.sadd(`posters:board:${board.id}`, post.ip); + + const hash = createHash("sha256") + .update(post.content_unformatted) + .digest("hex"); + + await valkey.zadd(`posts:ip:${post.ip}`, timestamp, cacheId); + await valkey.zadd(`posts:content:${hash}`, timestamp, cacheId); + + if (!post.thread) { + await valkey.zadd("threads:total", timestamp, cacheId); + await valkey.zadd(`threads:board:${board.id}`, timestamp, cacheId); + } + } + } + + for (const banner of banners) { + await valkey.zadd("banners", banner.id, SuperJSON.stringify(banner)); + } +}; + +export { valkey, initCache }; diff --git a/src/components/board_list.tsx b/src/components/board_list.tsx new file mode 100755 index 0000000..5ef81b7 --- /dev/null +++ b/src/components/board_list.tsx @@ -0,0 +1,21 @@ +import { TemplateCtx } from "../ctx"; +import { PropsWithChildren } from "@kitajs/html"; + +const BoardLinks = ({ ctx }: PropsWithChildren<{ ctx: TemplateCtx }>) => { + return ( + <> + [ + {ctx.listedBoards.map((board, index) => ( + <> + + {board} + + {index === ctx.listedBoards.length - 1 ? "" : " / "} + + ))} + ] + + ); +}; + +export default BoardLinks; diff --git a/src/components/capcode.tsx b/src/components/capcode.tsx new file mode 100755 index 0000000..acfdda0 --- /dev/null +++ b/src/components/capcode.tsx @@ -0,0 +1,20 @@ +import { parseCapcode } from "../db/schema"; +import { PropsWithChildren } from "@kitajs/html"; + +const Capcode = ({ capcode }: PropsWithChildren<{ capcode: string }>) => { + const capcodeObj = parseCapcode(capcode); + + return ( + + ## {capcodeObj.text} + {capcodeObj.icon !== null && ( + + )} + + ); +}; + +export default Capcode; diff --git a/src/components/datetime.tsx b/src/components/datetime.tsx new file mode 100755 index 0000000..b2e83be --- /dev/null +++ b/src/components/datetime.tsx @@ -0,0 +1,12 @@ +import { PropsWithChildren } from "@kitajs/html"; + +const Datetime = ({ date }: PropsWithChildren<{ date: Date | null }>) => + date ? ( + + ) : ( + Nikdy + ); + +export default Datetime; diff --git a/src/components/form.tsx b/src/components/form.tsx new file mode 100644 index 0000000..8bb7338 --- /dev/null +++ b/src/components/form.tsx @@ -0,0 +1,48 @@ +import { PropsWithChildren } from "@kitajs/html"; + +const Form = ({ + submit, + action, + center = false, + disabled = false, + children, +}: PropsWithChildren<{ + submit: string; + action: string; + disabled?: boolean; + center?: boolean; +}>) => { + return ( + + + {children} + {disabled ? ( + "" + ) : ( + + + + )} + +
+ +
+ ); +}; + +const FormField = ({ + label, + wrapper = false, + children, +}: PropsWithChildren<{ label: string; wrapper?: boolean }>) => { + return ( + + {label} + {children} + + ); +}; + +export { Form, FormField }; diff --git a/src/components/mod_nav.tsx b/src/components/mod_nav.tsx new file mode 100755 index 0000000..12777a9 --- /dev/null +++ b/src/components/mod_nav.tsx @@ -0,0 +1,43 @@ +import { TemplateCtx } from "../ctx"; +import { canUser, CzchanPerm } from "../permissions"; +import { PropsWithChildren } from "@kitajs/html"; + +const ModNav = ({ ctx }: PropsWithChildren<{ ctx: TemplateCtx }>) => { + return ( +

+ ); +}; + +export default ModNav; diff --git a/src/components/page.tsx b/src/components/page.tsx new file mode 100755 index 0000000..15472fa --- /dev/null +++ b/src/components/page.tsx @@ -0,0 +1,12 @@ +import { type PropsWithChildren } from "@kitajs/html"; + +const Page = ({ children }: PropsWithChildren) => { + return ( + <> + {""} + {children} + + ); +}; + +export default Page; diff --git a/src/components/page_body.tsx b/src/components/page_body.tsx new file mode 100755 index 0000000..05815aa --- /dev/null +++ b/src/components/page_body.tsx @@ -0,0 +1,54 @@ +import pjson from "../../package.json"; +import { TemplateCtx } from "../ctx"; +import BoardLinks from "./board_list"; +import { type PropsWithChildren } from "@kitajs/html"; + +const PageBody = ({ + ctx, + container, + children, +}: PropsWithChildren<{ ctx: TemplateCtx; container?: boolean }>) => { + return ( + +
+
+ +
+
+ {container ?
{children}
: children} +
+
+ +
+
+ + ); +}; + +export default PageBody; diff --git a/src/components/page_head.tsx b/src/components/page_head.tsx new file mode 100755 index 0000000..123465e --- /dev/null +++ b/src/components/page_head.tsx @@ -0,0 +1,30 @@ +import { TemplateCtx } from "../ctx"; +import { type PropsWithChildren } from "@kitajs/html"; + +const PageHead = ({ + ctx, + title, + description, + children, +}: PropsWithChildren<{ + ctx: TemplateCtx; + title: string; + description: string; +}>) => { + return ( + + + + {title} + + + + {children} + + ); +}; + +export default PageHead; diff --git a/src/config.ts b/src/config.ts new file mode 100755 index 0000000..0745350 --- /dev/null +++ b/src/config.ts @@ -0,0 +1,99 @@ +import { valkey } from "./cache"; +import { BoardConfig } from "./db/schema"; +import { CzchanError } from "./error"; +import { CzchanPerm } from "./permissions"; +import { readFile, writeFile } from "fs/promises"; + +type Config = { + site: { + server: { + port: number; + }; + postgres: string; + valkey: string; + secrets: { + jwt: string; + }; + globalTheme: string; + }; + boardDefaults: BoardConfig; + roles: { + [key: string]: { + rank: number; + capcode: string; + permissions: CzchanPerm[]; + }; + }; + // We use asset maps so we can pick appropriate resources (sometimes at random) from the backend without unneccesary fs reads. + // As opposed to banners, which are stored in the database, these assets are typically only managed by the site admin. + // A dedicated permission and UI to manage these assets MAY be added in the future. + // BANNERS ARE SOMETHING COMPLETELY DIFFERENT, TRUST ME CHUD + assets: { + capcodeIcons: string[]; + banImages: string[]; + // By status code + errorImages: { [key: string]: string[] }; + memeFlags: string[]; + }; + // Will be used for localization in the future. Currently used for assigning names to things we have a lot of. + locale: { + countries: { [key: string]: string }; + permissions: { [key: string]: string }; + memeFlags: { [key: string]: string }; + }; +}; + +const jsonConf = async (file: string) => { + const buffer = await readFile(file); + const json = buffer.toString(); + const config = JSON.parse(json); + + return config; +}; + +const loadConfig = async (): Promise => { + return { + site: await jsonConf("./config/site.json"), + boardDefaults: await jsonConf("./config/board_defaults.json"), + roles: await jsonConf("./config/roles.json"), + assets: { + capcodeIcons: await jsonConf("./config/assets/capcode_icons.json"), + banImages: await jsonConf("./config/assets/ban_images.json"), + errorImages: await jsonConf("./config/assets/error_images.json"), + memeFlags: await jsonConf("./config/assets/meme_flags.json"), + }, + locale: { + countries: await jsonConf("./config/assets/meme_flags.json"), + permissions: await jsonConf("./config/locale/permissions.json"), + memeFlags: await jsonConf("./config/locale/meme_flags.json"), + }, + }; +}; + +const getConfig = async (): Promise => { + const json = await valkey.get("config"); + + if (!json) { + throw new CzchanError("Konfigurace není načtená", 500); + } + + return JSON.parse(json); +}; + +const setConfig = async (config: Config) => { + await valkey.set("config", JSON.stringify(config)); + + await writeFile("../config/site.json", JSON.stringify(config.site, null, 2)); + await writeFile( + "../config/board_defaults.json", + JSON.stringify(config.boardDefaults, null, 2), + ); + await writeFile( + "../config/roles.json", + JSON.stringify(config.roles, null, 2), + ); + + // Asset maps and locales don't have a web UI yet +}; + +export { Config, loadConfig, getConfig, setConfig }; diff --git a/src/ctx.ts b/src/ctx.ts new file mode 100755 index 0000000..7fc02e6 --- /dev/null +++ b/src/ctx.ts @@ -0,0 +1,39 @@ +import { valkey } from "./cache"; +import { Config } from "./config"; +import { User } from "./db/schema"; +import { readUser } from "./db/user"; +import { Request } from "express"; +import jwt from "jsonwebtoken"; + +// Common object with session data n' sheeit +type TemplateCtx = { + user: User | null; + listedBoards: string[]; + config: Config; +}; + +const getCtx = async ( + req: Request, + attemptAuth: boolean = true, +): Promise => { + const token = req.cookies.auth; + const config = req.config; + + let user = req.user || null; + + if (token && attemptAuth && !user) { + try { + const payload = jwt.verify(token, config.site.secrets.jwt); + const username = payload["sub"] as string; + user = await readUser(username); + } catch { + user = null; + } + } + + const listedBoards = await valkey.lrange("boards:listed", 0, -1); + + return { user, listedBoards, config }; +}; + +export { getCtx, TemplateCtx }; diff --git a/src/db.ts b/src/db.ts new file mode 100755 index 0000000..c783a43 --- /dev/null +++ b/src/db.ts @@ -0,0 +1,25 @@ +import { Config } from "./config"; +import { Client as PgClient } from "pg"; +import { migrate } from "postgres-migrations"; + +let postgres: PgClient; + +const initDb = async (config: Config) => { + postgres = new PgClient({ connectionString: config.site.postgres }); + + try { + await postgres.connect(); + } catch (err) { + console.error(`Připojení k PostgreSQL selhalo: ${err}`); + process.exitCode = 1; + return; + } + + await migrate({ client: postgres }, "./scripts/migrations"); + + postgres.on("error", (err) => { + console.error(`Chyba v PostgreSQL: ${err}`); + }); +}; + +export { postgres, initDb }; diff --git a/src/db/ban.ts b/src/db/ban.ts new file mode 100755 index 0000000..c157eb1 --- /dev/null +++ b/src/db/ban.ts @@ -0,0 +1,54 @@ +import { postgres } from "../db"; +import { Ban } from "./schema"; + +// Creates a ban (bans an IP) +const createBan = async ( + ip_range: string, + board: string | null, + issued_by: string, + appealable: boolean, + expires: Date | null, +) => { + await postgres.query( + "INSERT INTO bans (ip_range, board, issued_by, appealable, expires) VALUES ($1::inet, $2, $3, $4, $5)", + [ip_range, board, issued_by, appealable, expires], + ); +}; + +// "Reads" a ban (checks if an IP is banned) +const readBan = async (ip: string, board: string): Promise => { + const result = await postgres.query( + "SELECT * FROM bans WHERE expired > CURRENT_TIMESTAMP AND ip_range >> $1::inet AND (board = $2 OR board IS NULL)", + [ip, board], + ); + + if (result.rowCount == 0) { + return null; + } else { + return result.rows[0]; + } +}; + +// Reads all bans +const readBans = async (): Promise => { + const result = await postgres.query( + "SELECT * FROM bans ORDER BY created DESC", + ); + + return result.rows; +}; + +// Appeals a ban +const updateBanAppeal = async (id: number, appeal: string) => { + await postgres.query("UPDATE bans SET appeal = $1 WHERE id = $2", [ + id, + appeal, + ]); +}; + +// Deletes a ban (unbans an IP) +const deleteBan = async (id: number) => { + await postgres.query("DELETE FROM bans WHERE id = $1", [id]); +}; + +export { createBan, readBan, readBans, updateBanAppeal, deleteBan }; diff --git a/src/db/banner.ts b/src/db/banner.ts new file mode 100755 index 0000000..c814dac --- /dev/null +++ b/src/db/banner.ts @@ -0,0 +1,44 @@ +import { valkey } from "../cache"; +import { postgres } from "../db"; +import { Banner, File } from "./schema"; +import { QueryResult } from "pg"; +import SuperJSON from "superjson"; + +// Adds a banner +const createBanner = async (file: File) => { + const result: QueryResult = await postgres.query( + "INSERT INTO banners (banner) VALUES ($1) RETURNING *", + [file], + ); + + const banner = result.rows[0]; + + await valkey.zadd("banners", banner.id, SuperJSON.stringify(banner)); +}; + +// Reads all banners +const readBanners = async (): Promise => { + const banners = (await valkey.zrange("banners", "0", "-1")).map( + (json) => SuperJSON.parse(json) as Banner, + ); + + return banners; +}; + +// Reads a random banner +const readRanomBanner = async (): Promise => { + const banner = await valkey.zrandmember("banners"); + + if (!banner) { + return null; + } + + return SuperJSON.parse(banner); +}; + +const deleteBanner = async (id: number) => { + await postgres.query("DELETE FROM banners WHERE id = $1", [id]); + await valkey.zremrangebyscore("banners", id, id); +}; + +export { createBanner, readBanners, readRanomBanner, deleteBanner }; diff --git a/src/db/board.ts b/src/db/board.ts new file mode 100755 index 0000000..f0f5650 --- /dev/null +++ b/src/db/board.ts @@ -0,0 +1,151 @@ +import { valkey } from "../cache"; +import { Config } from "../config"; +import { postgres } from "../db"; +import { CzchanError } from "../error"; +import { deletePost, readAllBoardThreads } from "./post"; +import { Board, BoardConfig } from "./schema"; +import { QueryResult } from "pg"; +import SuperJSON from "superjson"; + +// Creates a board +const createBoard = async ( + config: Config, + id: string, + name: string, + description: string, + setup: boolean = false, +) => { + const result: QueryResult = await postgres.query( + "INSERT INTO boards (id, name, description, config) VALUES ($1, $2, $3, $4) RETURNING *", + [id, name, description, config.boardDefaults], + ); + + // Sequence for post IDs + // I know what I'm doing. Okay, chud? + await postgres.query(`CREATE SEQUENCE posts_${id} AS BIGINT`); + + if (!setup) { + const board = result.rows[0]; + + await valkey.hset("boards", { [board.id]: SuperJSON.stringify(board) }); + + if (!(config.boardDefaults.unlisted || config.boardDefaults.private)) { + await valkey.rpush("boards:listed", board.id); + await valkey.sort("boards:listed", "ALPHA", "ASC"); + } + } +}; + +// Reads a board +const readBoard = async (id: string): Promise => { + const result = await valkey.hget("boards", id); + + if (!result) { + throw new CzchanError(`Nástěnka /${id}/ neexistuje.`, 404); + } + + return SuperJSON.parse(result); +}; + +// Lists all boards +const readBoards = async (): Promise => { + const boards = (await valkey.hvals("boards")) + .map((json) => SuperJSON.parse(json) as Board) + // Alphabetically by ID + .sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0)); + + return boards; +}; + +// Gets the next post's ID +const readNextId = async (board: string): Promise => { + const result: QueryResult<{ id: number }> = await postgres.query( + `SELECT id FROM nextval(posts_${board}) AS id`, + ); + + const id = result.rows[0].id; + + return id; +}; + +// Updates the board's name +const updateBoardName = async (id: string, name: string) => { + const result: QueryResult = await postgres.query( + "UPDATE boards SET name = $1 WHERE id = $2 RETURNING *", + [name, id], + ); + + const board = result.rows[0]; + + await valkey.hset("boards", id, SuperJSON.stringify(board)); +}; + +// Updates the board's description +const updateBoardDescription = async (id: string, description: string) => { + const result: QueryResult = await postgres.query( + "UPDATE boards SET description = $1 WHERE id = $2 RETURNING *", + [description, id], + ); + + const board = result.rows[0]; + + await valkey.hset("boards", id, SuperJSON.stringify(board)); +}; + +// Updates the board's config +const updateBoardConfig = async (id: string, config: BoardConfig) => { + const result: QueryResult = await postgres.query( + "UPDATE boards SET config = $1 WHERE id = $2 RETURNING *", + [config, id], + ); + + const board = result.rows[0]; + + await valkey.hset("boards", id, SuperJSON.stringify(board)); + await valkey.lrem("boards:listed", 1, id); + + if (!(config.unlisted || config.private)) { + await valkey.rpush("boards:listed", id); + await valkey.sort("boards:listed", "ALPHA", "ASC"); + } +}; + +// Deletes a board +const deleteBoard = async (id: string) => { + // Delete posts thread by thread, reply by reply, like a heckin' good boy + const posts = await readAllBoardThreads(id); + + for (const post of posts) { + await deletePost(post); + } + + // Purge bans + await postgres.query("DELETE FROM bans WHERE board = $1", [id]); + + // Delete cross-quotes of the board + const quote = `>>>/${id}/`; + await postgres.query("UPDATE posts SET content = replace(content, $1, $2)", [ + quote, + deadQuote, + ]); + + // Delete the board and sequence + await postgres.query("DELETE FROM boards WHERE id = $1", [id]); + await postgres.query(`DROP SEQUENCE posts_${id}`); + + await valkey.hdel("boards", id); + await valkey.del(`posts:board:${id}`); + await valkey.del(`threads:board:${id}`); +}; + +export { + createBoard, + readBoard, + readBoards, + readNextId, + updateBoardName, + updateBoardDescription, + updateBoardConfig, + deleteBoard, +}; diff --git a/src/db/news.ts b/src/db/news.ts new file mode 100755 index 0000000..11d4068 --- /dev/null +++ b/src/db/news.ts @@ -0,0 +1,43 @@ +import { postgres } from "../db"; +import { News } from "./schema"; + +// Adds news +const createNews = async ( + title: string, + content: string, + content_unformatted: string, + author: string, +) => { + await postgres.query( + "INSERT INTO news (title, content, content_unformatted, author) VALUES ($1, $2, $3, $4)", + [title, content, content_unformatted, author], + ); +}; + +// Reads all news +const readNews = async (): Promise => { + const result = await postgres.query( + "SELECT * FROM news ORDER BY created DESC", + ); + + return result.rows; +}; + +// Updates news +const updateNews = async ( + id: number, + content: string, + content_unformatted: string, +) => { + await postgres.query( + "UPDATE news SET content = $1, content_unformatted = $2 WHERE id = $3", + [content, content_unformatted, id], + ); +}; + +// Deletes news +const deleteNews = async (id: number) => { + await postgres.query("DELETE FROM news WHERE id = $1", [id]); +}; + +export { createNews, readNews, updateNews, deleteNews }; diff --git a/src/db/post.ts b/src/db/post.ts new file mode 100755 index 0000000..0fc985f --- /dev/null +++ b/src/db/post.ts @@ -0,0 +1,367 @@ +import { valkey } from "../cache"; +import { postgres } from "../db"; +import { CzchanError } from "../error"; +import { File, Post } from "./schema"; +import { createHash } from "crypto"; +import { QueryResult } from "pg"; + +// Creates a post +const createPost = async ( + board: string, + id: number, + thread: number | null, + subject: string | null, + name: string | null, + user_id: string, + tripcode: string | null, + capcode: string | null, + email: string | null, + content: string, + content_unformatted: string, + files: File[], + password: string, + ip: string, + metadata: object, +) => { + const result: QueryResult = await postgres.query( + "INSERT INTO posts " + + "(board, id, thread, subject, name, user_id, tripcode, capcode, email, content, content_unformatted, files, password, ip, metadata) " + + "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) RETURNING *", + [ + board, + id, + thread, + subject, + name, + user_id, + tripcode, + capcode, + email, + content, + content_unformatted, + files, + password, + ip, + metadata, + ], + ); + + const post = result.rows[0]; + const timestamp = post.created.getTime(); + const cacheId = `${board}/${id}`; + + await valkey.zadd("posts:total", timestamp, cacheId); + await valkey.zadd(`posts:board:${board}`, timestamp, cacheId); + await valkey.sadd("posters:total", ip); + await valkey.sadd(`posters:board:${board}`, ip); + + const hash = createHash("sha256").update(content_unformatted).digest("hex"); + + await valkey.zadd(`posts:ip:${ip}`, timestamp, cacheId); + await valkey.zadd(`posts:content:${hash}`, timestamp, cacheId); + + if (!thread) { + await valkey.zadd("threads:total", timestamp, cacheId); + await valkey.zadd(`threads:board:${board}`, timestamp, cacheId); + } else { + await postgres.query( + "UPDATE posts SET replies = replies + 1 WHERE board = $1 AND id = $2", + [board, thread], + ); + } +}; + +// Reports the post +const createPostReport = async (board: string, id: number, report: Report) => { + await postgres.query( + "UPDATE posts SET reported = CURRENT_TIMESTAMP, reports = reports || $1::jsonb WHERE board = $2 AND id = $3", + [[report], board, id], + ); +}; + +// Reads a post +const readPost = async (board: string, id: number): Promise => { + const result = await postgres.query( + "SELECT * FROM posts WHERE board = $1 AND id = $2", + [board, id], + ); + + if (result.rowCount === 0) { + throw new CzchanError(`Příspěvek >>>/${board}/${id} neexistuje.`, 404); + } + + return result.rows[0]; +}; + +// Reads replies to a thread +const readPostReplies = async (board: string, id: number): Promise => { + const result = await postgres.query( + "SELECT * FROM posts WHERE board = $1 AND thread = $2", + [board, id], + ); + + return result.rows; +}; + +// Reads a page of threads from a board +const readBoardThreads = async ( + board: string, + page: number, +): Promise => { + const result = await postgres.query( + "SELECT * FROM posts WHERE thread IS NULL AND board = $1 ORDER BY sticky DESC, bumped DESC LIMIT 10 OFFSET $2", + [board, (page - 1) * 10], + ); + + return result.rows; +}; + +// Reads all threads from a board, oldest to newest +const readAllBoardThreads = async (board: string): Promise => { + const result = await postgres.query( + "SELECT * FROM posts WHERE thread IS NULL AND board = $1 ORDER BY created DESC", + [board], + ); + + return result.rows; +}; + +// Reads a page of threads from multiple boards +const readOverboardThreads = async ( + page: number, + boards: string[] = [], +): Promise => { + const result = await (boards.length !== 0 + ? postgres.query( + "SELECT * FROM posts WHERE thread IS NULL ORDER BY bumped DESC LIMIT 10 OFFSET $1 WHERE board = ANY($2)", + [(page - 1) * 10, boards], + ) + : postgres.query( + "SELECT * FROM posts WHERE thread IS NULL ORDER BY bumped DESC LIMIT 10 OFFSET $1", + [(page - 1) * 10], + )); + + return result.rows; +}; + +// Reads all threads from a board +const readBoardCatalog = async (board: string): Promise => { + const result = await postgres.query( + "SELECT * FROM posts WHERE thread IS NULL AND board = $1 ORDER BY sticky DESC, bumped DESC", + [board], + ); + + return result.rows; +}; + +// Reads all threads from multiple boards +const readOverboardCatalog = async (boards: string[] = []): Promise => { + const result = await (boards.length !== 0 + ? postgres.query( + "SELECT * FROM posts WHERE thread IS NULL ORDER BY bumped DESC WHERE board = ANY($2)", + [boards], + ) + : postgres.query( + "SELECT * FROM posts WHERE thread IS NULL ORDER BY bumped DESC", + )); + + return result.rows; +}; + +// Reads posts containing a substring on a specific board +const readBoardQueryPosts = async ( + board: string, + query: string, + page: number, +): Promise => { + const result = await postgres.query( + "SELECT * FROM posts WHERE board = $1 AND LOWER(content_unformatted) LIKE LOWER($2) ORDER BY created DESC LIMIT 10 OFFSET $3", + [board, query, (page - 1) * 10], + ); + + return result.rows; +}; + +// Reads posts containing a substring +const readOverboardQueryPosts = async ( + query: string, + page: number, + boards: string[] = [], +): Promise => { + const result = await (boards.length !== 0 + ? postgres.query( + "SELECT * FROM posts WHERE LOWER(content_unformatted) LIKE LOWER($1) ORDER BY created DESC LIMIT 10 OFFSET $2 AND board = ANY($2)", + [query, (page - 1) * 10, boards], + ) + : postgres.query( + "SELECT * FROM posts WHERE LOWER(content_unformatted) LIKE LOWER($1) ORDER BY created DESC LIMIT 10 OFFSET $2", + [query, (page - 1) * 10], + )); + + return result.rows; +}; + +// Reads a page of reported posts +const readReportedPosts = async (page: number): Promise => { + const result = await postgres.query( + "SELECT * FROM posts WHERE reports != '[]'::jsonb ORDER BY jsonb_array_length(reports), reported DESC LIMIT 10 OFFSET $1", + [(page - 1) * 10], + ); + + return result.rows; +}; + +// Reads a page of posts by a specific IP +const readIpPosts = async (ip: string, page: number): Promise => { + const result = await postgres.query( + "SELECT * FROM posts WHERE ip = $1 ORDER BY created DESC LIMIT 10 OFFSET $2", + [ip, (page - 1) * 10], + ); + + return result.rows; +}; + +// Changes the post's content (useful for trolling by jannies) +const updatePostContent = async ( + board: string, + id: number, + content: string, + content_unformatted: string, +) => { + await postgres.query( + "UPDATE posts SET content = $1, content_unformatted = $2 WHERE board = $3 AND id = $4", + [content, content_unformatted, board, id], + ); + + // We're not updating the hash in Redis because that's there for rate limiting plebs +}; + +// Sets the thread's sticky priority +const updatePostSticky = async (board: string, id: number, sticky: number) => { + await postgres.query( + "UPDATE posts SET sticky = $1 WHERE board = $2 AND id = $3", + [sticky, board, id], + ); +}; + +// Locks/unlocks the thread +const updatePostLocked = async (board: string, id: number) => { + await postgres.query( + "UPDATE posts SET locked = NOT locked WHERE board = $1 AND id = $2", + [board, id], + ); +}; + +// Bumplocks/unlocks the thread +const updatePostBumplocked = async (board: string, id: number) => { + await postgres.query( + "UPDATE posts SET bumplocked = NOT bumplocked WHERE board = $1 AND id = $2", + [board, id], + ); +}; + +// Loops/unloops the thread +const updatePostLooped = async (board: string, id: number) => { + await postgres.query( + "UPDATE posts SET looped = NOT looped WHERE board = $1 AND id = $2", + [board, id], + ); +}; + +// Bumps the post +const updatePostBump = async (board: string, id: number) => { + await postgres.query( + "UPDATE posts SET bumps = bumps + 1, bumped = CURRENT_TIMESTAMP WHERE board = $1 AND id = $2", + [board, id], + ); +}; + +// Deletes the post +const deletePost = async (post: Post) => { + const cacheId = `${post.board}/${post.id}`; + + // If this is the OP + if (!post.thread) { + await valkey.zrem("threads:total", cacheId); + await valkey.zrem(`threads:board:${post.board}`, cacheId); + + const replies: QueryResult = await postgres.query( + "SELECT * FROM posts WHERE board = $1 AND thread = $2", + [post.board, post.id], + ); + + for (const reply of replies.rows) { + // Le epic recursion + await deletePost(reply); + } + } + + await postgres.query("DELETE FROM posts WHERE board = $1 AND id = $2", [ + post.board, + post.id, + ]); + + // Deactivate quotes of this post + const quote = `>>${post.id}`; + const deadQuote = `>>${post.id}`; + + await postgres.query("UPDATE posts SET content = replace(content, $1, $2)", [ + quote, + deadQuote, + ]); + + // Deactivate cross-quotes of this post + const crossQuote = `>>$gt/${post.board}/${post.id}/`; + const deadCrossQuote = `>>$gt;/${post.board}/${post.id}`; + + await postgres.query("UPDATE posts SET content = replace(content, $1, $2)", [ + crossQuote, + deadCrossQuote, + ]); + + await valkey.zrem("posts:total", cacheId); + await valkey.zrem(`posts:board:${post.board}`, cacheId); + + const hash = createHash("sha256") + .update(post.content_unformatted) + .digest("hex"); + + await valkey.zrem(`posts:ip:${post.ip}`, cacheId); + await valkey.zrem(`posts:content:${hash}`, cacheId); +}; + +// Deletes reports +const deletePostReports = async (board: string, id: number) => { + await postgres.query( + "UPDATE posts SET reports = '[]'::jsonb WHERE board = $1 AND id = $2", + [board, id], + ); +}; + +export { + createPost, + createPostReport, + readPost, + readPostReplies, + readBoardThreads, + readAllBoardThreads, + readOverboardThreads, + readBoardCatalog, + readOverboardCatalog, + readBoardQueryPosts, + readOverboardQueryPosts, + readReportedPosts, + readIpPosts, + updatePostContent, + updatePostSticky, + updatePostLocked, + updatePostBumplocked, + updatePostLooped, + updatePostBump, + deletePost, + deletePostReports, +}; diff --git a/src/db/schema.ts b/src/db/schema.ts new file mode 100755 index 0000000..42b7879 --- /dev/null +++ b/src/db/schema.ts @@ -0,0 +1,133 @@ +import { CzchanPerm } from "../permissions"; + +// JSON types +type File = { + original_filename: string; + url: string; + size: number; + width: number; + height: number; + format: string; + hashes: { + [key: string]: string; + }; + thumb: { + url: string; + size: number; + width: number; + height: number; + format: string; + } | null; +}; + +type BoardConfig = { + // Non-exhaustive + private: boolean; + unlisted: boolean; +}; + +// Schema +type User = { + username: string; + password: string; + rank: number; + capcode: string; + permissions: CzchanPerm[]; + created: Date; +}; + +type Board = { + id: string; + name: string; + description: string; + config: BoardConfig; + created_by: string; + created: Date; +}; + +type Post = { + board: string; + id: number; + thread: number | null; + name: string | null; + user_id: string; + tripcode: string | null; + capcode: string | null; + email: string | null; + content: string; + content_unformatted: string; + files: File[]; + password: string; + ip: string; + bumps: number; + replies: number; + sticky: number; + locked: boolean; + bumplocked: boolean; + looped: boolean; + reported: Date; + reports: { reason: string; ip: string }[]; + bumped: Date; + created: Date; + metadata: object; +}; + +type Ban = { + id: number; + ip_range: string; + reason: string; + board: string | null; + appealable: boolean; + appeal: string | null; + expires: Date | null; + created_by: string; + created: Date; +}; + +type Banner = { + id: number; + banner: File; + created_by: string; + created: Date; +}; + +type News = { + id: number; + subject: string; + content: string; + content_unformatted: string; + created_by: string; + created: Date; +}; + +// Utility +const encodeCapcode = (capcode: { + text: string; + color: string; + icon: string | null; +}): string => { + if (capcode.icon) { + return `${capcode.text}:${capcode.color}:${capcode.icon}`; + } else { + return `${capcode.text}:${capcode.color}`; + } +}; + +const parseCapcode = ( + capcode: string, +): { + text: string; + color: string; + icon: string | null; +} => { + const params = capcode.split(":"); + + return { + text: params[0], + color: params[1], + icon: params[2] || null, + }; +}; + +export type { File, BoardConfig, User, Board, Post, Ban, Banner, News }; +export { encodeCapcode, parseCapcode }; diff --git a/src/db/user.ts b/src/db/user.ts new file mode 100755 index 0000000..c7d4924 --- /dev/null +++ b/src/db/user.ts @@ -0,0 +1,120 @@ +import { valkey } from "../cache"; +import { postgres } from "../db"; +import { CzchanError } from "../error"; +import { CzchanPerm } from "../permissions"; +import { User } from "./schema"; +import { QueryResult } from "pg"; +import SuperJSON from "superjson"; + +// Creates an user (bcrypt-hashed password) +const createUser = async ( + username: string, + password: string, + rank: number, + capcode: string, + permissions: CzchanPerm[], + setup: boolean = false, +) => { + const result: QueryResult = await postgres.query( + "INSERT INTO users (username, password, rank, capcode, permissions) VALUES ($1, $2, $3, $4, $5) RETURNING *", + [username, password, rank, capcode, JSON.stringify(permissions)], + ); + + if (!setup) { + const user = result.rows[0]; + await valkey.hset("users", { + [user.username]: SuperJSON.stringify(user), + }); + } +}; + +// Reads an user +const readUser = async (username: string): Promise => { + const result = await valkey.hget("users", username); + + if (!result) { + throw new CzchanError(`Uživatel "${username}" neexistuje.`, 404); + } + + return SuperJSON.parse(result); +}; + +// Lists all users +const readUsers = async (): Promise => { + const users = (await valkey.hvals("users")) + .map((json) => SuperJSON.parse(json) as User) + .sort( + // rank ASC, created ASC + (a, b) => a.rank - b.rank || a.created.getTime() - b.created.getTime(), + ); + + return users; +}; + +// Updates the user's rank +const updateUserRank = async (username: string, rank: number) => { + const result = await postgres.query( + "UPDATE users SET rank = $1 WHERE username = $2 RETURNING *", + [rank, username], + ); + + const user = result.rows[0]; + + await valkey.hset("users", username, SuperJSON.stringify(user)); +}; + +// Updates the user's capcode +const updateUserCapcode = async (username: string, capcode: string) => { + const result = await postgres.query( + "UPDATE users SET capcode = $1 WHERE username = $2 RETURNING *", + [capcode, username], + ); + + const user = result.rows[0]; + + await valkey.hset("users", username, SuperJSON.stringify(user)); +}; + +// Updates the user's permissions +const updateUserPermissions = async ( + username: string, + permissions: CzchanPerm[], +) => { + const result = await postgres.query( + "UPDATE users SET permissions = $1 WHERE username = $2 RETURNING *", + [JSON.stringify(permissions), username], + ); + + const user = result.rows[0]; + + await valkey.hset("users", username, SuperJSON.stringify(user)); +}; + +// Changes the password (bcrypt) +const updateUserPassword = async (username: string, newPassword: string) => { + const result: QueryResult = await postgres.query( + "UPDATE users SET password = $1 WHERE username = $2 RETURNING *", + [newPassword, username], + ); + + const user = result.rows[0]; + + await valkey.hset("users", username, SuperJSON.stringify(user)); +}; + +// Deletes an user +const deleteUser = async (username: string) => { + await postgres.query("DELETE FROM users WHERE username = $1", [username]); + await valkey.hdel("users", username); +}; + +export { + createUser, + readUser, + readUsers, + updateUserRank, + updateUserCapcode, + updateUserPermissions, + updateUserPassword, + deleteUser, +}; diff --git a/src/error.tsx b/src/error.tsx new file mode 100755 index 0000000..060f264 --- /dev/null +++ b/src/error.tsx @@ -0,0 +1,73 @@ +import Page from "./components/page"; +import PageBody from "./components/page_body"; +import PageHead from "./components/page_head"; +import { getCtx, TemplateCtx } from "./ctx"; +import { NextFunction, Request, Response } from "express"; +import z from "zod"; + +class CzchanError extends Error { + statusCode: number; + + constructor(message: string, statusCode: number) { + super(message); + this.name = "CzchanError"; + this.statusCode = statusCode; + } +} + +const ErrorPage = (ctx: TemplateCtx, errors: CzchanError[]) => ( + + + +
+
Chyba {errors[0].statusCode}
+
+ {errors.map((err) => ( +

{err.message}

+ ))} +
+
+
+
+); + +// Error wrapper, used many times. We're just gonna call it e, short for error. +// THIS IS THE ONLY ONE LETTER NAME I'LL USE HERE I PROMISE +// Turning this into middleware doesn't work so this is the second best thing +const e = + (fn: (req: Request, res: Response, next: NextFunction) => Promise) => + async (req: Request, res: Response, next: NextFunction) => { + try { + await fn(req, res, next); + } catch (err) { + const ctx = await getCtx(req); + + if (err instanceof CzchanError) { + // Native error + const html = ErrorPage(ctx, [err]); + res.status(err.statusCode).send(html); + } else if (err instanceof z.ZodError) { + // Zod error + const errors = err.issues.map( + (issue) => new CzchanError(issue.message, 400), + ); + + const html = ErrorPage(ctx, errors); + res.status(400).send(html); + } else { + // What the fuck + console.error("Interní chyba serveru", err); + + const html = ErrorPage(ctx, [ + new CzchanError( + "Interní chyba serveru. Jestli přetrvává, kontaktuj administrátora.", + 500, + ), + ]); + + res.status(500).send(html); + } + } + }; + +export { CzchanError, ErrorPage, e }; diff --git a/src/main.ts b/src/main.ts new file mode 100755 index 0000000..fc2de8b --- /dev/null +++ b/src/main.ts @@ -0,0 +1,42 @@ +import { initCache } from "./cache"; +import { loadConfig } from "./config"; +import { getCtx } from "./ctx"; +import { initDb } from "./db"; +import { CzchanError, ErrorPage } from "./error"; +import { routes } from "./routes"; +import cookieParser from "cookie-parser"; +import express, { Request, Response } from "express"; + +const main = async () => { + const initialConfig = await loadConfig(); + + await initDb(initialConfig); + await initCache(initialConfig); + + const app = express(); + + app.use("/public", express.static("./public")); + app.use("/LICENSE.txt", express.static("./LICENSE.txt")); + app.use("/favicon.ico", express.static("./public/favicon.ico")); + app.use(cookieParser()); + + const router = await routes(); + app.use("/", router); + + app.use(async (req: Request, res: Response) => { + const ctx = await getCtx(req, false); + const html = ErrorPage(ctx, [ + new CzchanError("Tato stránka neexistuje.", 404), + ]); + + res.status(404).send(html); + }); + + app.listen(initialConfig.site.server.port, () => { + console.info( + `Server běží na http://localhost:${initialConfig.site.server.port}`, + ); + }); +}; + +main(); diff --git a/src/permissions.ts b/src/permissions.ts new file mode 100755 index 0000000..f44a5f8 --- /dev/null +++ b/src/permissions.ts @@ -0,0 +1,77 @@ +import { User } from "./db/schema"; + +enum CzchanPerm { + // Admin content permissions + MANAGE_USERS = "MANAGE_USERS", + MANAGE_BANNERS = "MANAGE_BANNERS", + ALL_BANNERS = "ALL_BANNERS", + MANAGE_BANS = "MANAGE_BANS", + ALL_BANS = "ALL_BANS", + MANAGE_BOARDS = "MANAGE_BOARDS", + ALL_BOARDS = "ALL_BOARDS", + MANAGE_NEWS = "MANAGE_NEWS", + ALL_NEWS = "ALL_NEWS", + // Post permissions + MANAGE_REPORTS = "MANAGE_REPORTS", + MANAGE_POSTS = "MANAGE_POSTS", + DELETE_POSTS = "DELETE_POSTS", + DELETE_FILES = "DELETE_FILES", + SPOILER_POSTS = "SPOILER_POSTS", + MOVE_POSTS = "MOVE_POSTS", + EDIT_POSTS = "EDIT_POSTS", + // Cognitohazards + VIEW_IPS = "VIEW_IPS", + VIEW_LOGS = "VIEW_LOGS", + VIEW_METADATA = "VIEW_METADATA", + VIEW_PRIVATE_BOARDS = "VIEW_PRIVATE_BOARDS", + // Capcode + CAPCODE = "CAPCODE", + CUSTOM_CAPCODE = "CUSTOM_CAPCODE", + // Bypasses + BYPASS_BAN = "BYPASS_BAN", + BYPASS_LOCK = "BYPASS_LOCK", + BYPASS_BLOCK = "BYPASS_BLOCK", + BYPASS_FILTER = "BYPASS_FILTER", + BYPASS_RATELIMIT = "BYPASS_RATELIMIT", + // For convenience + ALL_FIELDS = "ALL_FIELDS", + ADVANCED_MARKUP = "ADVANCED_MARKUP", + MANAGE_SITE_ASSETS = "MANAGE_SITE_ASSETS", + MANAGE_SITE_CONFIG = "MANAGE_SITE_CONFIG", +} + +const canUser = (user: User | null, perm: CzchanPerm): boolean => { + if (!user) { + return false; + } + + return user.rank === 0 || user.permissions.includes(perm); +}; + +const canUserApplyRole = ( + user: User | null, + role: { rank: number; capcode: string; permissions: CzchanPerm[] }, +) => { + if (!user) { + return false; + } + + return ( + user.rank === 0 || + (permSubset(user.permissions || [], role.permissions) && + user.rank < role.rank) + ); +}; + +const canUserActOn = (sub: User | null, obj: User): boolean => { + if (!sub) { + return false; + } + + return sub.rank === 0 || sub.rank < obj.rank; +}; + +const permSubset = (array: CzchanPerm[], target: CzchanPerm[]): boolean => + target.every((v) => array.includes(v)); + +export { CzchanPerm, canUser, canUserActOn, canUserApplyRole, permSubset }; diff --git a/src/routes.ts b/src/routes.ts new file mode 100755 index 0000000..c12d775 --- /dev/null +++ b/src/routes.ts @@ -0,0 +1,109 @@ +import { Config, getConfig } from "./config"; +import { User } from "./db/schema"; +import { readUser } from "./db/user"; +import { CzchanError, e } from "./error"; +import actionLogin from "./routes/actions/login"; +import actionLogout from "./routes/actions/logout"; +import index from "./routes/index"; +import login from "./routes/login"; +import modActionBoardActions from "./routes/mod/actions/board_actions"; +import modActionCreateBoard from "./routes/mod/actions/create_board"; +import modActionCreateUser from "./routes/mod/actions/create_user"; +import modActionUpdatePassword from "./routes/mod/actions/update_password"; +import modActionUpdatePermissions from "./routes/mod/actions/update_permissions"; +import modActionUserActions from "./routes/mod/actions/user_actions"; +import modBanners from "./routes/mod/banners"; +import modBans from "./routes/mod/bans"; +import modBoards from "./routes/mod/boards"; +import modIndex from "./routes/mod/index"; +import modLogs from "./routes/mod/logs"; +import modNews from "./routes/mod/news"; +import modPermissions from "./routes/mod/permissions"; +import modReports from "./routes/mod/reports"; +import modUsers from "./routes/mod/users"; +import { NextFunction, Request, Response, Router } from "express"; +import jwt from "jsonwebtoken"; + +const routes = async () => { + const router = Router(); + + // Middleware + router.use(e(configMiddleware)); + router.use("/mod/*splat", e(authMiddleware)); + // User display routes + router.get("/", e(index)); + router.get("/login", e(login)); + // User action routes + router.post("/actions/login", e(actionLogin)); + router.get("/actions/logout", e(actionLogout)); + // Mod display routes + router.get("/mod/", e(modIndex)); + router.get("/mod/users", e(modUsers)); + router.get("/mod/banners", e(modBanners)); + router.get("/mod/bans", e(modBans)); + router.get("/mod/boards", e(modBoards)); + router.get("/mod/reports", e(modReports)); + router.get("/mod/news", e(modNews)); + router.get("/mod/logs", e(modLogs)); + router.get("/mod/permissions/:user", e(modPermissions)); + // Mod action routes + router.post("/mod/actions/update_password", e(modActionUpdatePassword)); + router.post("/mod/actions/create_user", e(modActionCreateUser)); + router.post("/mod/actions/user_actions", e(modActionUserActions)); + router.post("/mod/actions/create_board", e(modActionCreateBoard)); + router.post("/mod/actions/board_actions", e(modActionBoardActions)); + router.post("/mod/actions/update_permissions", e(modActionUpdatePermissions)); + + return router; +}; + +declare global { + namespace Express { + interface Request { + user: User | null; + config: Config; + } + } +} + +const configMiddleware = async ( + req: Request, + _: Response, + next: NextFunction, +) => { + req.config = await getConfig(); + + next(); +}; + +const authMiddleware = async ( + req: Request, + _: Response, + next: NextFunction, +) => { + const token = req.cookies.auth; + const config = req.config; + + let user = null; + + if (!token) { + throw new CzchanError( + "Pro přístup k této stránce se musíš přihlásit.", + 401, + ); + } + + try { + const data = jwt.verify(token, config.site.secrets.jwt); + const username = data["sub"] as string; + user = await readUser(username); + } catch { + throw new CzchanError("Neplatný token. Zkus se znovu přihlásit.", 401); + } + + req.user = user; + + next(); +}; + +export { routes }; diff --git a/src/routes/actions/login.ts b/src/routes/actions/login.ts new file mode 100755 index 0000000..06bd407 --- /dev/null +++ b/src/routes/actions/login.ts @@ -0,0 +1,25 @@ +import { readUser } from "../../db/user"; +import { CzchanError } from "../../error"; +import { LoginForm } from "../../zod_schema"; +import bcrypt from "bcrypt"; +import { Request, Response } from "express"; +import formidable from "formidable"; +import jwt from "jsonwebtoken"; + +export default async (req: Request, res: Response) => { + const form = formidable({}); + const [fields, _] = await form.parse(req); + + const { username, password } = LoginForm.parse(fields); + + const user = await readUser(username); + const matches = await bcrypt.compare(password, user.password); + + if (!matches) { + throw new CzchanError("Nesprávné heslo.", 401); + } + + const token = jwt.sign({ sub: username }, req.config.site.secrets.jwt); + + res.cookie("auth", token).redirect("/mod/"); +}; diff --git a/src/routes/actions/logout.ts b/src/routes/actions/logout.ts new file mode 100755 index 0000000..a9f4c1b --- /dev/null +++ b/src/routes/actions/logout.ts @@ -0,0 +1,5 @@ +import { Request, Response } from "express"; + +export default async (_: Request, res: Response) => { + res.clearCookie("auth").redirect("/"); +}; diff --git a/src/routes/index.tsx b/src/routes/index.tsx new file mode 100755 index 0000000..9ac7b88 --- /dev/null +++ b/src/routes/index.tsx @@ -0,0 +1,30 @@ +import Page from "../components/page"; +import PageBody from "../components/page_body"; +import PageHead from "../components/page_head"; +import { TemplateCtx, getCtx } from "../ctx"; +import { Request, Response } from "express"; + +export default async (req: Request, res: Response) => { + const ctx = await getCtx(req); + const html = HomePage(ctx); + + res.send(html); +}; + +const HomePage = (ctx: TemplateCtx) => { + return ( + + + +
+ +

Český anonymní imageboard

+
+
+
+ ); +}; diff --git a/src/routes/login.tsx b/src/routes/login.tsx new file mode 100755 index 0000000..34fa440 --- /dev/null +++ b/src/routes/login.tsx @@ -0,0 +1,34 @@ +import { Form, FormField } from "../components/form"; +import Page from "../components/page"; +import PageBody from "../components/page_body"; +import PageHead from "../components/page_head"; +import { getCtx, TemplateCtx } from "../ctx"; +import { Request, Response } from "express"; + +export default async (req: Request, res: Response) => { + const ctx = await getCtx(req); + const html = Template(ctx); + + res.send(html); +}; + +const Template = (ctx: TemplateCtx) => { + return ( + + + +

Přihlásit se

+
+ + + + + + + +
+ +
+
+ ); +}; diff --git a/src/routes/mod/actions/board_actions.ts b/src/routes/mod/actions/board_actions.ts new file mode 100644 index 0000000..131ce51 --- /dev/null +++ b/src/routes/mod/actions/board_actions.ts @@ -0,0 +1,88 @@ +import { deleteBoard, readBoard, updateBoardName } from "../../../db/board"; +import { Board } from "../../../db/schema"; +import { CzchanError } from "../../../error"; +import { canUser, CzchanPerm } from "../../../permissions"; +import { BoardActionsForm, UpdateBoardForm } from "../../../zod_schema"; +import { Request, Response } from "express"; +import formidable from "formidable"; + +export default async (req: Request, res: Response) => { + if (canUser(req.user, CzchanPerm.MANAGE_BOARDS)) { + throw new CzchanError("K tomuto nemáš oprávnění.", 403); + } + + const form = formidable({}); + const [fields, _] = await form.parse(req); + const { boards, action } = BoardActionsForm.parse(fields); + + const boards_ = await Promise.all( + boards.map(async (id) => await readBoard(id)), + ); + + switch (action) { + case "$delete_board": + await deleteBoards(req, boards_); + break; + case "$update_board": + const { name, description } = UpdateBoardForm.parse(fields); + await updateBoards(req, boards_, name, description); + break; + } + + res.redirect("/mod/boards"); +}; + +const deleteBoards = async (req: Request, boards: Board[]) => { + // Validation + for (const board of boards) { + if ( + !( + req.user?.username === board.created_by || + canUser(req.user, CzchanPerm.ALL_BOARDS) + ) + ) { + throw new CzchanError( + `K odstranění nástěnky /${board.id}/ nemáš oprávnění.`, + 403, + ); + } + } + + // Action + for (const board of boards) { + await deleteBoard(board.id); + } +}; + +const updateBoards = async ( + req: Request, + boards: Board[], + name: string | null, + description: string | null, +) => { + // Validation + for (const board of boards) { + if ( + !( + req.user?.username === board.created_by || + canUser(req.user, CzchanPerm.ALL_BOARDS) + ) + ) { + throw new CzchanError( + `K upravení nástěnky /${board.id}/ nemáš oprávnění.`, + 403, + ); + } + } + + // Action + for (const board of boards) { + if (name) { + await updateBoardName(board.id, name); + } + + if (description) { + await updateBoardName(board.id, description); + } + } +}; diff --git a/src/routes/mod/actions/create_board.ts b/src/routes/mod/actions/create_board.ts new file mode 100644 index 0000000..234c8cc --- /dev/null +++ b/src/routes/mod/actions/create_board.ts @@ -0,0 +1,29 @@ +import { createBoard } from "../../../db/board"; +import { CzchanError } from "../../../error"; +import { canUser, CzchanPerm } from "../../../permissions"; +import { CreateBoardForm } from "../../../zod_schema"; +import { Request, Response } from "express"; +import formidable from "formidable"; +import { DatabaseError } from "pg"; + +export default async (req: Request, res: Response) => { + if (!canUser(req.user, CzchanPerm.MANAGE_BOARDS)) { + throw new CzchanError("K tomuto nemáš oprávnění.", 403); + } + + const form = formidable({}); + const [fields, _] = await form.parse(req); + const { id, name, description } = CreateBoardForm.parse(fields); + + try { + await createBoard(req.config, id, name, description); + } catch (e) { + if ((e as DatabaseError).constraint === "users_pkey") { + throw new CzchanError(`Nástěnka /${id}/ již existuje.`, 409); + } else { + throw e; + } + } + + res.redirect("/mod/boards"); +}; diff --git a/src/routes/mod/actions/create_user.ts b/src/routes/mod/actions/create_user.ts new file mode 100644 index 0000000..308c5a1 --- /dev/null +++ b/src/routes/mod/actions/create_user.ts @@ -0,0 +1,49 @@ +import { createUser } from "../../../db/user"; +import { CzchanError } from "../../../error"; +import { canUser, canUserApplyRole, CzchanPerm } from "../../../permissions"; +import { CreateUserForm } from "../../../zod_schema"; +import bcrypt from "bcrypt"; +import { Request, Response } from "express"; +import formidable from "formidable"; +import { DatabaseError } from "pg"; + +export default async (req: Request, res: Response) => { + if (!canUser(req.user, CzchanPerm.MANAGE_USERS)) { + throw new CzchanError("K tomuto nemáš oprávnění.", 403); + } + + const form = formidable({}); + const [fields, _] = await form.parse(req); + const { new_username, new_password, new_role } = CreateUserForm.parse(fields); + + const username_ = new_username; + const role_ = req.config.roles[new_role]; + + if (!role_) { + throw new CzchanError("Neplatná role.", 400); + } + + if (!canUserApplyRole(req.user, role_)) { + throw new CzchanError("K aplikaci této role nemáš oprávnění.", 403); + } + + const password_ = await bcrypt.hash(new_password, 10); + + try { + await createUser( + username_, + password_, + role_.rank, + role_.capcode, + role_.permissions, + ); + } catch (e) { + if ((e as DatabaseError).constraint === "users_pkey") { + throw new CzchanError(`Uživatel "${username_}" již existuje.`, 409); + } else { + throw e; + } + } + + res.redirect("/mod/users"); +}; diff --git a/src/routes/mod/actions/update_password.ts b/src/routes/mod/actions/update_password.ts new file mode 100644 index 0000000..0906337 --- /dev/null +++ b/src/routes/mod/actions/update_password.ts @@ -0,0 +1,27 @@ +import { User } from "../../../db/schema"; +import { updateUserPassword } from "../../../db/user"; +import { CzchanError } from "../../../error"; +import { UpdatePasswordForm } from "../../../zod_schema"; +import bcrypt from "bcrypt"; +import { Request, Response } from "express"; +import formidable from "formidable"; + +export default async (req: Request, res: Response) => { + const form = formidable({}); + const [fields, _] = await form.parse(req); + + const { old_password, new_password } = UpdatePasswordForm.parse(fields); + + const user = req.user as User; + const matches = await bcrypt.compare(old_password, user.password); + + if (!matches) { + throw new CzchanError("Nesprávné heslo.", 401); + } + + const new_password_ = await bcrypt.hash(new_password, 10); + + await updateUserPassword(user.username, new_password_); + + res.redirect("/mod/"); +}; diff --git a/src/routes/mod/actions/update_permissions.ts b/src/routes/mod/actions/update_permissions.ts new file mode 100644 index 0000000..3695830 --- /dev/null +++ b/src/routes/mod/actions/update_permissions.ts @@ -0,0 +1,46 @@ +import { readUser, updateUserPermissions } from "../../../db/user"; +import { CzchanError } from "../../../error"; +import { + canUser, + canUserActOn, + CzchanPerm, + permSubset, +} from "../../../permissions"; +import { UpdatePermissionsForm } from "../../../zod_schema"; +import { Request, Response } from "express"; +import formidable from "formidable"; + +export default async (req: Request, res: Response) => { + if (!canUser(req.user, CzchanPerm.MANAGE_USERS)) { + throw new CzchanError("K tomuto nemáš oprávnění.", 403); + } + + const form = formidable({}); + const [fields, _] = await form.parse(req); + const { user, permissions } = UpdatePermissionsForm.parse(fields); + + const user_ = await readUser(user); + + if ( + !( + req.user?.rank !== 0 || + permSubset(req.user?.permissions || [], permissions) + ) + ) { + throw new CzchanError( + `Aplikovaná oprávnění nesmí obsahovat jiná oprávnění, než tvá vlastní.`, + 403, + ); + } + + if (!canUserActOn(req.user, user_)) { + throw new CzchanError( + `K upravení oprávnění uživatele "${user_.username}" nemáš oprávnění.`, + 403, + ); + } + + await updateUserPermissions(user_.username, permissions); + + res.redirect(`/mod/users`); +}; diff --git a/src/routes/mod/actions/user_actions.ts b/src/routes/mod/actions/user_actions.ts new file mode 100644 index 0000000..64c7bee --- /dev/null +++ b/src/routes/mod/actions/user_actions.ts @@ -0,0 +1,213 @@ +import { encodeCapcode, parseCapcode, User } from "../../../db/schema"; +import { + deleteUser, + readUser, + updateUserCapcode, + updateUserPermissions, + updateUserRank, +} from "../../../db/user"; +import { CzchanError } from "../../../error"; +import { + canUser, + canUserActOn, + canUserApplyRole, + CzchanPerm, +} from "../../../permissions"; +import { + ApplyRoleForm, + UpdateCapcodeForm, + UpdateRankForm, + UserActionsForm, +} from "../../../zod_schema"; +import { Request, Response } from "express"; +import formidable from "formidable"; + +export default async (req: Request, res: Response) => { + const form = formidable({}); + const [fields, _] = await form.parse(req); + const { users, action } = UserActionsForm.parse(fields); + + const users_ = await Promise.all( + users.map(async (username) => await readUser(username)), + ); + + let selfDeletion = false; + + switch (action) { + case "$delete_user": + selfDeletion = await deleteUsers(req, users_); + break; + case "$update_rank": + const { rank } = UpdateRankForm.parse(fields); + await updateRank(req, users_, rank); + break; + case "$update_capcode": + const { capcode_text, capcode_color, capcode_icon } = + UpdateCapcodeForm.parse(fields); + + await updateCapcode( + req, + users_, + capcode_text, + capcode_color, + capcode_icon, + ); + break; + case "$apply_role": + const { role } = ApplyRoleForm.parse(fields); + await applyRole(req, users_, role); + break; + } + + if (selfDeletion) { + res.redirect("/mod/logout"); + } else { + res.redirect("/mod/users"); + } +}; + +const deleteUsers = async (req: Request, users: User[]): Promise => { + // Validation + for (const user of users) { + if ( + !( + (canUser(req.user, CzchanPerm.MANAGE_USERS) && + canUserActOn(req.user, user)) || + req.user?.username === user.username + ) + ) { + throw new CzchanError( + `K odstranění užvatele "${user.username}" nemáš oprávnění.`, + 403, + ); + } + } + + // Action + let selfDeletion = false; + + for (const user of users) { + if (req.user?.username === user.username) { + selfDeletion = true; + } + + await deleteUser(user.username); + } + + return selfDeletion; +}; + +const updateRank = async (req: Request, users: User[], rank: number) => { + // Validation + if (req.user?.rank === undefined || (rank !== 0 && req.user?.rank >= rank)) { + throw new CzchanError( + `Aplikovaná úroveň musí být nižší (numericky větší) než tvá vlastní.`, + 403, + ); + } + + for (const user of users) { + if ( + !( + canUser(req.user, CzchanPerm.MANAGE_USERS) && + canUserActOn(req.user, user) + ) + ) { + throw new CzchanError( + `K upravení úrovně uživatele "${user.username}" nemáš oprávnění.`, + 403, + ); + } + } + + // Action + for (const user of users) { + await updateUserRank(user.username, rank); + } +}; + +const updateCapcode = async ( + req: Request, + users: User[], + capcode_text: string | null, + capcode_color: string | null, + capcode_icon: string | null, +) => { + // Validation + for (const user of users) { + if ( + !( + (canUser(req.user, CzchanPerm.MANAGE_USERS) && + canUserActOn(req.user, user)) || + (canUser(req.user, CzchanPerm.CUSTOM_CAPCODE) && + req.user?.username === user.username) + ) + ) { + throw new CzchanError( + `K upravení capcodu uživatele "${user.username}" nemáš oprávnění.`, + 403, + ); + } + } + + // Action + for (const user of users) { + let { text, color, icon } = parseCapcode(user.capcode); + + if (capcode_text) { + text = capcode_text; + } + + if (capcode_color) { + color = capcode_color; + } + + if (!capcode_icon || capcode_icon === "$keep") { + icon = icon; + } else if (capcode_icon === "$none") { + icon = null; + } else { + icon = capcode_icon; + } + + const capcode = encodeCapcode({ text, color, icon }); + + await updateUserCapcode(user.username, capcode); + } +}; + +const applyRole = async (req: Request, users: User[], role: string) => { + // Validation + let role_ = req.config.roles[role]; + + if (!role_) { + throw new CzchanError("Neplatná role.", 400); + } + + if (!canUserApplyRole(req.user, role_)) { + throw new CzchanError("K aplikaci této role nemáš oprávnění.", 403); + } + + const { rank, capcode, permissions } = role_; + + for (const user of users) { + if ( + !( + canUser(req.user, CzchanPerm.MANAGE_USERS) && + canUserActOn(req.user, user) + ) + ) { + throw new CzchanError( + `K aplikaci rolí na uživatele "${user.username}" nemáš oprávnění.`, + 403, + ); + } + } + + // Action + for (const user of users) { + await updateUserRank(user.username, rank); + await updateUserCapcode(user.username, capcode); + await updateUserPermissions(user.username, permissions); + } +}; diff --git a/src/routes/mod/banners.tsx b/src/routes/mod/banners.tsx new file mode 100644 index 0000000..16719f1 --- /dev/null +++ b/src/routes/mod/banners.tsx @@ -0,0 +1,123 @@ +import Datetime from "../../components/datetime"; +import { Form, FormField } from "../../components/form"; +import ModNav from "../../components/mod_nav"; +import Page from "../../components/page"; +import PageBody from "../../components/page_body"; +import PageHead from "../../components/page_head"; +import { getCtx, TemplateCtx } from "../../ctx"; +import { readBanners } from "../../db/banner"; +import { Banner } from "../../db/schema"; +import { CzchanError } from "../../error"; +import { canUser, CzchanPerm } from "../../permissions"; +import { PropsWithChildren } from "@kitajs/html"; +import { Request, Response } from "express"; + +export default async (req: Request, res: Response) => { + const ctx = await getCtx(req); + + if (!canUser(ctx.user, CzchanPerm.MANAGE_BANNERS)) { + throw new CzchanError("K tomuto nemáš oprávnění.", 403); + } + + const banners = await readBanners(); + const html = Template(ctx, banners); + + res.send(html); +}; + +const Template = (ctx: TemplateCtx, banners: Banner[]) => { + return ( + + + +

Bannery

+
+ +
+
+ + {canUser(ctx.user, CzchanPerm.MANAGE_BANNERS) && ( + <> +
+ + + )} + + +
+
+
+ ); +}; + +const BannerTable = ({ + ctx, + banners, +}: PropsWithChildren<{ ctx: TemplateCtx; banners: Banner[] }>) => ( + + + + + + + + + + + {banners.map((banner) => ( + + ))} + {banners.length === 0 && ( + + + + )} + +
BannerVytvořilVytvořen
+ Nic tady není... +
+); + +const BannerRow = ({ + ctx, + banner, +}: PropsWithChildren<{ ctx: TemplateCtx; banner: Banner }>) => ( + + + + + + + + {banner.created_by} + + + + +); + +const CreateForm = () => ( + <> +
+

Vytvořit

+
+ + + + +
+ + +); diff --git a/src/routes/mod/bans.tsx b/src/routes/mod/bans.tsx new file mode 100755 index 0000000..39c18fd --- /dev/null +++ b/src/routes/mod/bans.tsx @@ -0,0 +1,120 @@ +import Datetime from "../../components/datetime"; +import ModNav from "../../components/mod_nav"; +import Page from "../../components/page"; +import PageBody from "../../components/page_body"; +import PageHead from "../../components/page_head"; +import { getCtx, TemplateCtx } from "../../ctx"; +import { readBans } from "../../db/ban"; +import { Ban } from "../../db/schema"; +import { CzchanError } from "../../error"; +import { canUser, CzchanPerm } from "../../permissions"; +import { PropsWithChildren } from "@kitajs/html"; +import { Request, Response } from "express"; + +export default async (req: Request, res: Response) => { + const ctx = await getCtx(req); + + if (!canUser(ctx.user, CzchanPerm.MANAGE_BANS)) { + throw new CzchanError("K tomuto nemáš oprávnění.", 403); + } + + const bans = await readBans(); + const html = Template(ctx, bans); + + res.send(html); +}; + +const Template = (ctx: TemplateCtx, bans: Ban[]) => { + return ( + + + +

Bany

+
+ +
+
+ +
+ + +
+
+
+ ); +}; + +const BanTable = ({ + ctx, + bans, +}: PropsWithChildren<{ ctx: TemplateCtx; bans: Ban[] }>) => ( + + + + + + + + + + + + + + {bans.map((ban) => ( + + ))} + {bans.length === 0 && ( + + + + )} + +
IPDůvodNástěnkaVytvořilVypršíVytvořen
+ Nic tady není... +
+); + +const BanRow = ({ + ctx, + ban, +}: PropsWithChildren<{ ctx: TemplateCtx; ban: Ban }>) => ( + + + {(() => { + return ( + + ); + })()} + + + {canUser(ctx.user, CzchanPerm.VIEW_IPS) ? ban.ip_range : Skrytá} + + + {ban.board === null ? ( + Všechny + ) : ( + {`/${ban.board}/`} + )} + + {ban.created_by} + + + + + + + +); diff --git a/src/routes/mod/boards.tsx b/src/routes/mod/boards.tsx new file mode 100755 index 0000000..b3dcd1d --- /dev/null +++ b/src/routes/mod/boards.tsx @@ -0,0 +1,146 @@ +import Datetime from "../../components/datetime"; +import { Form, FormField } from "../../components/form"; +import ModNav from "../../components/mod_nav"; +import Page from "../../components/page"; +import PageBody from "../../components/page_body"; +import PageHead from "../../components/page_head"; +import { getCtx, TemplateCtx } from "../../ctx"; +import { readBoards } from "../../db/board"; +import { Board } from "../../db/schema"; +import { CzchanError } from "../../error"; +import { canUser, CzchanPerm } from "../../permissions"; +import { PropsWithChildren } from "@kitajs/html"; +import { Request, Response } from "express"; + +export default async (req: Request, res: Response) => { + const ctx = await getCtx(req); + + if (!canUser(ctx.user, CzchanPerm.MANAGE_BOARDS)) { + throw new CzchanError("K tomuto nemáš oprávnění.", 403); + } + + const boards = await readBoards(); + const html = Template(ctx, boards); + + res.send(html); +}; + +const Template = (ctx: TemplateCtx, boards: Board[]) => { + return ( + + + +

Nástěnky

+
+ +
+
+ +
+ + + + +
+
+
+ ); +}; + +const BoardTable = ({ + ctx, + boards, +}: PropsWithChildren<{ ctx: TemplateCtx; boards: Board[] }>) => ( + + + + + + + + + + + + + {boards.map((board) => ( + + ))} + {boards.length === 0 && ( + + + + )} + +
IDJménoPopisNastaveníVytvořena
+ Nic tady není... +
+); + +const BoardRow = ({ + ctx, + board, +}: PropsWithChildren<{ ctx: TemplateCtx; board: Board }>) => ( + + + + + /{board.id}/ + {board.name} + {board.description} + + [Nastavení] + + + + + +); + +const UpdateForm = () => ( + <> +
+

Upravit

+

Prázdná pole nebudou změněna.

+
+ + + + + + +
+ +); + +const CreateForm = () => ( + <> +
+

Vytvořit

+
+ + + + + + + + + + +
+ + +); diff --git a/src/routes/mod/index.tsx b/src/routes/mod/index.tsx new file mode 100755 index 0000000..e455ee8 --- /dev/null +++ b/src/routes/mod/index.tsx @@ -0,0 +1,144 @@ +import Capcode from "../../components/capcode"; +import { Form, FormField } from "../../components/form"; +import ModNav from "../../components/mod_nav"; +import Page from "../../components/page"; +import PageBody from "../../components/page_body"; +import PageHead from "../../components/page_head"; +import { getCtx, TemplateCtx } from "../../ctx"; +import { readBoards } from "../../db/board"; +import { Board } from "../../db/schema"; +import { CzchanPerm, canUser } from "../../permissions"; +import { PropsWithChildren } from "@kitajs/html"; +import { Request, Response } from "express"; + +export default async (req: Request, res: Response) => { + const ctx = await getCtx(req); + const boards = await readBoards(); + const html = Template(ctx, boards); + + res.send(html); +}; + +const Template = (ctx: TemplateCtx, boards: Board[]) => { + return ( + + + +

Domov

+
+ +
+ +
+ + +
+
+
+ ); +}; + +const UserTable = ({ ctx }: PropsWithChildren<{ ctx: TemplateCtx }>) => ( + + + + + + + + + + + + + + + + + + + +
Jméno{ctx.user?.username}
Úroveň + {ctx.user?.rank} + {ctx.user?.rank === 0 && ( + <> + {" "} + (root) + + )} +
Capcode + +
Oprávnění + [{ctx.user?.permissions.length}]{" "} + [Zobrazit] +
+); + +const BoardTable = ({ + ctx, + boards, +}: PropsWithChildren<{ ctx: TemplateCtx; boards: Board[] }>) => ( + + + + + + + + {boards + .filter( + (board) => + !board.config.private || + canUser(ctx.user, CzchanPerm.VIEW_PRIVATE_BOARDS), + ) + .map((board) => ( + + ))} + {boards.length === 0 && ( + + + + )} + +
Nástěnky
+ Nic tady není... +
+); + +const BoardRow = ({ board }: PropsWithChildren<{ board: Board }>) => ( + + + + /{board.id}/ - {board.name} + + {board.config.unlisted && ( + + )} + {board.config.private && ( + + )} + + {board.description} + +); + +const PasswordChangeForm = () => ( + <> +
+

Změnit heslo

+
+ + + + + + + +
+ + +); diff --git a/src/routes/mod/logs.tsx b/src/routes/mod/logs.tsx new file mode 100755 index 0000000..a70b9bb --- /dev/null +++ b/src/routes/mod/logs.tsx @@ -0,0 +1,35 @@ +import ModNav from "../../components/mod_nav"; +import Page from "../../components/page"; +import PageBody from "../../components/page_body"; +import PageHead from "../../components/page_head"; +import { getCtx, TemplateCtx } from "../../ctx"; +import { CzchanError } from "../../error"; +import { canUser, CzchanPerm } from "../../permissions"; +import { Request, Response } from "express"; + +export default async (req: Request, res: Response) => { + const ctx = await getCtx(req); + + if (!canUser(ctx.user, CzchanPerm.VIEW_LOGS)) { + throw new CzchanError("K tomuto nemáš oprávnění.", 403); + } + + const html = Template(ctx); + + res.send(html); +}; + +const Template = (ctx: TemplateCtx) => { + return ( + + + +

Záznamy

+

TBI

+
+ +
+
+
+ ); +}; diff --git a/src/routes/mod/news.tsx b/src/routes/mod/news.tsx new file mode 100755 index 0000000..2366ca1 --- /dev/null +++ b/src/routes/mod/news.tsx @@ -0,0 +1,124 @@ +import Datetime from "../../components/datetime"; +import { Form, FormField } from "../../components/form"; +import ModNav from "../../components/mod_nav"; +import Page from "../../components/page"; +import PageBody from "../../components/page_body"; +import PageHead from "../../components/page_head"; +import { getCtx, TemplateCtx } from "../../ctx"; +import { readNews } from "../../db/news"; +import { News } from "../../db/schema"; +import { CzchanError } from "../../error"; +import { canUser, CzchanPerm } from "../../permissions"; +import { PropsWithChildren } from "@kitajs/html"; +import { Request, Response } from "express"; + +export default async (req: Request, res: Response) => { + const ctx = await getCtx(req); + + if (!canUser(ctx.user, CzchanPerm.MANAGE_NEWS)) { + throw new CzchanError("K tomuto nemáš oprávnění.", 403); + } + + const news = await readNews(); + const html = Template(ctx, news); + + res.send(html); +}; + +const Template = (ctx: TemplateCtx, news: News[]) => { + return ( + + + +

Novinky

+
+ +
+
+ +
+ + + +
+
+
+ ); +}; + +const NewsTable = ({ + ctx, + news, +}: PropsWithChildren<{ ctx: TemplateCtx; news: News[] }>) => ( + + + + + + + + + + + + {news.map((news) => ( + + ))} + {news.length === 0 && ( + + + + )} + +
PředmětObsahVytvořilVyvtvořeny
+ Nic tady není... +
+); + +const NewsRow = ({ + ctx, + news, +}: PropsWithChildren<{ ctx: TemplateCtx; news: News }>) => ( + + + + + {news.subject} + {news.created_by} + + [Upravit] + + + + + +); + +const CreateForm = () => ( + <> +
+

Vytvořit

+
+ + + + + + + +
+ + +); diff --git a/src/routes/mod/permissions.tsx b/src/routes/mod/permissions.tsx new file mode 100644 index 0000000..ada4001 --- /dev/null +++ b/src/routes/mod/permissions.tsx @@ -0,0 +1,77 @@ +import { Form, FormField } from "../../components/form"; +import Page from "../../components/page"; +import PageBody from "../../components/page_body"; +import PageHead from "../../components/page_head"; +import { getCtx, TemplateCtx } from "../../ctx"; +import { User } from "../../db/schema"; +import { readUser } from "../../db/user"; +import { canUser, canUserActOn, CzchanPerm } from "../../permissions"; +import { PropsWithChildren } from "@kitajs/html"; +import { Request, Response } from "express"; + +export default async (req: Request, res: Response) => { + const ctx = await getCtx(req); + const user = await readUser(req.params.user); + const html = Template(ctx, user); + + res.send(html); +}; + +const Template = (ctx: TemplateCtx, user: User) => ( + + + +

+ Oprávnění ({user.username}) +

+ {user.rank === 0 && ( +

+ Tento uživatel je root (rank = 0), takže na něj nemají změny oprávnění + efekt. +

+ )} + +
+
+
+); + +const UpdateForm = ({ + ctx, + user, +}: PropsWithChildren<{ ctx: TemplateCtx; user: User }>) => ( + <> +
+
+ + + {Object.values(CzchanPerm).map((perm) => ( + + + + ))} +
+ + +); diff --git a/src/routes/mod/reports.tsx b/src/routes/mod/reports.tsx new file mode 100755 index 0000000..d30b19e --- /dev/null +++ b/src/routes/mod/reports.tsx @@ -0,0 +1,35 @@ +import ModNav from "../../components/mod_nav"; +import Page from "../../components/page"; +import PageBody from "../../components/page_body"; +import PageHead from "../../components/page_head"; +import { getCtx, TemplateCtx } from "../../ctx"; +import { CzchanError } from "../../error"; +import { canUser, CzchanPerm } from "../../permissions"; +import { Request, Response } from "express"; + +export default async (req: Request, res: Response) => { + const ctx = await getCtx(req); + + if (!canUser(ctx.user, CzchanPerm.MANAGE_REPORTS)) { + throw new CzchanError("K tomuto nemáš oprávnění.", 403); + } + + const html = Template(ctx); + + res.send(html); +}; + +const Template = (ctx: TemplateCtx) => { + return ( + + + +

Hlášení

+

TBI

+
+ +
+
+
+ ); +}; diff --git a/src/routes/mod/users.tsx b/src/routes/mod/users.tsx new file mode 100755 index 0000000..ff75af6 --- /dev/null +++ b/src/routes/mod/users.tsx @@ -0,0 +1,231 @@ +import Capcode from "../../components/capcode"; +import Datetime from "../../components/datetime"; +import { Form, FormField } from "../../components/form"; +import ModNav from "../../components/mod_nav"; +import Page from "../../components/page"; +import PageBody from "../../components/page_body"; +import PageHead from "../../components/page_head"; +import { getCtx, TemplateCtx } from "../../ctx"; +import { User } from "../../db/schema"; +import { readUsers } from "../../db/user"; +import { + canUser, + canUserActOn, + canUserApplyRole, + CzchanPerm, +} from "../../permissions"; +import { PropsWithChildren } from "@kitajs/html"; +import { Request, Response } from "express"; + +export default async (req: Request, res: Response) => { + const ctx = await getCtx(req); + const users = await readUsers(); + const html = Template(ctx, users); + + res.send(html); +}; + +const Template = (ctx: TemplateCtx, users: User[]) => { + return ( + + + +

Uživatelé

+
+ +
+
+ +
+ + {canUser(ctx.user, CzchanPerm.MANAGE_USERS) && ( + + )} + {(canUser(ctx.user, CzchanPerm.MANAGE_USERS) || + canUser(ctx.user, CzchanPerm.CUSTOM_CAPCODE)) && ( + + )} + {canUser(ctx.user, CzchanPerm.MANAGE_USERS) && } + + {canUser(ctx.user, CzchanPerm.MANAGE_USERS) && } +
+
+
+ ); +}; + +const UserTable = ({ + ctx, + users, +}: PropsWithChildren<{ ctx: TemplateCtx; users: User[] }>) => ( + + + + + + + + + + + + + {users.map((user) => ( + + ))} + {users.length === 0 && ( + + + + )} + +
JménoÚroveňCapcodeOprávněníVytvořen
+ Nic tady není... +
+); + +const UserRow = ({ + ctx, + user, +}: PropsWithChildren<{ ctx: TemplateCtx; user: User }>) => ( + + + {(() => { + const permissible = + (canUser(ctx.user, CzchanPerm.MANAGE_USERS) && + canUserActOn(ctx.user, user)) || + ctx.user?.username === user.username; + return ( + + ); + })()} + + {user.username} + + {user.rank} + {user.rank === 0 && ( + <> + {" "} + (root) + + )} + + + + + + [{user.permissions.length}]{" "} + [Zobrazit] + + + + + +); + +const UpdateRankForm = ({ ctx }: PropsWithChildren<{ ctx: TemplateCtx }>) => ( + <> +
+

Upravit úroveň

+
+ + + +
+ +); + +const UpdateCapcodeForm = ({ + ctx, +}: PropsWithChildren<{ ctx: TemplateCtx }>) => ( + <> +
+

Upravit capcode

+

Prázdná pole nebudou změněna.

+
+ + + + + + + + + +
+ +); + +const RoleForm = ({ ctx }: PropsWithChildren<{ ctx: TemplateCtx }>) => ( + <> +
+

Aplikovat roli

+
+ + + +
+ +); + +const CreateForm = ({ ctx }: PropsWithChildren<{ ctx: TemplateCtx }>) => ( + <> +
+

Vytvořit

+
+ + + + + + + + + + +
+ + +); + +const RoleList = ({ ctx }: PropsWithChildren<{ ctx: TemplateCtx }>) => ( + <> + {Object.keys(ctx.config.roles) + .filter((role) => { + const role_ = ctx.config.roles[role]; + return canUserApplyRole(ctx.user, role_); + }) + .map((role) => ( + + ))} + +); diff --git a/src/zod_schema.ts b/src/zod_schema.ts new file mode 100644 index 0000000..e53991e --- /dev/null +++ b/src/zod_schema.ts @@ -0,0 +1,184 @@ +import { CzchanPerm } from "./permissions"; +import z, { ZodType } from "zod"; + +// Fields + +const zHttpValue = z + .string() + .trim() + .transform((value) => (value === "" ? null : value)); + +const zRequiredField = >(type: T) => + z + .array(zHttpValue, "Bylo vynecháno povinné pole.") + .transform((arr) => arr[0] || null) + .pipe(z.string("Povinné pole nesmí být prázdné.").pipe(type)); + +const zOptionalField = >(type: T) => + z + .optional( + z + .array(zHttpValue) + .transform((arr) => arr[0] || null) + .pipe(z.nullable(type)), + ) + .transform((value) => (value === undefined ? null : value)); + +const zMultiField = >(type: T) => + z + .optional(z.array(zHttpValue.pipe(type))) + .transform((value) => (value === undefined ? [] : value)); + +const zUsername = z + .string() + .regex( + /^[\w]{1,32}$/, + "Uživatelské jméno musí mít 1-32 znaků a může obsahovat pouze čísla, písmena (A-Z) a podtržítka (_).", + ); + +const zPassword = z + .string() + .regex( + /^[^\s]{8,256}$/, + "Heslo musí mít 8-256 znaků a nemůže obsahovat mezery.", + ); + +const zRank = z.coerce + .number("Úroveň musí být číslo.") + .pipe( + z.int("Úroveň musí být celé číslo.").min(0, "Úroveň musí být větší než 0."), + ); + +const zCapcodeText = z + .string() + .max(32, "Capcode musí mít maximálně 32 znaků") + .refine( + (string) => !string.includes(":"), + "Capcode nesmí obsahovat dvojtečku (:), jelikož se používá jako oddělovač.", + ); + +const zCapcodeIcon = z + .string() + .max( + 32, + "Jméno ikony musí mít maximálně 32 znaků. Pokud jsi administrátor, uprav jméno ikony.", + ) + .refine( + (string) => !string.includes(":"), + "Capcode nesmí obsahovat dvojtečku (:), jelikož se používá jako oddělovač. Pokud jsi administrátor, uprav jméno ikony.", + ); + +const zColor = z + .string() + .regex( + /^(?:[A-Fa-f0-9]{3}){1,2}$/, + "Barva musí být trojmístný nebo šestimístný hexadecimální kód. (BEZ #)", + ); + +const zRole = z.string(); + +const zPermission = z.enum(CzchanPerm); + +const zBoardId = z + .string() + .regex( + /^[\w]{1,16}$/, + "ID nástěnky musí mít 1-16 znaků a může obsahovat pouze čísla, písmena (A-Z) a podtržítka (_).", + ); + +const zBoardName = z + .string() + .max(32, "Jméno nástěnky musí mít maximálně 32 znaků."); + +const zBoardDescription = z + .string() + .max(256, "Popis nástěnky musí mít maximálně 256 znaků."); + +// Form-specific schema + +const zUserAction = z.enum( + ["$delete_user", "$update_rank", "$update_capcode", "$apply_role"], + "Neplatná akce.", +); + +const zBoardAction = z.enum( + ["$delete_board", "$update_board"], + "Neplatná akce.", +); + +// Forms + +// /actions/login +const LoginForm = z.object({ + username: zRequiredField(zUsername), + password: zRequiredField(zPassword), +}); + +// /mod/actions/update_password +const UpdatePasswordForm = z.object({ + old_password: zRequiredField(zPassword), + new_password: zRequiredField(zPassword), +}); + +// /mod/actions/create_user +const CreateUserForm = z.object({ + new_username: zRequiredField(zUsername), + new_password: zRequiredField(zPassword), + new_role: zRequiredField(zRole), +}); + +// /mod/actions/user_actions +const UserActionsForm = z.object({ + users: zMultiField(zUsername), + action: zRequiredField(zUserAction), +}); + +const UpdateRankForm = z.object({ + rank: zRequiredField(zRank), +}); + +const UpdateCapcodeForm = z.object({ + capcode_text: zOptionalField(zCapcodeText), + capcode_color: zOptionalField(zColor), + capcode_icon: zOptionalField(zCapcodeIcon), +}); + +const ApplyRoleForm = z.object({ role: zRequiredField(zRole) }); + +// /mod/actions/create_board +const CreateBoardForm = z.object({ + id: zRequiredField(zBoardId), + name: zRequiredField(zBoardName), + description: zRequiredField(zBoardDescription), +}); + +// /mod/actions/board_actions +const BoardActionsForm = z.object({ + boards: zMultiField(zBoardId), + action: zRequiredField(zBoardAction), +}); + +const UpdateBoardForm = z.object({ + name: zOptionalField(zBoardName), + description: zOptionalField(zBoardDescription), +}); + +// /mod/actions/update_permissions +const UpdatePermissionsForm = z.object({ + user: zRequiredField(zUsername), + permissions: zMultiField(zPermission), +}); + +export { + LoginForm, + UpdatePasswordForm, + CreateUserForm, + UserActionsForm, + UpdateRankForm, + UpdateCapcodeForm, + ApplyRoleForm, + CreateBoardForm, + BoardActionsForm, + UpdateBoardForm, + UpdatePermissionsForm, +}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100755 index 0000000..6f4f679 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "NodeNext", + "rootDir": ".", + "outDir": "./dist", + "strict": true, + "moduleResolution": "NodeNext", + // Style + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "allowUnusedLabels": false, + "allowUnreachableCode": false, + // JSX + "jsx": "react-jsx", + "jsxImportSource": "@kitajs/html", + "plugins": [{ "name": "@kitajs/ts-html-plugin" }] + } +} diff --git a/web/styles/base.scss b/web/styles/base.scss new file mode 100755 index 0000000..0c6e679 --- /dev/null +++ b/web/styles/base.scss @@ -0,0 +1,145 @@ +body { + margin: 0; + overflow-wrap: break-word; +} + +input[type="text"], +input[type="password"], +input[type="number"], +input[type="file"], +button, +textarea, +select, +.input-wrapper { + padding: 4px; +} + +button { + cursor: pointer; +} + +header { + padding: 4px; +} + +header:after { + display: block; + clear: right; +} + +nav a { + text-decoration: none; +} + +footer, +.main { + margin: 8px; +} + +.user-options { + float: right; +} + +.container { + margin: 0 auto; + /* Gracefully degrades into just being full-width on ancient browsers */ + max-width: 960px; +} + +.logo { + margin: 0 auto; + width: 400px; + max-width: 100%; +} + +.subtitle { + font-weight: bold; +} + +.form-table { + /* 4px on the outside in total */ + border-spacing: 2px; + padding: 2px; +} + +.form-table th { + padding: 4px; + font-weight: bold; + text-align: left; +} + +.form-table td { + padding: 0; +} + +.data-table { + border-collapse: collapse; +} + +.data-table th { + text-align: left; +} + +.data-table th, +.data-table td { + padding: 4px; +} + +.infobox-head { + padding: 4px; + font-weight: bold; +} + +.infobox-body { + padding: 4px; +} + +.pagination { + display: inline-block; + padding: 8px; +} + +footer { + text-align: center; +} + +.inner-footer { + display: inline-block; + margin: 0 auto; + padding: 4px; +} + +/* Formatting */ + +.capcode { + font-weight: bold; +} + +.icon { + vertical-align: text-top; + height: 16px; +} + +/* Utility */ + +.clearfix:after { + display: block; + clear: both; + content: ""; +} + +.left { + text-align: left; +} + +.center { + text-align: center; +} + +.auto-center { + margin: 0 auto; +} + +.full-width { + width: 100%; +} diff --git a/web/styles/themes/yotsuba.scss b/web/styles/themes/yotsuba.scss new file mode 100755 index 0000000..aa31762 --- /dev/null +++ b/web/styles/themes/yotsuba.scss @@ -0,0 +1,113 @@ +$bg: #ffffee url("/public/img/theme/bg-yotsuba.png") top repeat-x; +$font: Arial, Helvetica, sans-serif; +$title-font: + Times New Roman, + Georgia, + serif; + +$text: #800000; +$link: #800000; +$link-hover: #dd0000; +$post-link: #000080; +$post-link-hover: #dd0000; +$link-list: #bb8866; +$title: #af0a0f; +$name: #117743; +$trip: #117743; +$capcode: #cc1105; + +$hr: #d9bfb7; +$table-head: #ffccaa; +$table-bg: #ffffff; +$table-border: #800000; +$input: #000000; +$input-bg: #ffffff; +$input-border: #808080; +$box-bg: #f0e0d6; +$box-border: #d9bfb7; +$hl-box-bg: #f0c0b0; +$hl-box-border: #d99f91; + +body { + background: $bg; + color: $text; + font-family: $font; +} + +a { + color: $link; +} + +a:hover { + color: $link-hover; +} + +hr { + border: none; + border-top: 1px solid $hr; +} + +header { + border-bottom: 1px solid $box-border; + background-color: $box-bg; + color: $link-list; +} + +.title { + color: $title; + font-family: $title-font; +} + +.form-table { + border-right: 1px solid $box-border; + border-bottom: 1px solid $box-border; + background-color: $box-bg; +} + +.form-table th, +.data-table th { + border: 1px solid $table-border; + background-color: $table-head; +} + +input[type="text"], +input[type="password"], +input[type="number"], +input[type="file"], +button, +textarea, +select, +.input-wrapper { + border: 1px solid $input-border; + background-color: $input-bg; + color: $input; +} + +.data-table { + background-color: $table-bg; +} + +.data-table td { + border: 1px solid $table-border; +} + +.infobox { + border: 1px solid $table-border; + background-color: $table-bg; +} + +.infobox-head { + background-color: $table-head; +} + +.pagination { + border-right: 1px solid $box-border; + border-bottom: 1px solid $box-border; + background-color: $box-bg; +} + +.inner-footer { + border-right: 1px solid $box-border; + border-bottom: 1px solid $box-border; + background-color: $box-bg; +}