aboutsummaryrefslogtreecommitdiffstats
path: root/edaweb
diff options
context:
space:
mode:
Diffstat (limited to 'edaweb')
-rw-r--r--edaweb/app.py256
-rw-r--r--edaweb/cache.py18
-rw-r--r--edaweb/database.py248
-rw-r--r--edaweb/downloader.py54
-rw-r--r--edaweb/parser.py209
-rw-r--r--edaweb/services.py369
-rw-r--r--edaweb/static/images/0don0t4ofuc41-3776935852.jpgbin0 -> 131290 bytes
-rw-r--r--edaweb/static/images/1544552064683.jpgbin0 -> 171824 bytes
-rw-r--r--edaweb/static/images/1549844950404.jpgbin0 -> 112071 bytes
-rw-r--r--edaweb/static/images/1555824429083.pngbin0 -> 170252 bytes
-rw-r--r--edaweb/static/images/1555824491105.pngbin0 -> 177365 bytes
-rw-r--r--edaweb/static/images/1671540582317176.pngbin0 -> 3276156 bytes
-rw-r--r--edaweb/static/images/1urouter.jpgbin0 -> 183429 bytes
-rw-r--r--edaweb/static/images/20220401_222149.jpgbin0 -> 38897 bytes
-rw-r--r--edaweb/static/images/324162a23865a6a7e75761871d29935314cbc2b3.jpgbin0 -> 531668 bytes
-rw-r--r--edaweb/static/images/3502e26d0181c684bc17b56188bde6e80569d191_full.jpgbin0 -> 13920 bytes
-rw-r--r--edaweb/static/images/5600x.jpgbin0 -> 151209 bytes
-rw-r--r--edaweb/static/images/9400t.jpgbin0 -> 2603012 bytes
-rw-r--r--edaweb/static/images/9400t_cooler.jpgbin0 -> 4997396 bytes
-rw-r--r--edaweb/static/images/E1NlPc1X0AEkB-s.pngbin0 -> 3029 bytes
-rw-r--r--edaweb/static/images/E3xdm-hWYAEADNx.jpgbin0 -> 13609 bytes
-rw-r--r--edaweb/static/images/E4WhXAvWYAIFwEa.pngbin0 -> 10560 bytes
-rw-r--r--edaweb/static/images/E4cjVaPXEAU83U0.pngbin0 -> 15418 bytes
-rw-r--r--edaweb/static/images/E7ODIBeX0AAivRV.jpgbin0 -> 75338 bytes
-rw-r--r--edaweb/static/images/E7ODIZEWUAMQjty.jpgbin0 -> 94889 bytes
-rw-r--r--edaweb/static/images/E7ODIv0WUAUp-ad.jpgbin0 -> 88028 bytes
-rw-r--r--edaweb/static/images/E7ODJELX0AAVqna.jpgbin0 -> 107275 bytes
-rw-r--r--edaweb/static/images/EbmVCYKWkAAwXKW.jpgbin0 -> 34657 bytes
-rw-r--r--edaweb/static/images/Ffi1ducXEAArXDg.jpgbin0 -> 18055 bytes
-rw-r--r--edaweb/static/images/GGBDzuSXMAA1Ktk.jpgbin0 -> 65355 bytes
-rw-r--r--edaweb/static/images/GSxGe_rXQAAgEhj.jpgbin0 -> 1443057 bytes
-rw-r--r--edaweb/static/images/GTq-GOgWQAErvCp.jpgbin0 -> 376374 bytes
-rwxr-xr-xedaweb/static/images/GZhgzaK.pngbin0 -> 124272 bytes
-rw-r--r--edaweb/static/images/GcyexeCW0AAYssz.jpgbin0 -> 992976 bytes
-rwxr-xr-xedaweb/static/images/GegzRla.pngbin0 -> 44635 bytes
-rw-r--r--edaweb/static/images/IMG_-g4wqat.jpgbin0 -> 82371 bytes
-rw-r--r--edaweb/static/images/IMG_1602.jpgbin0 -> 24762 bytes
-rw-r--r--edaweb/static/images/IMG_1603.jpgbin0 -> 26478 bytes
-rw-r--r--edaweb/static/images/IMG_1604.jpgbin0 -> 35792 bytes
-rw-r--r--edaweb/static/images/IMG_1605.jpgbin0 -> 33782 bytes
-rw-r--r--edaweb/static/images/IMG_1606.jpgbin0 -> 34771 bytes
-rw-r--r--edaweb/static/images/IMG_1607.jpgbin0 -> 39764 bytes
-rw-r--r--edaweb/static/images/IMG_1609.jpgbin0 -> 48287 bytes
-rw-r--r--edaweb/static/images/IMG_1610.jpgbin0 -> 41368 bytes
-rw-r--r--edaweb/static/images/IMG_1611.jpgbin0 -> 24732 bytes
-rw-r--r--edaweb/static/images/IMG_1612.jpgbin0 -> 26570 bytes
-rw-r--r--edaweb/static/images/IMG_1613.pngbin0 -> 2339298 bytes
-rw-r--r--edaweb/static/images/IMG_1614.pngbin0 -> 317510 bytes
-rw-r--r--edaweb/static/images/IMG_1615.jpgbin0 -> 27792 bytes
-rw-r--r--edaweb/static/images/IMG_1616.jpgbin0 -> 43497 bytes
-rw-r--r--edaweb/static/images/IMG_1617.jpgbin0 -> 11756 bytes
-rw-r--r--edaweb/static/images/IMG_1618.jpgbin0 -> 21924 bytes
-rw-r--r--edaweb/static/images/IMG_1619.jpgbin0 -> 10839 bytes
-rw-r--r--edaweb/static/images/IMG_1620.jpgbin0 -> 25630 bytes
-rw-r--r--edaweb/static/images/IMG_1621.jpgbin0 -> 246181 bytes
-rw-r--r--edaweb/static/images/IMG_1622.jpgbin0 -> 163205 bytes
-rw-r--r--edaweb/static/images/IMG_1623.jpgbin0 -> 65738 bytes
-rw-r--r--edaweb/static/images/IMG_1624.pngbin0 -> 337723 bytes
-rw-r--r--edaweb/static/images/IMG_1625.jpgbin0 -> 31273 bytes
-rw-r--r--edaweb/static/images/IMG_1626.jpgbin0 -> 28916 bytes
-rw-r--r--edaweb/static/images/IMG_1627.jpgbin0 -> 33325 bytes
-rw-r--r--edaweb/static/images/IMG_1628.jpgbin0 -> 21475 bytes
-rw-r--r--edaweb/static/images/IMG_1629.jpgbin0 -> 13153 bytes
-rw-r--r--edaweb/static/images/IMG_1630.jpgbin0 -> 33638 bytes
-rw-r--r--edaweb/static/images/IMG_1632.jpgbin0 -> 52252 bytes
-rw-r--r--edaweb/static/images/IMG_1633.jpgbin0 -> 32028 bytes
-rw-r--r--edaweb/static/images/IMG_1636.jpgbin0 -> 45916 bytes
-rw-r--r--edaweb/static/images/IMG_1637.jpgbin0 -> 13999 bytes
-rw-r--r--edaweb/static/images/IMG_1638.jpgbin0 -> 79337 bytes
-rw-r--r--edaweb/static/images/IMG_1639.jpgbin0 -> 130710 bytes
-rw-r--r--edaweb/static/images/IMG_1640.jpgbin0 -> 77986 bytes
-rw-r--r--edaweb/static/images/IMG_1641.jpgbin0 -> 41247 bytes
-rw-r--r--edaweb/static/images/IMG_20210422_212009.jpgbin0 -> 2976182 bytes
-rw-r--r--edaweb/static/images/IMG_20210824_175000.jpgbin0 -> 3292970 bytes
-rw-r--r--edaweb/static/images/IMG_20220809_172130442_HDR.jpgbin0 -> 2715100 bytes
-rw-r--r--edaweb/static/images/IMG_20220811_133224877_HDR.jpgbin0 -> 3336994 bytes
-rw-r--r--edaweb/static/images/IMG_20220811_133224877_HDR_crop.jpgbin0 -> 2168764 bytes
-rw-r--r--edaweb/static/images/IMG_20220812_112838125_HDR.jpgbin0 -> 3440299 bytes
-rw-r--r--edaweb/static/images/IMG_20220823_154457137_HDR.jpgbin0 -> 3971263 bytes
-rw-r--r--edaweb/static/images/IMG_20221023_163821542.jpgbin0 -> 1461946 bytes
-rw-r--r--edaweb/static/images/IMG_20230812_132400733_HDR.jpgbin0 -> 3044563 bytes
-rw-r--r--edaweb/static/images/IMG_20230812_132632784_HDR.jpgbin0 -> 3452950 bytes
-rwxr-xr-xedaweb/static/images/JwL2S2V.pngbin0 -> 120751 bytes
-rw-r--r--edaweb/static/images/PXL_20250314_150055804.jpgbin0 -> 1835356 bytes
-rw-r--r--edaweb/static/images/Screenshot_20210520-221830.pngbin0 -> 275641 bytes
-rw-r--r--edaweb/static/images/Screenshot_20210527_122432.pngbin0 -> 1683456 bytes
-rw-r--r--edaweb/static/images/Screenshot_20210620-144127.pngbin0 -> 142673 bytes
-rw-r--r--edaweb/static/images/Screenshot_20210620-231652.pngbin0 -> 133513 bytes
-rw-r--r--edaweb/static/images/Screenshot_20210727-072303.pngbin0 -> 138225 bytes
-rw-r--r--edaweb/static/images/Screenshot_20210727-072344.pngbin0 -> 129039 bytes
-rw-r--r--edaweb/static/images/Screenshot_20210727-223004.pngbin0 -> 136574 bytes
-rw-r--r--edaweb/static/images/Screenshot_20210731-181223.pngbin0 -> 359217 bytes
-rw-r--r--edaweb/static/images/Screenshot_20210731-181229.pngbin0 -> 152242 bytes
-rw-r--r--edaweb/static/images/Screenshot_20210811-125853.pngbin0 -> 129411 bytes
-rw-r--r--edaweb/static/images/Screenshot_20210818-203828.pngbin0 -> 112321 bytes
-rw-r--r--edaweb/static/images/Screenshot_20210825-131938.pngbin0 -> 120763 bytes
-rw-r--r--edaweb/static/images/Startech.jpgbin0 -> 456042 bytes
-rw-r--r--edaweb/static/images/a4-5000.jpgbin0 -> 6252 bytes
-rwxr-xr-xedaweb/static/images/aLvcFjj.pngbin0 -> 23224 bytes
-rw-r--r--edaweb/static/images/aaaaahh.jpgbin0 -> 52325 bytes
-rw-r--r--edaweb/static/images/anti_nft_b.pngbin0 -> 2311 bytes
-rw-r--r--edaweb/static/images/arenas.jpgbin0 -> 294229 bytes
-rw-r--r--edaweb/static/images/basil.jpgbin0 -> 6068 bytes
-rw-r--r--edaweb/static/images/bigshark.pngbin0 -> 2355827 bytes
-rw-r--r--edaweb/static/images/bob.gifbin0 -> 142993 bytes
-rw-r--r--edaweb/static/images/braindamage.pngbin0 -> 22441 bytes
-rw-r--r--edaweb/static/images/catonlap.jpgbin0 -> 68382 bytes
-rw-r--r--edaweb/static/images/chuddy.jpgbin0 -> 78447 bytes
-rw-r--r--edaweb/static/images/cloudfree.pngbin0 -> 3080 bytes
-rw-r--r--edaweb/static/images/embedded_img2.pngbin0 -> 8613 bytes
-rw-r--r--edaweb/static/images/embedded_img3.pngbin0 -> 18291 bytes
-rw-r--r--edaweb/static/images/embedded_img4.pngbin0 -> 16801 bytes
-rw-r--r--edaweb/static/images/embedded_img5.PNGbin0 -> 11374 bytes
-rw-r--r--edaweb/static/images/face.jpgbin0 -> 226207 bytes
-rw-r--r--edaweb/static/images/first_server.jpgbin0 -> 2700813 bytes
-rw-r--r--edaweb/static/images/fstab.pngbin0 -> 88618 bytes
-rw-r--r--edaweb/static/images/graduation.jpgbin0 -> 290228 bytes
-rw-r--r--edaweb/static/images/greenboi.jpgbin0 -> 17079 bytes
-rw-r--r--edaweb/static/images/hpkvm_puttyconf.pngbin0 -> 17830 bytes
-rw-r--r--edaweb/static/images/hpkvm_serialconf.pngbin0 -> 13801 bytes
-rw-r--r--edaweb/static/images/i5-1145G7.pngbin0 -> 118559 bytes
-rwxr-xr-xedaweb/static/images/iKVCEoy.pngbin0 -> 97879 bytes
-rw-r--r--edaweb/static/images/icons.pngbin0 -> 46132 bytes
-rw-r--r--edaweb/static/images/ioporn.jpgbin0 -> 51451 bytes
-rw-r--r--edaweb/static/images/its_true_1.jpgbin0 -> 71857 bytes
-rw-r--r--edaweb/static/images/its_true_2.jpgbin0 -> 109263 bytes
-rw-r--r--edaweb/static/images/j1800.jpgbin0 -> 1589479 bytes
-rw-r--r--edaweb/static/images/j5040.jpgbin0 -> 3220569 bytes
-rw-r--r--edaweb/static/images/j5040_server.jpgbin0 -> 2767061 bytes
-rw-r--r--edaweb/static/images/kvm_1.pngbin0 -> 60072 bytes
-rw-r--r--edaweb/static/images/kvm_2.pngbin0 -> 150115 bytes
-rw-r--r--edaweb/static/images/lackrack1.jpgbin0 -> 335971 bytes
-rw-r--r--edaweb/static/images/lackrack2.jpgbin0 -> 381379 bytes
-rw-r--r--edaweb/static/images/libreboot.jpgbin0 -> 1910228 bytes
-rw-r--r--edaweb/static/images/liostore.pngbin0 -> 691184 bytes
-rw-r--r--edaweb/static/images/media_E9lTIldWUAEeT63.jpgbin0 -> 180897 bytes
-rw-r--r--edaweb/static/images/media_FPus_2aXsAUhCu9.jpgbin0 -> 480286 bytes
-rw-r--r--edaweb/static/images/media_FRlRqYKXoAIw82O.jpgbin0 -> 55076 bytes
-rw-r--r--edaweb/static/images/media_FeP_m8FXEAAqcJs.jpgbin0 -> 279374 bytes
-rw-r--r--edaweb/static/images/media_FfcNNZ2WYAAz97v.jpgbin0 -> 377136 bytes
-rw-r--r--edaweb/static/images/media_FlBsMq2XwAATJX5.jpgbin0 -> 885129 bytes
-rw-r--r--edaweb/static/images/media_FlBsfDcWQAQhJdP.jpgbin0 -> 660235 bytes
-rw-r--r--edaweb/static/images/minecraft.pngbin0 -> 27516 bytes
-rw-r--r--edaweb/static/images/new_switch_opened.jpgbin0 -> 4348235 bytes
-rw-r--r--edaweb/static/images/newdiscord.jpgbin0 -> 56309 bytes
-rw-r--r--edaweb/static/images/nicfan.jpgbin0 -> 2593649 bytes
-rw-r--r--edaweb/static/images/notstolenvalour4.pngbin0 -> 1752598 bytes
-rw-r--r--edaweb/static/images/notstolenvalour5.pngbin0 -> 1703482 bytes
-rw-r--r--edaweb/static/images/notstolenvalour8.pngbin0 -> 1706779 bytes
-rw-r--r--edaweb/static/images/oldpic.pngbin0 -> 938082 bytes
-rw-r--r--edaweb/static/images/pfsenseswissvpnportforwards.pngbin0 -> 118254 bytes
-rw-r--r--edaweb/static/images/photo_2022-12-07_14-06-03.jpgbin0 -> 12097 bytes
-rw-r--r--edaweb/static/images/photo_5857106224099739202_y.jpgbin0 -> 124721 bytes
-rw-r--r--edaweb/static/images/powerani.gifbin0 -> 7203 bytes
-rw-r--r--edaweb/static/images/profile_images_1598652418580963328_ENk7xKDw.jpgbin0 -> 42497 bytes
-rwxr-xr-xedaweb/static/images/rOJXmjG.pngbin0 -> 101799 bytes
-rw-r--r--edaweb/static/images/rack1.jpgbin0 -> 210052 bytes
-rw-r--r--edaweb/static/images/rack2.jpgbin0 -> 190057 bytes
-rw-r--r--edaweb/static/images/router.jpgbin0 -> 694385 bytes
-rw-r--r--edaweb/static/images/russian_isp.pngbin0 -> 52664 bytes
-rw-r--r--edaweb/static/images/russian_isp_abuse.pngbin0 -> 52880 bytes
-rw-r--r--edaweb/static/images/russian_nginx_logs.jpgbin0 -> 224155 bytes
-rw-r--r--edaweb/static/images/russian_yt.jpgbin0 -> 130618 bytes
-rw-r--r--edaweb/static/images/s-l1600.jpgbin0 -> 152914 bytes
-rw-r--r--edaweb/static/images/s-l16001.jpgbin0 -> 226604 bytes
-rw-r--r--edaweb/static/images/s-l500.jpgbin0 -> 31862 bytes
-rw-r--r--edaweb/static/images/sample_ede336afca555579dd78f051e4a23feaa838716d.jpgbin0 -> 185850 bytes
-rw-r--r--edaweb/static/images/selfie.jpgbin0 -> 73451 bytes
-rw-r--r--edaweb/static/images/server.jpgbin0 -> 2578909 bytes
-rw-r--r--edaweb/static/images/server2.jpgbin0 -> 2877532 bytes
-rw-r--r--edaweb/static/images/shark1.jpgbin0 -> 449065 bytes
-rw-r--r--edaweb/static/images/shark2.jpgbin0 -> 433903 bytes
-rw-r--r--edaweb/static/images/shark3.jpgbin0 -> 518122 bytes
-rw-r--r--edaweb/static/images/shark3.pngbin0 -> 2944760 bytes
-rw-r--r--edaweb/static/images/shun-hashimoto-mio-chibana.gifbin0 -> 1472535 bytes
-rw-r--r--edaweb/static/images/sicp.jpgbin0 -> 3664911 bytes
-rw-r--r--edaweb/static/images/startech_rack_1.jpgbin0 -> 3083071 bytes
-rw-r--r--edaweb/static/images/startech_rack_2.jpgbin0 -> 2093105 bytes
-rw-r--r--edaweb/static/images/stolenvalour.jpgbin0 -> 234621 bytes
-rw-r--r--edaweb/static/images/sun.gifbin0 -> 3097 bytes
-rw-r--r--edaweb/static/images/switch.jpgbin0 -> 90618 bytes
-rw-r--r--edaweb/static/images/switches.jpgbin0 -> 3169089 bytes
-rw-r--r--edaweb/static/images/t30.jpgbin0 -> 235413 bytes
-rw-r--r--edaweb/static/images/techdome.jpgbin0 -> 125311 bytes
-rw-r--r--edaweb/static/images/telegrampic.jpgbin0 -> 138371 bytes
-rw-r--r--edaweb/static/images/telegrampic2.jpgbin0 -> 25752 bytes
-rw-r--r--edaweb/static/images/testimonials.PNGbin0 -> 190391 bytes
-rw-r--r--edaweb/static/images/theNVMEVault.pngbin0 -> 51977 bytes
-rw-r--r--edaweb/static/images/twitterpic.jpgbin0 -> 14667 bytes
-rwxr-xr-xedaweb/static/images/v2Yfoou.jpgbin0 -> 165128 bytes
-rw-r--r--edaweb/static/images/vcss-blue.gifbin0 -> 1759 bytes
-rw-r--r--edaweb/static/images/www.gifbin0 -> 1531 bytes
-rwxr-xr-xedaweb/static/images/wxbKYVv.pngbin0 -> 123728 bytes
-rw-r--r--edaweb/static/images/x200.jpgbin0 -> 2732895 bytes
-rw-r--r--edaweb/static/images/xeon.jpgbin0 -> 571574 bytes
-rw-r--r--edaweb/static/images/xeon_motherboard.jpgbin0 -> 757343 bytes
-rw-r--r--edaweb/static/index.md38
-rw-r--r--edaweb/static/papers/aai.pdfbin0 -> 311217 bytes
-rw-r--r--edaweb/static/papers/ar2.pdfbin0 -> 289686 bytes
-rw-r--r--edaweb/static/robots.txt7
-rw-r--r--edaweb/static/style.css211
-rw-r--r--edaweb/templates/diary.html.j226
-rw-r--r--edaweb/templates/discord.html.j25
-rw-r--r--edaweb/templates/index.html.j224
-rw-r--r--edaweb/templates/isocd.html.j232
-rw-r--r--edaweb/templates/isocd_confirmation.html.j216
-rw-r--r--edaweb/templates/nhdl.html.j28
-rw-r--r--edaweb/templates/questions.html.j217
-rw-r--r--edaweb/templates/random.html.j222
-rw-r--r--edaweb/templates/services.html.j259
-rw-r--r--edaweb/templates/template.html.j276
-rw-r--r--edaweb/templates/thought.html.j229
-rw-r--r--edaweb/templates/thoughts.html.j212
213 files changed, 1736 insertions, 0 deletions
diff --git a/edaweb/app.py b/edaweb/app.py
new file mode 100644
index 0000000..436df7a
--- /dev/null
+++ b/edaweb/app.py
@@ -0,0 +1,256 @@
+from paste.translogger import TransLogger
+from waitress import serve
+from PIL import Image
+import configparser
+import transmission_rpc
+import downloader
+import datetime
+import database
+import services
+import urllib
+import random
+import parser
+import flask
+import sys
+import os
+import io
+
+app = flask.Flask(__name__)
+CONFIG = configparser.ConfigParser(interpolation = None)
+CONFIG.read(os.path.join(os.path.dirname(__file__), "edaweb.conf"))
+shown_images = set()
+shown_sidebar_images = set()
+
+def get_pfp_img(db:database.Database):
+ global shown_images
+ dbimg = db.get_pfp_images()
+ if len(shown_images) == len(dbimg):
+ shown_images = set()
+ folder = set(dbimg).difference(shown_images)
+ choice = random.choice(list(folder))
+ shown_images.add(choice)
+ return choice
+
+def get_sidebar_img(db:database.Database):
+ global shown_sidebar_images
+ dbimg = db.get_sidebar_images()
+ if len(shown_sidebar_images) == len(dbimg):
+ shown_sidebar_images = set()
+ folder = set(dbimg).difference(shown_sidebar_images)
+ choice = random.choice(list(folder))
+ shown_sidebar_images.add(choice)
+ return choice
+
+def get_correct_article_headers(db:database.Database, title):
+ db_headers = list(db.get_header_articles())
+ if title in [i[0] for i in db_headers]:
+ out = []
+ for i in db_headers:
+ if i[0] != title:
+ out.append(i)
+ return out + [("index", "/~")]
+ else:
+ return db_headers + [("index", "/~")]
+
+def get_template_items(title, db):
+ return {
+ "links": db.get_header_links(),
+ "image": get_pfp_img(db),
+ "title": title,
+ "articles": get_correct_article_headers(db, title)
+ }
+
+@app.route("/")
+@app.route("/~")
+def index():
+ with database.Database() as db:
+ with open(os.path.join(os.path.dirname(__file__), "static", "index.md"), "r") as f:
+ return flask.render_template(
+ "index.html.j2",
+ **get_template_items("eden's site :3", db),
+ days_till_ffs = datetime.datetime(2025, 11, 8) - datetime.datetime.now(),
+ markdown = parser.parse_text(f.read())[0],
+ featured_thoughts = db.get_featured_thoughts(),
+ commits = services.get_recent_commits(db)[:15],
+ sidebar_img = get_sidebar_img(db)
+ )
+
+@app.route("/robots.txt")
+def robots():
+ return flask.send_from_directory("static", "robots.txt")
+
+@app.route("/services")
+def serve_services():
+ with database.Database() as db:
+ return flask.render_template(
+ "services.html.j2",
+ **get_template_items("services", db),
+ docker = services.get_all_docker_containers(),
+ trans = services.get_torrent_stats(),
+ pihole = services.get_pihole_stats()
+ )
+
+@app.route("/discord")
+def discord():
+ with database.Database() as db:
+ return flask.render_template(
+ "discord.html.j2",
+ **get_template_items("discord", db),
+ discord = CONFIG["discord"]["username"]
+ )
+
+@app.route("/thought")
+def get_thought():
+ thought_id = flask.request.args.get("id", type=int)
+ with database.Database() as db:
+ try:
+ category_name, title, dt, parsed, headers, redirect = parser.get_thought_from_id(db, thought_id)
+ # print(headers)
+ except TypeError:
+ flask.abort(404)
+ return
+
+ if redirect is not None:
+ return flask.redirect(redirect, code = 301)
+
+ return flask.render_template(
+ "thought.html.j2",
+ **get_template_items(title, db),
+ md_html = parsed,
+ contents_html = headers,
+ dt = "published: " + str(dt),
+ category = category_name,
+ othercategories = db.get_categories_not(category_name),
+ related = db.get_similar_thoughts(category_name, thought_id)
+ )
+
+@app.route("/thoughts")
+def get_thoughts():
+ with database.Database() as db:
+ all_ = db.get_all_thoughts()
+ tree = {}
+ for id_, title, dt, category in all_:
+ if category not in tree.keys():
+ tree[category] = [(id_, title, dt)]
+ else:
+ tree[category].append((id_, title, str(dt)))
+
+ return flask.render_template(
+ "thoughts.html.j2",
+ **get_template_items("thoughts", db),
+ tree = tree
+ )
+
+@app.route("/img/<filename>")
+def serve_image(filename):
+ imdirpath = os.path.join(os.path.dirname(__file__), "static", "images")
+ if filename in os.listdir(imdirpath):
+ try:
+ w = int(flask.request.args['w'])
+ h = int(flask.request.args['h'])
+ except (KeyError, ValueError):
+ return flask.send_from_directory(imdirpath, filename)
+
+ img = Image.open(os.path.join(imdirpath, filename))
+ img.thumbnail((w, h), Image.LANCZOS)
+ io_ = io.BytesIO()
+ img.save(io_, format='JPEG')
+ return flask.Response(io_.getvalue(), mimetype='image/jpeg')
+ else:
+ flask.abort(404)
+
+@app.route("/nhdl")
+def serve_nhdl():
+ with database.Database() as db:
+ try:
+ nhentai_id = int(flask.request.args["id"])
+ with downloader.CompressedImages(nhentai_id) as zippath:
+ # return app.send_static_file(os.path.split(zippath)[-1])
+ return flask.redirect("/zip/%s" % os.path.split(zippath)[-1])
+
+ except (KeyError, ValueError):
+ return flask.render_template(
+ "nhdl.html.j2",
+ **get_template_items("Hentai Downloader", db)
+ )
+
+@app.route("/isocd")
+def serve_iso_form():
+ with database.Database() as db:
+ return flask.render_template(
+ "isocd.html.j2",
+ **get_template_items("Get a GNU/Linux install CD", db),
+ iso_options = db.get_iso_cd_options()
+ )
+
+@app.route("/zip/<zipfile>")
+def serve_zip(zipfile):
+ return flask.send_from_directory(os.path.join(os.path.dirname(__file__), "static", "zips"), zipfile)
+
+@app.route("/pdf/<pdfname>")
+def serve_pdf(pdfname):
+ return flask.send_from_directory(os.path.join(os.path.dirname(__file__), "static", "papers"), pdfname)
+
+@app.route("/nhdlredirect", methods = ["POST"])
+def redirect_nhdl():
+ req = dict(flask.request.form)
+ try:
+ return flask.redirect("/nhdl?id=%i" % int(req["number_input"]))
+ except (TypeError, ValueError, KeyError):
+ flask.abort(400)
+
+@app.route("/getisocd", methods = ["POST"])
+def get_iso_cd():
+ req = dict(flask.request.form)
+ print(req)
+ with database.Database() as db:
+ id_ = db.append_cd_orders(**req)
+ print(id_)
+ return flask.render_template(
+ "isocd_confirmation.html.j2",
+ **get_template_items("Get a GNU/Linux install CD", db),
+ email = req["email"],
+ req = req,
+ id_ = id_
+ )
+
+@app.route("/random")
+def serve_random():
+ try:
+ tags = flask.request.args['tags'].split(" ")
+ except KeyError:
+ flask.abort(400)
+
+ sbi = services.get_random_image(tags)
+ req = urllib.request.Request(sbi.imurl)
+ mediaContent = urllib.request.urlopen(req).read()
+ with open(os.path.join(os.path.dirname(__file__), "static", "images", "random.jpg"), "wb") as f:
+ f.write(mediaContent)
+
+ with database.Database() as db:
+ return flask.render_template(
+ "random.html.j2",
+ **get_template_items("random image", db),
+ sbi = sbi,
+ localimg = "/img/random.jpg?seed=%i" % random.randint(0, 9999)
+ )
+
+@app.route("/questions")
+def serve_questions():
+ with database.Database() as db:
+ return flask.render_template(
+ "questions.html.j2",
+ **get_template_items("questions and answers", db),
+ qnas_link = CONFIG.get("qnas", "url"),
+ qnas = db.get_qnas()
+ )
+
+if __name__ == "__main__":
+ try:
+ if sys.argv[1] == "--production":
+ #serve(TransLogger(app), host='127.0.0.1', port = 6969)
+ serve(TransLogger(app), host='0.0.0.0', port = 6969, threads = 32)
+ else:
+ app.run(host = "0.0.0.0", port = 5001, debug = True)
+ except IndexError:
+ app.run(host = "0.0.0.0", port = 5001, debug = True)
diff --git a/edaweb/cache.py b/edaweb/cache.py
new file mode 100644
index 0000000..5b66e43
--- /dev/null
+++ b/edaweb/cache.py
@@ -0,0 +1,18 @@
+import database
+import services
+
+def update_cache():
+ print("Updating cache...")
+ with database.Database() as db:
+ db.update_commit_cache(services.request_recent_commits(since = db.get_last_commit_time()))
+ print("Finished adding github commits...")
+ db.append_qnas(services.scrape_whispa(db.config.get("qnas", "url"), since = db.get_oldest_qna()))
+ print("Finished parsing Q&As...")
+
+ print("Started getting docker information with SSH...")
+ print(services.cache_all_docker_containers(services.CONFIG.get("ssh", "docker_key_path")))
+ print("Finished caching.")
+
+
+if __name__ == "__main__":
+ update_cache() \ No newline at end of file
diff --git a/edaweb/database.py b/edaweb/database.py
new file mode 100644
index 0000000..49ec33c
--- /dev/null
+++ b/edaweb/database.py
@@ -0,0 +1,248 @@
+from urllib.parse import urlparse
+from dataclasses import dataclass
+from lxml import html
+import configparser
+import threading
+import services
+import operator
+import datetime
+import requests
+import twython
+import pymysql
+import random
+import os
+import re
+
+@dataclass
+class Database:
+ safeLogin:bool = True #automatically login with the user in the config file, who is read only
+ user:str = None #otherwise, login with the given username and passwd
+ passwd:str = None
+
+ def __enter__(self):
+ self.config = configparser.ConfigParser(interpolation = None)
+ self.config.read(os.path.join(os.path.dirname(__file__), "edaweb.conf"))
+
+ if self.safeLogin:
+ self.__connection = pymysql.connect(
+ **self.config["mysql"],
+ charset = "utf8mb4"
+ )
+ else:
+ self.__connection = pymysql.connect(
+ user = self.user,
+ passwd = self.passwd,
+ host = self.config["mysql"]["host"],
+ db = self.config["mysql"]["db"],
+ charset = "utf8mb4"
+ )
+ return self
+
+ def __exit__(self, type, value, traceback):
+ self.__connection.commit()
+ self.__connection.close()
+
+ def get_header_links(self):
+ with self.__connection.cursor() as cursor:
+ cursor.execute("SELECT name, link FROM headerLinks WHERE display = true ORDER BY name;")
+ return cursor.fetchall()
+
+ def get_image(self, imageName):
+ with self.__connection.cursor() as cursor:
+ cursor.execute("SELECT alt, url FROM images WHERE imageName = %s;", (imageName, ))
+ return cursor.fetchone()
+
+ def get_pfp_images(self):
+ with self.__connection.cursor() as cursor:
+ cursor.execute("SELECT alt, url FROM images WHERE pfp_img = 1;")
+ return cursor.fetchall()
+
+ def get_sidebar_images(self):
+ with self.__connection.cursor() as cursor:
+ cursor.execute("SELECT alt, url FROM images WHERE sidebar_image = 1;")
+ return cursor.fetchall()
+
+ def get_header_articles(self):
+ with self.__connection.cursor() as cursor:
+ cursor.execute("SELECT articleName, link FROM headerArticles WHERE display = true;")
+ return cursor.fetchall()
+
+ def get_all_categories(self):
+ with self.__connection.cursor() as cursor:
+ cursor.execute("SELECT category_name FROM categories;")
+ return [i[0] for i in cursor.fetchall()]
+
+ def add_category(self, category):
+ if not category in self.get_all_categories():
+ with self.__connection.cursor() as cursor:
+ cursor.execute("INSERT INTO categories (category_name) VALUES (%s);", (category, ))
+
+ self.__connection.commit()
+ return True
+
+ return False
+
+ def add_thought(self, category, title, markdown):
+ with self.__connection.cursor() as cursor:
+ cursor.execute("""
+ INSERT INTO thoughts (category_id, title, markdown_text)
+ VALUES ((
+ SELECT category_id FROM categories WHERE category_name = %s
+ ), %s, %s);""", (category, title, markdown))
+ self.__connection.commit()
+
+ def get_thought(self, id_):
+ with self.__connection.cursor() as cursor:
+ cursor.execute("""
+ SELECT categories.category_name, thoughts.title, thoughts.dt, thoughts.markdown_text, thoughts.redirect
+ FROM thoughts INNER JOIN categories
+ ON thoughts.category_id = categories.category_id
+ WHERE thought_id = %s;""", (id_, ))
+ return cursor.fetchone()
+
+ def get_similar_thoughts(self, category, id_):
+ with self.__connection.cursor() as cursor:
+ cursor.execute("""
+ SELECT thought_id, title, dt, category_name FROM thoughts
+ INNER JOIN categories ON thoughts.category_id = categories.category_id
+ WHERE category_name = %s AND thought_id != %s;""",
+ (category, id_))
+ return cursor.fetchall()
+
+ def get_featured_thoughts(self):
+ with self.__connection.cursor() as cursor:
+ cursor.execute("SELECT thought_id, title FROM thoughts WHERE featured = 1;")
+ return cursor.fetchall()
+
+ def update_thought_markdown(self, id_, markdown):
+ with self.__connection.cursor() as cursor:
+ cursor.execute("UPDATE thoughts SET markdown_text = %s WHERE thought_id = %s;", (markdown, id_))
+ self.__connection.commit()
+
+ def get_categories_not(self, category_name):
+ with self.__connection.cursor() as cursor:
+ cursor.execute("SELECT category_name FROM categories WHERE category_name != %s;", (category_name, ))
+ return [i[0] for i in cursor.fetchall()]
+
+ def get_all_thoughts(self):
+ with self.__connection.cursor() as cursor:
+ cursor.execute("""
+ SELECT thought_id, title, dt, category_name FROM thoughts
+ INNER JOIN categories ON thoughts.category_id = categories.category_id;
+ """)
+ return cursor.fetchall()
+
+ def get_cached_tweets(self, numToGet = None):
+ with self.__connection.cursor() as cursor:
+ sql = "SELECT tweet, tweet_id, account FROM diary WHERE account = %s ORDER BY tweeted_at DESC"
+ args = (self.config.get("twitter", "main_account"), )
+ if numToGet is not None:
+ sql += " LIMIT %s;"
+ args = (self.config.get("twitter", "main_account"), numToGet)
+ else:
+ sql += ";"
+ cursor.execute(sql, args)
+
+ return [(i[0], "https://%s/%s/status/%d" % (self.config.get("nitter", "outsideurl"), i[2], i[1])) for i in cursor.fetchall()]
+
+ def get_cached_commits(self, since = None, recurse = True):
+ with self.__connection.cursor() as cursor:
+ if since is not None:
+ cursor.execute("SELECT DISTINCT message, url, commitTime, additions, deletions, total FROM commitCache WHERE commitTime > %s ORDER BY commitTime DESC;", (since, ))
+ else:
+ cursor.execute("SELECT DISTINCT message, url, commitTime, additions, deletions, total FROM commitCache ORDER BY commitTime DESC;")
+ # i think i might have spent too long doing functional programming
+ return [{
+ "repo": urlparse(i[1]).path.split("/")[2],
+ "github_repo_url": "https://github.com" + "/".join(urlparse(i[1]).path.split("/")[:3]),
+ "git_repo_url": "https://%s/%s.git/about" % (self.config.get("github", "personal_domain"), urlparse(i[1]).path.split("/")[2]),
+ "message": i[0],
+ "github_commit_url": i[1],
+ "git_commit_url": "https://%s/%s.git/commit/?id=%s" % (
+ self.config.get("github", "personal_domain"),
+ urlparse(i[1]).path.split("/")[2],
+ urlparse(i[1]).path.split("/")[-1]
+ ),
+ "datetime": i[2].timestamp(),
+ "stats": {
+ "additions": i[3],
+ "deletions": i[4],
+ "total": i[5]
+ }
+ } for i in cursor.fetchall()]
+
+ def update_commit_cache(self, requested):
+ with self.__connection.cursor() as cursor:
+ for commit in requested:
+ cursor.execute("SELECT DISTINCT url FROM commitCache;")
+ urls = [i[0] for i in cursor.fetchall()]
+
+ if commit["url"] not in urls:
+ cursor.execute("""
+ INSERT INTO commitCache (message, url, commitTime, additions, deletions, total)
+ VALUES (%s, %s, %s, %s, %s, %s)""",
+ (commit["message"], commit["url"], commit["datetime"], commit["stats"]["additions"], commit["stats"]["deletions"], commit["stats"]["total"])
+ )
+ self.__connection.commit()
+
+ def get_last_commit_time(self):
+ with self.__connection.cursor() as cursor:
+ cursor.execute("SELECT MAX(commitTime) FROM commitCache;")
+ return cursor.fetchone()[0]
+
+ def get_my_twitter(self):
+ with self.__connection.cursor() as cursor:
+ cursor.execute("SELECT link FROM headerLinks WHERE name = 'twitter';")
+ return cursor.fetchone()[0]
+
+ def get_my_diary_twitter(self):
+ return self.config.get("twitter", "diary_account")
+
+ def get_iso_cd_options(self):
+ iso_dir = self.config.get("cds", "location")
+ return [
+ i
+ for i in os.listdir(iso_dir)
+ if os.path.splitext(i)[-1].lower() in [".iso"]
+ and os.path.getsize(os.path.join(iso_dir, i)) < self.config.getint("cds", "maxsize")
+ ]
+
+ def append_cd_orders(self, iso, email, house, street, city, county, postcode, name):
+ with self.__connection.cursor() as cursor:
+ cursor.execute("""
+ INSERT INTO cd_orders_2 (iso, email, house, street, city, county, postcode, name)
+ VALUES (%s, %s, %s, %s, %s, %s, %s, %s);
+ """, (iso, email, house, street, city, county, postcode, name))
+ id_ = cursor.lastrowid
+ self.__connection.commit()
+ return id_
+
+ def append_qnas(self, qnas):
+ with self.__connection.cursor() as cursor:
+ for qna in qnas:
+ cursor.execute("SELECT curiouscat_id FROM qnas WHERE curiouscat_id = %s;", (qna["id"], ))
+ if cursor.fetchone() is None:
+
+ cursor.execute("INSERT INTO `qnas` VALUES (%s, %s, %s, %s, %s, %s);", (
+ qna["id"], qna["link"], qna["datetime"], qna["question"], qna["answer"], qna["host"]
+ ))
+ print("Appended question with timestamp %s" % qna["datetime"].isoformat())
+
+ else:
+ print("Skipped question with timestamp %s" % qna["datetime"].isoformat())
+ self.__connection.commit()
+
+ def get_oldest_qna(self):
+ with self.__connection.cursor() as cursor:
+ cursor.execute("SELECT MAX(timestamp) FROM qnas;")
+ return cursor.fetchone()[0]
+
+ def get_qnas(self):
+ with self.__connection.cursor() as cursor:
+ cursor.execute("SELECT * FROM qnas;")
+ return sorted(cursor.fetchall(), key = operator.itemgetter(2), reverse = True)
+
+
+
+
+
diff --git a/edaweb/downloader.py b/edaweb/downloader.py
new file mode 100644
index 0000000..4b2af2f
--- /dev/null
+++ b/edaweb/downloader.py
@@ -0,0 +1,54 @@
+from dataclasses import dataclass
+from lxml import html
+import requests
+import shutil
+import urllib
+import os
+
+@dataclass
+class CompressedImages:
+ nhentai_id: int
+
+ def __enter__(self):
+ self.folderpath = os.path.join("static", str(self.nhentai_id))
+ self.zippath = os.path.join("static", "zips", "%i.zip" % self.nhentai_id)
+ os.mkdir(self.folderpath)
+
+ self.num_downloaded = self.download_images("https://nhentai.net/g/%i" % self.nhentai_id, self.folderpath, "nhentai.net")
+
+ shutil.make_archive(self.zippath[:-4], "zip", self.folderpath)
+
+ return self.zippath
+
+ def __exit__(self, type, value, traceback):
+ # os.remove(self.zippath)
+ shutil.rmtree(self.folderpath)
+
+ def download_images(self, url:str, out:str, domain:str) -> int:
+ tree = html.fromstring(requests.get(url).content)
+ for i, element in enumerate(tree.xpath("//a[@class='gallerythumb']"), 1):
+ imurl = self.get_img("https://%s%s" % (domain, element.get("href")), i)
+ print(imurl)
+ self.dl_img(imurl, out)
+
+ return i
+
+ def get_img(self, srcurl:str, num:int) -> str:
+ tree = html.fromstring(requests.get(srcurl).content)
+ for element in tree.xpath("//img"):
+ try:
+ if num == int(os.path.splitext(element.get("src").split("/")[-1])[0]):
+ return element.get("src")
+ except ValueError:
+ pass
+
+ def dl_img(self, imurl, outpath:str):
+ req = urllib.request.Request(imurl, headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_5_8) AppleWebKit/534.50.2 (KHTML, like Gecko) Version/5.0.6 Safari/533.22.3'})
+ mediaContent = urllib.request.urlopen(req).read()
+ with open(os.path.join(outpath, imurl.split("/")[-1]), "wb") as f:
+ f.write(mediaContent)
+
+if __name__ == "__main__":
+ with CompressedImages(306013) as zippath:
+ import subprocess
+ subprocess.run(["cp", zippath, "/home/eden/Downloads"]) \ No newline at end of file
diff --git a/edaweb/parser.py b/edaweb/parser.py
new file mode 100644
index 0000000..00daf3b
--- /dev/null
+++ b/edaweb/parser.py
@@ -0,0 +1,209 @@
+#!/usr/bin/env python3
+
+from urllib.parse import urlparse
+from pygments import highlight
+from pygments.formatters import HtmlFormatter, ClassNotFound
+from pygments.lexers import get_lexer_by_name
+import urllib.parse
+import webbrowser
+import lxml.etree
+import lxml.html
+import database
+import argparse
+import getpass
+import houdini
+import mistune
+import jinja2
+import app
+import sys
+import re
+import os
+
+class EdawebRenderer(mistune.HTMLRenderer):
+ def blockcode(self, text, lang):
+ try:
+ lexer = get_lexer_by_name(lang, stripall=True)
+ except ClassNotFound:
+ lexer = None
+
+ if lexer:
+ formatter = HtmlFormatter()
+ return highlight(text, lexer, formatter)
+ # default
+ return '\n<pre><code>{}</code></pre>\n'.format(houdini.escape_html(text.strip()))
+
+ def block_quote(self, content):
+ content = content[3:-5] # idk why this is required...
+ out = '\n<blockquote>'
+ for line in houdini.escape_html(content.strip()).split("\n"):
+ out += '\n<span class="quote">{}</span><br>'.format(line)
+ return out + '\n</blockquote>'
+
+ def image(self, link, text, title):
+ return "<a href='%s' target='_blank'><img alt='%s' src='%s'></a>" % (
+ urlparse(link)._replace(query='').geturl(), text, link
+ )
+
+ def heading(self, text, level):
+ hash_ = urllib.parse.quote_plus(text)
+ return "<h%d id='%s'>%s <a class='header_linker' href='#%s'>[#]</a></h%d>" % (
+ level, hash_, text, hash_, level
+ )
+
+ # it would appear that the escape=False option does not work with custom renderers,
+ # so we set it to True and just have these silly function stubs to include them manually
+ def inline_html(self, html):
+ return html
+
+ def block_html(self, html):
+ return html
+
+def get_thought_from_id(db, id_):
+ category_name, title, dt, markdown, redirect = db.get_thought(id_)
+ html, headers = parse_text(markdown)
+ return category_name, title, dt, html, headers, redirect
+
+def parse_file(path):
+ with open(path, "r") as f:
+ unformatted = f.read()
+
+ return parse_text(unformatted)[0]
+
+def parse_text(unformatted):
+ md = mistune.create_markdown(
+ renderer = EdawebRenderer(),
+ plugins = ["strikethrough", "table", "url", "task_lists", "def_list"],
+ # escape = False
+ )
+ html = md(unformatted)
+ if html == "":
+ return "", ""
+
+ return html, get_headers(html)
+
+def get_headers(html):
+ root = lxml.html.fromstring(html)
+
+ headers = []
+ thesmallestlevel = 7
+ for node in root.xpath('//h1|//h2|//h3|//h4|//h5//h6'):
+ level = int(node.tag[-1])
+ if level < thesmallestlevel:
+ thesmallestlevel = level
+ headers.append((
+ # lxml.etree.tostring(node),
+ # "<p>%s</p>" % urllib.parse.unquote_plus(node.attrib["id"]), # possibly insecure?
+ urllib.parse.unquote_plus(node.attrib["id"]),
+ level, # -horrible hack
+ "#%s" % node.attrib["id"])
+ )
+
+ headers = [(i[0], i[1] - thesmallestlevel, i[2]) for i in headers]
+ # print(headers)
+ # there is a bug here-
+ # it must start with the largest header and only go up and down in increments of one
+ # TODO: fix it!
+ md_template = jinja2.Template("""
+{% for text, depth, link in contents %}
+{{ " " * depth }} - [{{ text }}]({{ link }})
+{% endfor %}
+ """)
+
+ return mistune.html(md_template.render(contents = headers))
+
+def main():
+ p = argparse.ArgumentParser()
+ subparse = p.add_subparsers(help = "sub-command help")
+ save_parser = subparse.add_parser("save", help = "Add a markdown file to the database")
+ echo_parser = subparse.add_parser("echo", help = "Print markdown render to stdout")
+ update_parser = subparse.add_parser("update", help = "Replace a markdown file")
+ export_parser = subparse.add_parser("export", help = "Export a database markdown file to disk")
+ list_parser = subparse.add_parser("list", help = "List all the markdowns in the database")
+
+ for s in [save_parser, echo_parser, update_parser]:
+ s.add_argument(
+ "-m", "--markdown",
+ help = "Path to a markdown file",
+ type = str,
+ required = True
+ )
+
+ for s in [save_parser]:
+ s.add_argument(
+ "-t", "--title",
+ help = "Article title",
+ type = str,
+ required = True
+ )
+ s.add_argument(
+ "-c", "--category",
+ help = "Article category",
+ type = str,
+ required = True
+ )
+
+ for s in [save_parser, update_parser, export_parser, list_parser]:
+ s.add_argument(
+ "-u", "--username",
+ help = "Username to use for the database",
+ type = str,
+ required = True
+ )
+
+ for s in [export_parser, update_parser]:
+ s.add_argument(
+ "-i", "--id",
+ help = "Article's id",
+ type = int,
+ required = True
+ )
+
+ export_parser.add_argument(
+ "-o", "--out",
+ help = "Path to write the markdown file to",
+ type = str,
+ required = True
+ )
+
+ args = vars(p.parse_args())
+
+ if "username" in args.keys():
+ args["password"] = getpass.getpass("Enter password for %s@%s: " % (args["username"], app.CONFIG["mysql"]["host"]))
+
+ try:
+ verb = sys.argv[1]
+ except IndexError:
+ print("No verb specified... Nothing to do... Exiting...")
+ exit()
+
+ if verb in ["save", "export", "update", "list"]:
+ with database.Database(
+ safeLogin = False,
+ user = args["username"],
+ passwd = args["password"]
+ ) as db:
+ if verb == "save":
+ if db.add_category(args["category"]):
+ print("Added category...")
+ with open(args["markdown"], "r") as f:
+ db.add_thought(args["category"], args["title"], f.read())
+ print("Added thought...")
+
+ elif verb == "export":
+ with open(args["out"], "w") as f:
+ f.writelines(db.get_thought(args["id"])[-2])
+ print("Written to %s" % args["out"])
+
+ elif verb == "update":
+ with open(args["markdown"], "r") as f:
+ db.update_thought_markdown(args["id"], f.read())
+
+ elif verb == "list":
+ for id_, title, dt, category_name in db.get_all_thoughts():
+ print("%d\t%s\t%s\t%s" % (id_, title, dt, category_name))
+
+ elif verb == "echo":
+ print(parse_file(args["markdown"]))
+
+if __name__ == "__main__":
+ main()
diff --git a/edaweb/services.py b/edaweb/services.py
new file mode 100644
index 0000000..5506ef1
--- /dev/null
+++ b/edaweb/services.py
@@ -0,0 +1,369 @@
+from dataclasses import dataclass
+from io import StringIO
+from lxml import html, etree
+from github import Github
+import multiprocessing
+import paramiko.client
+from APiHole import PiHole
+import transmission_rpc
+import configparser
+import math as maths
+import requests
+import datetime
+import urllib
+import docker
+import random
+import subprocess
+import fabric
+import pickle
+import queue
+import json
+import time
+import os
+
+theLastId = 0
+CONFIG = configparser.ConfigParser(interpolation = None)
+CONFIG.read(os.path.join(os.path.dirname(__file__), "edaweb.conf"))
+
+def humanbytes(B):
+ 'Return the given bytes as a human friendly KB, MB, GB, or TB string'
+ B = float(B)
+ KB = float(1024)
+ MB = float(KB ** 2) # 1,048,576
+ GB = float(KB ** 3) # 1,073,741,824
+ TB = float(KB ** 4) # 1,099,511,627,776
+
+ if B < KB:
+ return '{0} {1}'.format(B,'Bytes' if 0 == B > 1 else 'Byte')
+ elif KB <= B < MB:
+ return '{0:.2f} KB'.format(B/KB)
+ elif MB <= B < GB:
+ return '{0:.2f} MB'.format(B/MB)
+ elif GB <= B < TB:
+ return '{0:.2f} GB'.format(B/GB)
+ elif TB <= B:
+ return '{0:.2f} TB'.format(B/TB)
+
+@dataclass
+class SafebooruImage:
+ id_: int
+ url: str
+ searchTags: list
+ tags: list
+ source: str
+ imurl: str
+
+ def remove_tag(self, tag):
+ return list(set(self.searchTags).difference(set([tag])))
+
+@dataclass
+class DownloadedImage:
+ imurl: str
+
+ def __enter__(self):
+ self.filename = os.path.join("static", "images", "random.jpg")
+
+ req = urllib.request.Request(self.imurl, headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_5_8) AppleWebKit/534.50.2 (KHTML, like Gecko) Version/5.0.6 Safari/533.22.3'})
+ mediaContent = urllib.request.urlopen(req).read()
+ with open(self.filename, "wb") as f:
+ f.write(mediaContent)
+ return self.filename
+
+ def __exit__(self, type, value, traceback):
+ os.remove(self.filename)
+
+def get_num_pages(tags):
+ pages_url = "https://safebooru.org/index.php?page=post&s=list&tags=%s" % "+".join(tags)
+ tree = html.fromstring(requests.get(pages_url).content)
+ try:
+ finalpage_element = tree.xpath("/html/body/div[6]/div/div[2]/div[2]/div/a[12]")[0]
+ except IndexError:
+ return 1
+ else:
+ return int(int(urllib.parse.parse_qs(finalpage_element.get("href"))["pid"][0]) / (5*8))
+
+def get_id_from_url(url):
+ return int(urllib.parse.parse_qs(url)["id"][0])
+
+def get_random_image(tags):
+ global theLastId
+ searchPage = random.randint(1, get_num_pages(tags)) * 5 * 8
+ url = "https://safebooru.org/index.php?page=post&s=list&tags=%s&pid=%i" % ("+".join(tags), searchPage)
+ tree = html.fromstring(requests.get(url).content)
+
+ imageElements = [e for e in tree.xpath("/html/body/div[6]/div/div[2]/div[1]")[0].iter(tag = "a")]
+ try:
+ element = random.choice(imageElements)
+ except IndexError:
+ # raise ConnectionError("Couldn't find any images")
+ return get_random_image(tags)
+
+ url = "https://safebooru.org/" + element.get("href")
+ if get_id_from_url(url) == theLastId:
+ return get_random_image(tags)
+ theLastId = get_id_from_url(url)
+
+ try:
+ sbi = SafebooruImage(
+ id_ = get_id_from_url(url),
+ url = url,
+ tags = element.find("img").get("alt").split(),
+ searchTags = tags,
+ source = fix_source_url(get_source(url)),
+ imurl = get_imurl(url)
+ )
+ except (ConnectionError, KeyError) as e:
+ print("[ERROR]", e)
+ return get_random_image(tags)
+
+ if link_deleted(sbi.url):
+ print("Retried since the source was deleted...")
+ return get_random_image(tags)
+
+ return sbi
+
+def get_source(url):
+ tree = html.fromstring(requests.get(url).content)
+ for element in tree.xpath('//*[@id="stats"]')[0].iter("li"):
+ if element.text.startswith("Source: h"):
+ return element.text[8:]
+ elif element.text.startswith("Source:"):
+ for child in element.iter():
+ if child.get("href") is not None:
+ return child.get("href")
+ raise ConnectionError("Couldn't find source image for id %i" % get_id_from_url(url))
+
+def fix_source_url(url):
+ parsed = urllib.parse.urlparse(url)
+ if parsed.netloc == "www.pixiv.net":
+ return "https://www.pixiv.net/en/artworks/" + urllib.parse.parse_qs(parsed.query)["illust_id"][0]
+ elif parsed.netloc in ["bishie.booru.org", "www.secchan.net"]:
+ return ConnectionError("Couldn't get source")
+ elif "pximg.net" in parsed.netloc or "pixiv.net" in parsed.netloc:
+ return "https://www.pixiv.net/en/artworks/" + parsed.path.split("/")[-1][:8]
+ elif parsed.netloc == "twitter.com":
+ return url.replace("twitter.com", "nitter.eda.gay")
+ return url
+
+def get_imurl(url):
+ tree = html.fromstring(requests.get(url).content)
+ return tree.xpath('//*[@id="image"]')[0].get("src")
+
+def link_deleted(url):
+ text = requests.get(url).text
+ return text[text.find("<title>") + 7 : text.find("</title>")] in ["Error | nitter", "イラストコミュニケーションサービス[pixiv]"]
+
+def request_recent_commits(since = datetime.datetime.now() - datetime.timedelta(days=7)):
+ g = Github(CONFIG.get("github", "access_code"))
+ out = []
+ for repo in g.get_user().get_repos():
+ # print(repo.name, list(repo.get_branches()))
+ try:
+ for commit in repo.get_commits(since = since):
+ out.append({
+ "repo": repo.name,
+ "message": commit.commit.message,
+ "url": commit.html_url,
+ "datetime": commit.commit.author.date,
+ "stats": {
+ "additions": commit.stats.additions,
+ "deletions": commit.stats.deletions,
+ "total": commit.stats.total
+ }
+ })
+ except Exception as e:
+ print(repo, e)
+
+ return sorted(out, key = lambda a: a["datetime"], reverse = True)
+
+def scrape_nitter(username, get_until:int):
+ new_tweets = []
+ nitter_url = CONFIG.get("nitter", "internalurl")
+ nitter_port = CONFIG.getint("nitter", "internalport")
+ scrape_new_pages = True
+ url = "http://%s:%d/%s" % (nitter_url, nitter_port, username)
+
+ while scrape_new_pages:
+ tree = html.fromstring(requests.get(url).content)
+ for i, tweetUrlElement in enumerate(tree.xpath('//*[@class="tweet-link"]'), 0):
+ if i > 0 and tweetUrlElement.get("href").split("/")[1] == username:
+ id_ = int(urllib.parse.urlparse(tweetUrlElement.get("href")).path.split("/")[-1])
+ tweet_link = "http://%s:%d%s" % (nitter_url, nitter_port, tweetUrlElement.get("href"))
+
+ if id_ == get_until:
+ scrape_new_pages = False
+ break
+
+ try:
+ dt, replying_to, text, images = parse_tweet(tweet_link)
+ new_tweets.append((id_, dt, replying_to, text, username, images))
+ print(dt, "'%s'" % text)
+ except IndexError:
+ print("Couldn't get any more tweets")
+ scrape_new_pages = False
+ break
+ except ConnectionError:
+ print("Rate limited, try again later")
+ return []
+
+
+ try:
+ cursor = tree.xpath('//*[@class="show-more"]/a')[0].get("href")
+ except IndexError:
+ # no more elements
+ break
+ url = "http://%s:%d/%s%s" % (nitter_url, nitter_port, username, cursor)
+
+ return new_tweets
+
+def parse_tweet(tweet_url):
+ # print(tweet_url)
+ tree = html.fromstring(requests.get(tweet_url).content)
+ # with open("2images.html", "r") as f:
+ # tree = html.fromstring(f.read())
+
+ rate_limited_elem = tree.xpath("/html/body/div/div/div/span")
+ if rate_limited_elem != []:
+ if rate_limited_elem[0].text == "Instance has been rate limited.":
+ raise ConnectionError("Instance has been rate limited.")
+
+ main_tweet_elem = tree.xpath('//*[@class="main-tweet"]')[0]
+
+ dt_str = main_tweet_elem.xpath('//*[@class="tweet-published"]')[0].text
+ dt = datetime.datetime.strptime(dt_str.replace("Â", ""), "%b %d, %Y · %I:%M %p UTC")
+ text = tree.xpath('//*[@class="main-tweet"]/div/div/div[2]')[0].text_content()
+ if text == "":
+ text = "[Image only]"
+ replying_to_elems = tree.xpath('//*[@class="before-tweet thread-line"]/div/a')
+ if replying_to_elems != []:
+ replying_to = int(urllib.parse.urlparse(replying_to_elems[-1].get("href")).path.split("/")[-1])
+ else:
+ replying_to = None
+
+ images = []
+ images_elems = tree.xpath('//*[@class="main-tweet"]/div/div/div[3]/div/div/a/img')
+ for image_elem in images_elems:
+ images.append("https://" + CONFIG.get("nitter", "outsideurl") + urllib.parse.urlparse(image_elem.get("src")).path)
+
+ return dt, replying_to, text, images
+
+def scrape_whispa(whispa_url, since = None):
+ tree = html.fromstring(requests.get(whispa_url).content.decode())
+ qnas = []
+ # we're not doing proper HTML scraping here really... since the site uses client side rendering
+ # we rather parse the JS scripts to get the JSON payload of useful information... sadly this looks horrible
+ for i, script in enumerate(tree.xpath("/html/body/script"), 0):
+ js = str(script.text)
+ if "receivedFeedback" in js:
+ # my god this is horrible...
+ for j in json.loads(json.loads(js[19:-1])[1][2:])[0][3]["loadedUser"]["receivedFeedback"]:
+ if j["childFeedback"] == []:
+ continue
+
+ dt = datetime.datetime.fromisoformat(j["childFeedback"][0]["createdAt"][:-1])
+
+ qnas.append({
+ # "id": int(str(maths.modf(maths.log(int(j["id"], 16)))[0])[2:]),
+ "id": int(dt.timestamp()),
+ "link": None,
+ "datetime": dt,
+ "question": j["content"],
+ "answer": j["childFeedback"][0]["content"],
+ "host": "whispa.sh"
+ })
+ return qnas
+
+def get_docker_containers(host, ssh_key_path):
+ result = fabric.Connection(
+ host = host,
+ user = "root",
+ connect_kwargs = {
+ "key_filename": ssh_key_path,
+ "look_for_keys": False
+ }
+ ).run('docker ps -a -s --format "table {{.Names}};{{.Status}};{{.Image}}"', hide = True)
+ return [line.split(";") for line in result.stdout.split("\n")[1:-1]]
+
+def cache_all_docker_containers(ssh_key_path):
+ containers = {}
+ containers["containers"] = {}
+ for host, name in CONFIG["docker_hosts"].items():
+ print(host)
+ containers["containers"][(host, name)] = get_docker_containers(host, ssh_key_path)
+
+ containers["cachetime"] = "Docker information last updated at %s" % str(datetime.datetime.now())
+ with open("/tmp/docker-cache.json", "wb") as f:
+ pickle.dump(containers, f)
+
+def get_all_docker_containers():
+ if not os.path.exists("/tmp/docker-cache.json"):
+ return {"containers": {}, "cachetime": "No cached docker information"}
+
+ with open("/tmp/docker-cache.json", "rb") as f:
+ return pickle.load(f)
+
+def timeout(func):
+ # cant get this to work with queue.Queue() for some reason?
+ # this works but Manager() uses an extra thread than Queue()
+ manager = multiprocessing.Manager()
+ returnVan = manager.list()
+ # ti = time.time()
+
+ def runFunc(q, func):
+ q.append(func())
+
+ def beginTimeout():
+ t = multiprocessing.Process(target = runFunc, args = (returnVan, func))
+ t.start()
+
+ t.join(timeout = CONFIG["servicetimeout"].getint("seconds"))
+
+ # print("Request took:", time.time() - ti)
+ try:
+ return returnVan[0]
+ except IndexError:
+ if t.is_alive():
+ t.terminate()
+
+ return beginTimeout
+
+@timeout
+def get_torrent_stats():
+ client = transmission_rpc.client.Client(
+ host = CONFIG.get("transmission", "host")
+ )
+ s = vars(client.session_stats())["fields"]
+ return {
+ "Active torrents:": s["activeTorrentCount"],
+ "Downloaded:": humanbytes(s["cumulative-stats"]["downloadedBytes"]),
+ "Uploaded:": humanbytes(s["cumulative-stats"]["uploadedBytes"]),
+ "Active time:": str(datetime.timedelta(seconds = s["cumulative-stats"]["secondsActive"])),
+ "Files added:": s["cumulative-stats"]["filesAdded"],
+ "Current upload speed:": humanbytes(s["uploadSpeed"]) + "s/S",
+ "Current download speed:": humanbytes(s["downloadSpeed"]) + "s/S"
+ }
+
+@timeout
+def get_pihole_stats():
+ return PiHole.GetSummary(CONFIG.get("pihole", "url"), CONFIG.get("pihole", "key"), True)
+
+def get_recent_commits(db, max_per_repo = 3):
+ cache = db.get_cached_commits()
+ num_per_repo = {}
+ out = []
+ for commit in cache:
+ if commit["repo"] not in num_per_repo.keys():
+ num_per_repo[commit["repo"]] = 0
+
+ num_per_repo[commit["repo"]] += 1
+ if num_per_repo[commit["repo"]] <= max_per_repo:
+ out.append(commit)
+
+ return sorted(out, key = lambda a: a["datetime"], reverse = True)
+
+if __name__ == "__main__":
+ print(scrape_whispa(CONFIG.get("qnas", "url")))
+ # import database
+
+ # with database.Database() as db:
+ # print(json.dumps(get_recent_commits(db), indent=4))
diff --git a/edaweb/static/images/0don0t4ofuc41-3776935852.jpg b/edaweb/static/images/0don0t4ofuc41-3776935852.jpg
new file mode 100644
index 0000000..f65e951
--- /dev/null
+++ b/edaweb/static/images/0don0t4ofuc41-3776935852.jpg
Binary files differ
diff --git a/edaweb/static/images/1544552064683.jpg b/edaweb/static/images/1544552064683.jpg
new file mode 100644
index 0000000..20a6527
--- /dev/null
+++ b/edaweb/static/images/1544552064683.jpg
Binary files differ
diff --git a/edaweb/static/images/1549844950404.jpg b/edaweb/static/images/1549844950404.jpg
new file mode 100644
index 0000000..5197c11
--- /dev/null
+++ b/edaweb/static/images/1549844950404.jpg
Binary files differ
diff --git a/edaweb/static/images/1555824429083.png b/edaweb/static/images/1555824429083.png
new file mode 100644
index 0000000..5b2fd0b
--- /dev/null
+++ b/edaweb/static/images/1555824429083.png
Binary files differ
diff --git a/edaweb/static/images/1555824491105.png b/edaweb/static/images/1555824491105.png
new file mode 100644
index 0000000..2e20fca
--- /dev/null
+++ b/edaweb/static/images/1555824491105.png
Binary files differ
diff --git a/edaweb/static/images/1671540582317176.png b/edaweb/static/images/1671540582317176.png
new file mode 100644
index 0000000..dd741d3
--- /dev/null
+++ b/edaweb/static/images/1671540582317176.png
Binary files differ
diff --git a/edaweb/static/images/1urouter.jpg b/edaweb/static/images/1urouter.jpg
new file mode 100644
index 0000000..b295e59
--- /dev/null
+++ b/edaweb/static/images/1urouter.jpg
Binary files differ
diff --git a/edaweb/static/images/20220401_222149.jpg b/edaweb/static/images/20220401_222149.jpg
new file mode 100644
index 0000000..d9fecae
--- /dev/null
+++ b/edaweb/static/images/20220401_222149.jpg
Binary files differ
diff --git a/edaweb/static/images/324162a23865a6a7e75761871d29935314cbc2b3.jpg b/edaweb/static/images/324162a23865a6a7e75761871d29935314cbc2b3.jpg
new file mode 100644
index 0000000..cede48f
--- /dev/null
+++ b/edaweb/static/images/324162a23865a6a7e75761871d29935314cbc2b3.jpg
Binary files differ
diff --git a/edaweb/static/images/3502e26d0181c684bc17b56188bde6e80569d191_full.jpg b/edaweb/static/images/3502e26d0181c684bc17b56188bde6e80569d191_full.jpg
new file mode 100644
index 0000000..1f75924
--- /dev/null
+++ b/edaweb/static/images/3502e26d0181c684bc17b56188bde6e80569d191_full.jpg
Binary files differ
diff --git a/edaweb/static/images/5600x.jpg b/edaweb/static/images/5600x.jpg
new file mode 100644
index 0000000..f3995b2
--- /dev/null
+++ b/edaweb/static/images/5600x.jpg
Binary files differ
diff --git a/edaweb/static/images/9400t.jpg b/edaweb/static/images/9400t.jpg
new file mode 100644
index 0000000..175e8f1
--- /dev/null
+++ b/edaweb/static/images/9400t.jpg
Binary files differ
diff --git a/edaweb/static/images/9400t_cooler.jpg b/edaweb/static/images/9400t_cooler.jpg
new file mode 100644
index 0000000..dcfaf27
--- /dev/null
+++ b/edaweb/static/images/9400t_cooler.jpg
Binary files differ
diff --git a/edaweb/static/images/E1NlPc1X0AEkB-s.png b/edaweb/static/images/E1NlPc1X0AEkB-s.png
new file mode 100644
index 0000000..b0be028
--- /dev/null
+++ b/edaweb/static/images/E1NlPc1X0AEkB-s.png
Binary files differ
diff --git a/edaweb/static/images/E3xdm-hWYAEADNx.jpg b/edaweb/static/images/E3xdm-hWYAEADNx.jpg
new file mode 100644
index 0000000..d14513d
--- /dev/null
+++ b/edaweb/static/images/E3xdm-hWYAEADNx.jpg
Binary files differ
diff --git a/edaweb/static/images/E4WhXAvWYAIFwEa.png b/edaweb/static/images/E4WhXAvWYAIFwEa.png
new file mode 100644
index 0000000..144eb45
--- /dev/null
+++ b/edaweb/static/images/E4WhXAvWYAIFwEa.png
Binary files differ
diff --git a/edaweb/static/images/E4cjVaPXEAU83U0.png b/edaweb/static/images/E4cjVaPXEAU83U0.png
new file mode 100644
index 0000000..941a436
--- /dev/null
+++ b/edaweb/static/images/E4cjVaPXEAU83U0.png
Binary files differ
diff --git a/edaweb/static/images/E7ODIBeX0AAivRV.jpg b/edaweb/static/images/E7ODIBeX0AAivRV.jpg
new file mode 100644
index 0000000..dc52196
--- /dev/null
+++ b/edaweb/static/images/E7ODIBeX0AAivRV.jpg
Binary files differ
diff --git a/edaweb/static/images/E7ODIZEWUAMQjty.jpg b/edaweb/static/images/E7ODIZEWUAMQjty.jpg
new file mode 100644
index 0000000..2e90eda
--- /dev/null
+++ b/edaweb/static/images/E7ODIZEWUAMQjty.jpg
Binary files differ
diff --git a/edaweb/static/images/E7ODIv0WUAUp-ad.jpg b/edaweb/static/images/E7ODIv0WUAUp-ad.jpg
new file mode 100644
index 0000000..97e704e
--- /dev/null
+++ b/edaweb/static/images/E7ODIv0WUAUp-ad.jpg
Binary files differ
diff --git a/edaweb/static/images/E7ODJELX0AAVqna.jpg b/edaweb/static/images/E7ODJELX0AAVqna.jpg
new file mode 100644
index 0000000..eedfb14
--- /dev/null
+++ b/edaweb/static/images/E7ODJELX0AAVqna.jpg
Binary files differ
diff --git a/edaweb/static/images/EbmVCYKWkAAwXKW.jpg b/edaweb/static/images/EbmVCYKWkAAwXKW.jpg
new file mode 100644
index 0000000..20b3a19
--- /dev/null
+++ b/edaweb/static/images/EbmVCYKWkAAwXKW.jpg
Binary files differ
diff --git a/edaweb/static/images/Ffi1ducXEAArXDg.jpg b/edaweb/static/images/Ffi1ducXEAArXDg.jpg
new file mode 100644
index 0000000..5c31cc5
--- /dev/null
+++ b/edaweb/static/images/Ffi1ducXEAArXDg.jpg
Binary files differ
diff --git a/edaweb/static/images/GGBDzuSXMAA1Ktk.jpg b/edaweb/static/images/GGBDzuSXMAA1Ktk.jpg
new file mode 100644
index 0000000..35e04e1
--- /dev/null
+++ b/edaweb/static/images/GGBDzuSXMAA1Ktk.jpg
Binary files differ
diff --git a/edaweb/static/images/GSxGe_rXQAAgEhj.jpg b/edaweb/static/images/GSxGe_rXQAAgEhj.jpg
new file mode 100644
index 0000000..0351920
--- /dev/null
+++ b/edaweb/static/images/GSxGe_rXQAAgEhj.jpg
Binary files differ
diff --git a/edaweb/static/images/GTq-GOgWQAErvCp.jpg b/edaweb/static/images/GTq-GOgWQAErvCp.jpg
new file mode 100644
index 0000000..6a2f7b4
--- /dev/null
+++ b/edaweb/static/images/GTq-GOgWQAErvCp.jpg
Binary files differ
diff --git a/edaweb/static/images/GZhgzaK.png b/edaweb/static/images/GZhgzaK.png
new file mode 100755
index 0000000..c73e8b4
--- /dev/null
+++ b/edaweb/static/images/GZhgzaK.png
Binary files differ
diff --git a/edaweb/static/images/GcyexeCW0AAYssz.jpg b/edaweb/static/images/GcyexeCW0AAYssz.jpg
new file mode 100644
index 0000000..c0762a8
--- /dev/null
+++ b/edaweb/static/images/GcyexeCW0AAYssz.jpg
Binary files differ
diff --git a/edaweb/static/images/GegzRla.png b/edaweb/static/images/GegzRla.png
new file mode 100755
index 0000000..cf12555
--- /dev/null
+++ b/edaweb/static/images/GegzRla.png
Binary files differ
diff --git a/edaweb/static/images/IMG_-g4wqat.jpg b/edaweb/static/images/IMG_-g4wqat.jpg
new file mode 100644
index 0000000..9f05aaf
--- /dev/null
+++ b/edaweb/static/images/IMG_-g4wqat.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1602.jpg b/edaweb/static/images/IMG_1602.jpg
new file mode 100644
index 0000000..d1937fb
--- /dev/null
+++ b/edaweb/static/images/IMG_1602.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1603.jpg b/edaweb/static/images/IMG_1603.jpg
new file mode 100644
index 0000000..4d3e8c1
--- /dev/null
+++ b/edaweb/static/images/IMG_1603.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1604.jpg b/edaweb/static/images/IMG_1604.jpg
new file mode 100644
index 0000000..4cc82c3
--- /dev/null
+++ b/edaweb/static/images/IMG_1604.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1605.jpg b/edaweb/static/images/IMG_1605.jpg
new file mode 100644
index 0000000..38d6770
--- /dev/null
+++ b/edaweb/static/images/IMG_1605.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1606.jpg b/edaweb/static/images/IMG_1606.jpg
new file mode 100644
index 0000000..fcbe2a4
--- /dev/null
+++ b/edaweb/static/images/IMG_1606.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1607.jpg b/edaweb/static/images/IMG_1607.jpg
new file mode 100644
index 0000000..825e338
--- /dev/null
+++ b/edaweb/static/images/IMG_1607.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1609.jpg b/edaweb/static/images/IMG_1609.jpg
new file mode 100644
index 0000000..6b7d834
--- /dev/null
+++ b/edaweb/static/images/IMG_1609.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1610.jpg b/edaweb/static/images/IMG_1610.jpg
new file mode 100644
index 0000000..d201596
--- /dev/null
+++ b/edaweb/static/images/IMG_1610.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1611.jpg b/edaweb/static/images/IMG_1611.jpg
new file mode 100644
index 0000000..9eb1af5
--- /dev/null
+++ b/edaweb/static/images/IMG_1611.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1612.jpg b/edaweb/static/images/IMG_1612.jpg
new file mode 100644
index 0000000..f176c17
--- /dev/null
+++ b/edaweb/static/images/IMG_1612.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1613.png b/edaweb/static/images/IMG_1613.png
new file mode 100644
index 0000000..63b743a
--- /dev/null
+++ b/edaweb/static/images/IMG_1613.png
Binary files differ
diff --git a/edaweb/static/images/IMG_1614.png b/edaweb/static/images/IMG_1614.png
new file mode 100644
index 0000000..349afb7
--- /dev/null
+++ b/edaweb/static/images/IMG_1614.png
Binary files differ
diff --git a/edaweb/static/images/IMG_1615.jpg b/edaweb/static/images/IMG_1615.jpg
new file mode 100644
index 0000000..da42a2b
--- /dev/null
+++ b/edaweb/static/images/IMG_1615.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1616.jpg b/edaweb/static/images/IMG_1616.jpg
new file mode 100644
index 0000000..6f83c38
--- /dev/null
+++ b/edaweb/static/images/IMG_1616.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1617.jpg b/edaweb/static/images/IMG_1617.jpg
new file mode 100644
index 0000000..7528c7d
--- /dev/null
+++ b/edaweb/static/images/IMG_1617.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1618.jpg b/edaweb/static/images/IMG_1618.jpg
new file mode 100644
index 0000000..9b82ea1
--- /dev/null
+++ b/edaweb/static/images/IMG_1618.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1619.jpg b/edaweb/static/images/IMG_1619.jpg
new file mode 100644
index 0000000..ccae28a
--- /dev/null
+++ b/edaweb/static/images/IMG_1619.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1620.jpg b/edaweb/static/images/IMG_1620.jpg
new file mode 100644
index 0000000..54e92d6
--- /dev/null
+++ b/edaweb/static/images/IMG_1620.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1621.jpg b/edaweb/static/images/IMG_1621.jpg
new file mode 100644
index 0000000..5aebae0
--- /dev/null
+++ b/edaweb/static/images/IMG_1621.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1622.jpg b/edaweb/static/images/IMG_1622.jpg
new file mode 100644
index 0000000..56cc9c3
--- /dev/null
+++ b/edaweb/static/images/IMG_1622.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1623.jpg b/edaweb/static/images/IMG_1623.jpg
new file mode 100644
index 0000000..43d0107
--- /dev/null
+++ b/edaweb/static/images/IMG_1623.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1624.png b/edaweb/static/images/IMG_1624.png
new file mode 100644
index 0000000..4fd690f
--- /dev/null
+++ b/edaweb/static/images/IMG_1624.png
Binary files differ
diff --git a/edaweb/static/images/IMG_1625.jpg b/edaweb/static/images/IMG_1625.jpg
new file mode 100644
index 0000000..496a7fa
--- /dev/null
+++ b/edaweb/static/images/IMG_1625.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1626.jpg b/edaweb/static/images/IMG_1626.jpg
new file mode 100644
index 0000000..90060fb
--- /dev/null
+++ b/edaweb/static/images/IMG_1626.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1627.jpg b/edaweb/static/images/IMG_1627.jpg
new file mode 100644
index 0000000..90c4f49
--- /dev/null
+++ b/edaweb/static/images/IMG_1627.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1628.jpg b/edaweb/static/images/IMG_1628.jpg
new file mode 100644
index 0000000..f2cc8c6
--- /dev/null
+++ b/edaweb/static/images/IMG_1628.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1629.jpg b/edaweb/static/images/IMG_1629.jpg
new file mode 100644
index 0000000..b19b03e
--- /dev/null
+++ b/edaweb/static/images/IMG_1629.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1630.jpg b/edaweb/static/images/IMG_1630.jpg
new file mode 100644
index 0000000..ddef77b
--- /dev/null
+++ b/edaweb/static/images/IMG_1630.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1632.jpg b/edaweb/static/images/IMG_1632.jpg
new file mode 100644
index 0000000..070615a
--- /dev/null
+++ b/edaweb/static/images/IMG_1632.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1633.jpg b/edaweb/static/images/IMG_1633.jpg
new file mode 100644
index 0000000..6fa7ae8
--- /dev/null
+++ b/edaweb/static/images/IMG_1633.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1636.jpg b/edaweb/static/images/IMG_1636.jpg
new file mode 100644
index 0000000..a3de5ad
--- /dev/null
+++ b/edaweb/static/images/IMG_1636.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1637.jpg b/edaweb/static/images/IMG_1637.jpg
new file mode 100644
index 0000000..8e6b489
--- /dev/null
+++ b/edaweb/static/images/IMG_1637.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1638.jpg b/edaweb/static/images/IMG_1638.jpg
new file mode 100644
index 0000000..b9ca9f3
--- /dev/null
+++ b/edaweb/static/images/IMG_1638.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1639.jpg b/edaweb/static/images/IMG_1639.jpg
new file mode 100644
index 0000000..5695e58
--- /dev/null
+++ b/edaweb/static/images/IMG_1639.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1640.jpg b/edaweb/static/images/IMG_1640.jpg
new file mode 100644
index 0000000..df3eaea
--- /dev/null
+++ b/edaweb/static/images/IMG_1640.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_1641.jpg b/edaweb/static/images/IMG_1641.jpg
new file mode 100644
index 0000000..ce2eddb
--- /dev/null
+++ b/edaweb/static/images/IMG_1641.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_20210422_212009.jpg b/edaweb/static/images/IMG_20210422_212009.jpg
new file mode 100644
index 0000000..a8337c4
--- /dev/null
+++ b/edaweb/static/images/IMG_20210422_212009.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_20210824_175000.jpg b/edaweb/static/images/IMG_20210824_175000.jpg
new file mode 100644
index 0000000..216c617
--- /dev/null
+++ b/edaweb/static/images/IMG_20210824_175000.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_20220809_172130442_HDR.jpg b/edaweb/static/images/IMG_20220809_172130442_HDR.jpg
new file mode 100644
index 0000000..e1987f8
--- /dev/null
+++ b/edaweb/static/images/IMG_20220809_172130442_HDR.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_20220811_133224877_HDR.jpg b/edaweb/static/images/IMG_20220811_133224877_HDR.jpg
new file mode 100644
index 0000000..eb05b1b
--- /dev/null
+++ b/edaweb/static/images/IMG_20220811_133224877_HDR.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_20220811_133224877_HDR_crop.jpg b/edaweb/static/images/IMG_20220811_133224877_HDR_crop.jpg
new file mode 100644
index 0000000..7bc63ba
--- /dev/null
+++ b/edaweb/static/images/IMG_20220811_133224877_HDR_crop.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_20220812_112838125_HDR.jpg b/edaweb/static/images/IMG_20220812_112838125_HDR.jpg
new file mode 100644
index 0000000..6e56a49
--- /dev/null
+++ b/edaweb/static/images/IMG_20220812_112838125_HDR.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_20220823_154457137_HDR.jpg b/edaweb/static/images/IMG_20220823_154457137_HDR.jpg
new file mode 100644
index 0000000..b46f0fe
--- /dev/null
+++ b/edaweb/static/images/IMG_20220823_154457137_HDR.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_20221023_163821542.jpg b/edaweb/static/images/IMG_20221023_163821542.jpg
new file mode 100644
index 0000000..f2284df
--- /dev/null
+++ b/edaweb/static/images/IMG_20221023_163821542.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_20230812_132400733_HDR.jpg b/edaweb/static/images/IMG_20230812_132400733_HDR.jpg
new file mode 100644
index 0000000..9f46bdc
--- /dev/null
+++ b/edaweb/static/images/IMG_20230812_132400733_HDR.jpg
Binary files differ
diff --git a/edaweb/static/images/IMG_20230812_132632784_HDR.jpg b/edaweb/static/images/IMG_20230812_132632784_HDR.jpg
new file mode 100644
index 0000000..82060d1
--- /dev/null
+++ b/edaweb/static/images/IMG_20230812_132632784_HDR.jpg
Binary files differ
diff --git a/edaweb/static/images/JwL2S2V.png b/edaweb/static/images/JwL2S2V.png
new file mode 100755
index 0000000..9394c56
--- /dev/null
+++ b/edaweb/static/images/JwL2S2V.png
Binary files differ
diff --git a/edaweb/static/images/PXL_20250314_150055804.jpg b/edaweb/static/images/PXL_20250314_150055804.jpg
new file mode 100644
index 0000000..b660e18
--- /dev/null
+++ b/edaweb/static/images/PXL_20250314_150055804.jpg
Binary files differ
diff --git a/edaweb/static/images/Screenshot_20210520-221830.png b/edaweb/static/images/Screenshot_20210520-221830.png
new file mode 100644
index 0000000..33afee7
--- /dev/null
+++ b/edaweb/static/images/Screenshot_20210520-221830.png
Binary files differ
diff --git a/edaweb/static/images/Screenshot_20210527_122432.png b/edaweb/static/images/Screenshot_20210527_122432.png
new file mode 100644
index 0000000..33432ff
--- /dev/null
+++ b/edaweb/static/images/Screenshot_20210527_122432.png
Binary files differ
diff --git a/edaweb/static/images/Screenshot_20210620-144127.png b/edaweb/static/images/Screenshot_20210620-144127.png
new file mode 100644
index 0000000..dd9b43a
--- /dev/null
+++ b/edaweb/static/images/Screenshot_20210620-144127.png
Binary files differ
diff --git a/edaweb/static/images/Screenshot_20210620-231652.png b/edaweb/static/images/Screenshot_20210620-231652.png
new file mode 100644
index 0000000..5e2532d
--- /dev/null
+++ b/edaweb/static/images/Screenshot_20210620-231652.png
Binary files differ
diff --git a/edaweb/static/images/Screenshot_20210727-072303.png b/edaweb/static/images/Screenshot_20210727-072303.png
new file mode 100644
index 0000000..7c03644
--- /dev/null
+++ b/edaweb/static/images/Screenshot_20210727-072303.png
Binary files differ
diff --git a/edaweb/static/images/Screenshot_20210727-072344.png b/edaweb/static/images/Screenshot_20210727-072344.png
new file mode 100644
index 0000000..248644a
--- /dev/null
+++ b/edaweb/static/images/Screenshot_20210727-072344.png
Binary files differ
diff --git a/edaweb/static/images/Screenshot_20210727-223004.png b/edaweb/static/images/Screenshot_20210727-223004.png
new file mode 100644
index 0000000..dbe0630
--- /dev/null
+++ b/edaweb/static/images/Screenshot_20210727-223004.png
Binary files differ
diff --git a/edaweb/static/images/Screenshot_20210731-181223.png b/edaweb/static/images/Screenshot_20210731-181223.png
new file mode 100644
index 0000000..b1547be
--- /dev/null
+++ b/edaweb/static/images/Screenshot_20210731-181223.png
Binary files differ
diff --git a/edaweb/static/images/Screenshot_20210731-181229.png b/edaweb/static/images/Screenshot_20210731-181229.png
new file mode 100644
index 0000000..363c17b
--- /dev/null
+++ b/edaweb/static/images/Screenshot_20210731-181229.png
Binary files differ
diff --git a/edaweb/static/images/Screenshot_20210811-125853.png b/edaweb/static/images/Screenshot_20210811-125853.png
new file mode 100644
index 0000000..7d45240
--- /dev/null
+++ b/edaweb/static/images/Screenshot_20210811-125853.png
Binary files differ
diff --git a/edaweb/static/images/Screenshot_20210818-203828.png b/edaweb/static/images/Screenshot_20210818-203828.png
new file mode 100644
index 0000000..8026a0c
--- /dev/null
+++ b/edaweb/static/images/Screenshot_20210818-203828.png
Binary files differ
diff --git a/edaweb/static/images/Screenshot_20210825-131938.png b/edaweb/static/images/Screenshot_20210825-131938.png
new file mode 100644
index 0000000..4b16443
--- /dev/null
+++ b/edaweb/static/images/Screenshot_20210825-131938.png
Binary files differ
diff --git a/edaweb/static/images/Startech.jpg b/edaweb/static/images/Startech.jpg
new file mode 100644
index 0000000..ba7f02f
--- /dev/null
+++ b/edaweb/static/images/Startech.jpg
Binary files differ
diff --git a/edaweb/static/images/a4-5000.jpg b/edaweb/static/images/a4-5000.jpg
new file mode 100644
index 0000000..f26fd66
--- /dev/null
+++ b/edaweb/static/images/a4-5000.jpg
Binary files differ
diff --git a/edaweb/static/images/aLvcFjj.png b/edaweb/static/images/aLvcFjj.png
new file mode 100755
index 0000000..c928ba7
--- /dev/null
+++ b/edaweb/static/images/aLvcFjj.png
Binary files differ
diff --git a/edaweb/static/images/aaaaahh.jpg b/edaweb/static/images/aaaaahh.jpg
new file mode 100644
index 0000000..8dac9d4
--- /dev/null
+++ b/edaweb/static/images/aaaaahh.jpg
Binary files differ
diff --git a/edaweb/static/images/anti_nft_b.png b/edaweb/static/images/anti_nft_b.png
new file mode 100644
index 0000000..61dfb99
--- /dev/null
+++ b/edaweb/static/images/anti_nft_b.png
Binary files differ
diff --git a/edaweb/static/images/arenas.jpg b/edaweb/static/images/arenas.jpg
new file mode 100644
index 0000000..2b2cac6
--- /dev/null
+++ b/edaweb/static/images/arenas.jpg
Binary files differ
diff --git a/edaweb/static/images/basil.jpg b/edaweb/static/images/basil.jpg
new file mode 100644
index 0000000..c63ba48
--- /dev/null
+++ b/edaweb/static/images/basil.jpg
Binary files differ
diff --git a/edaweb/static/images/bigshark.png b/edaweb/static/images/bigshark.png
new file mode 100644
index 0000000..a499fb1
--- /dev/null
+++ b/edaweb/static/images/bigshark.png
Binary files differ
diff --git a/edaweb/static/images/bob.gif b/edaweb/static/images/bob.gif
new file mode 100644
index 0000000..90b6dd5
--- /dev/null
+++ b/edaweb/static/images/bob.gif
Binary files differ
diff --git a/edaweb/static/images/braindamage.png b/edaweb/static/images/braindamage.png
new file mode 100644
index 0000000..c26e75f
--- /dev/null
+++ b/edaweb/static/images/braindamage.png
Binary files differ
diff --git a/edaweb/static/images/catonlap.jpg b/edaweb/static/images/catonlap.jpg
new file mode 100644
index 0000000..37e526b
--- /dev/null
+++ b/edaweb/static/images/catonlap.jpg
Binary files differ
diff --git a/edaweb/static/images/chuddy.jpg b/edaweb/static/images/chuddy.jpg
new file mode 100644
index 0000000..7d732f7
--- /dev/null
+++ b/edaweb/static/images/chuddy.jpg
Binary files differ
diff --git a/edaweb/static/images/cloudfree.png b/edaweb/static/images/cloudfree.png
new file mode 100644
index 0000000..71c6ff2
--- /dev/null
+++ b/edaweb/static/images/cloudfree.png
Binary files differ
diff --git a/edaweb/static/images/embedded_img2.png b/edaweb/static/images/embedded_img2.png
new file mode 100644
index 0000000..3eeea56
--- /dev/null
+++ b/edaweb/static/images/embedded_img2.png
Binary files differ
diff --git a/edaweb/static/images/embedded_img3.png b/edaweb/static/images/embedded_img3.png
new file mode 100644
index 0000000..2a55742
--- /dev/null
+++ b/edaweb/static/images/embedded_img3.png
Binary files differ
diff --git a/edaweb/static/images/embedded_img4.png b/edaweb/static/images/embedded_img4.png
new file mode 100644
index 0000000..5e6ec6d
--- /dev/null
+++ b/edaweb/static/images/embedded_img4.png
Binary files differ
diff --git a/edaweb/static/images/embedded_img5.PNG b/edaweb/static/images/embedded_img5.PNG
new file mode 100644
index 0000000..c96d284
--- /dev/null
+++ b/edaweb/static/images/embedded_img5.PNG
Binary files differ
diff --git a/edaweb/static/images/face.jpg b/edaweb/static/images/face.jpg
new file mode 100644
index 0000000..b123e53
--- /dev/null
+++ b/edaweb/static/images/face.jpg
Binary files differ
diff --git a/edaweb/static/images/first_server.jpg b/edaweb/static/images/first_server.jpg
new file mode 100644
index 0000000..8f537d7
--- /dev/null
+++ b/edaweb/static/images/first_server.jpg
Binary files differ
diff --git a/edaweb/static/images/fstab.png b/edaweb/static/images/fstab.png
new file mode 100644
index 0000000..6acaa83
--- /dev/null
+++ b/edaweb/static/images/fstab.png
Binary files differ
diff --git a/edaweb/static/images/graduation.jpg b/edaweb/static/images/graduation.jpg
new file mode 100644
index 0000000..89bafd7
--- /dev/null
+++ b/edaweb/static/images/graduation.jpg
Binary files differ
diff --git a/edaweb/static/images/greenboi.jpg b/edaweb/static/images/greenboi.jpg
new file mode 100644
index 0000000..7761c8a
--- /dev/null
+++ b/edaweb/static/images/greenboi.jpg
Binary files differ
diff --git a/edaweb/static/images/hpkvm_puttyconf.png b/edaweb/static/images/hpkvm_puttyconf.png
new file mode 100644
index 0000000..5e9852f
--- /dev/null
+++ b/edaweb/static/images/hpkvm_puttyconf.png
Binary files differ
diff --git a/edaweb/static/images/hpkvm_serialconf.png b/edaweb/static/images/hpkvm_serialconf.png
new file mode 100644
index 0000000..76122c1
--- /dev/null
+++ b/edaweb/static/images/hpkvm_serialconf.png
Binary files differ
diff --git a/edaweb/static/images/i5-1145G7.png b/edaweb/static/images/i5-1145G7.png
new file mode 100644
index 0000000..96b6fa3
--- /dev/null
+++ b/edaweb/static/images/i5-1145G7.png
Binary files differ
diff --git a/edaweb/static/images/iKVCEoy.png b/edaweb/static/images/iKVCEoy.png
new file mode 100755
index 0000000..05d6d70
--- /dev/null
+++ b/edaweb/static/images/iKVCEoy.png
Binary files differ
diff --git a/edaweb/static/images/icons.png b/edaweb/static/images/icons.png
new file mode 100644
index 0000000..4f19e72
--- /dev/null
+++ b/edaweb/static/images/icons.png
Binary files differ
diff --git a/edaweb/static/images/ioporn.jpg b/edaweb/static/images/ioporn.jpg
new file mode 100644
index 0000000..6608d31
--- /dev/null
+++ b/edaweb/static/images/ioporn.jpg
Binary files differ
diff --git a/edaweb/static/images/its_true_1.jpg b/edaweb/static/images/its_true_1.jpg
new file mode 100644
index 0000000..3dddfb3
--- /dev/null
+++ b/edaweb/static/images/its_true_1.jpg
Binary files differ
diff --git a/edaweb/static/images/its_true_2.jpg b/edaweb/static/images/its_true_2.jpg
new file mode 100644
index 0000000..b532a21
--- /dev/null
+++ b/edaweb/static/images/its_true_2.jpg
Binary files differ
diff --git a/edaweb/static/images/j1800.jpg b/edaweb/static/images/j1800.jpg
new file mode 100644
index 0000000..05db3a6
--- /dev/null
+++ b/edaweb/static/images/j1800.jpg
Binary files differ
diff --git a/edaweb/static/images/j5040.jpg b/edaweb/static/images/j5040.jpg
new file mode 100644
index 0000000..1a24299
--- /dev/null
+++ b/edaweb/static/images/j5040.jpg
Binary files differ
diff --git a/edaweb/static/images/j5040_server.jpg b/edaweb/static/images/j5040_server.jpg
new file mode 100644
index 0000000..9e2f3aa
--- /dev/null
+++ b/edaweb/static/images/j5040_server.jpg
Binary files differ
diff --git a/edaweb/static/images/kvm_1.png b/edaweb/static/images/kvm_1.png
new file mode 100644
index 0000000..fa05b15
--- /dev/null
+++ b/edaweb/static/images/kvm_1.png
Binary files differ
diff --git a/edaweb/static/images/kvm_2.png b/edaweb/static/images/kvm_2.png
new file mode 100644
index 0000000..6fca0f8
--- /dev/null
+++ b/edaweb/static/images/kvm_2.png
Binary files differ
diff --git a/edaweb/static/images/lackrack1.jpg b/edaweb/static/images/lackrack1.jpg
new file mode 100644
index 0000000..31c4f9d
--- /dev/null
+++ b/edaweb/static/images/lackrack1.jpg
Binary files differ
diff --git a/edaweb/static/images/lackrack2.jpg b/edaweb/static/images/lackrack2.jpg
new file mode 100644
index 0000000..9fda7d8
--- /dev/null
+++ b/edaweb/static/images/lackrack2.jpg
Binary files differ
diff --git a/edaweb/static/images/libreboot.jpg b/edaweb/static/images/libreboot.jpg
new file mode 100644
index 0000000..0bac72e
--- /dev/null
+++ b/edaweb/static/images/libreboot.jpg
Binary files differ
diff --git a/edaweb/static/images/liostore.png b/edaweb/static/images/liostore.png
new file mode 100644
index 0000000..43003ee
--- /dev/null
+++ b/edaweb/static/images/liostore.png
Binary files differ
diff --git a/edaweb/static/images/media_E9lTIldWUAEeT63.jpg b/edaweb/static/images/media_E9lTIldWUAEeT63.jpg
new file mode 100644
index 0000000..b30195a
--- /dev/null
+++ b/edaweb/static/images/media_E9lTIldWUAEeT63.jpg
Binary files differ
diff --git a/edaweb/static/images/media_FPus_2aXsAUhCu9.jpg b/edaweb/static/images/media_FPus_2aXsAUhCu9.jpg
new file mode 100644
index 0000000..398fadd
--- /dev/null
+++ b/edaweb/static/images/media_FPus_2aXsAUhCu9.jpg
Binary files differ
diff --git a/edaweb/static/images/media_FRlRqYKXoAIw82O.jpg b/edaweb/static/images/media_FRlRqYKXoAIw82O.jpg
new file mode 100644
index 0000000..b45a62b
--- /dev/null
+++ b/edaweb/static/images/media_FRlRqYKXoAIw82O.jpg
Binary files differ
diff --git a/edaweb/static/images/media_FeP_m8FXEAAqcJs.jpg b/edaweb/static/images/media_FeP_m8FXEAAqcJs.jpg
new file mode 100644
index 0000000..f3afe10
--- /dev/null
+++ b/edaweb/static/images/media_FeP_m8FXEAAqcJs.jpg
Binary files differ
diff --git a/edaweb/static/images/media_FfcNNZ2WYAAz97v.jpg b/edaweb/static/images/media_FfcNNZ2WYAAz97v.jpg
new file mode 100644
index 0000000..8ae39ad
--- /dev/null
+++ b/edaweb/static/images/media_FfcNNZ2WYAAz97v.jpg
Binary files differ
diff --git a/edaweb/static/images/media_FlBsMq2XwAATJX5.jpg b/edaweb/static/images/media_FlBsMq2XwAATJX5.jpg
new file mode 100644
index 0000000..7ba3ef1
--- /dev/null
+++ b/edaweb/static/images/media_FlBsMq2XwAATJX5.jpg
Binary files differ
diff --git a/edaweb/static/images/media_FlBsfDcWQAQhJdP.jpg b/edaweb/static/images/media_FlBsfDcWQAQhJdP.jpg
new file mode 100644
index 0000000..601dfbe
--- /dev/null
+++ b/edaweb/static/images/media_FlBsfDcWQAQhJdP.jpg
Binary files differ
diff --git a/edaweb/static/images/minecraft.png b/edaweb/static/images/minecraft.png
new file mode 100644
index 0000000..19210d2
--- /dev/null
+++ b/edaweb/static/images/minecraft.png
Binary files differ
diff --git a/edaweb/static/images/new_switch_opened.jpg b/edaweb/static/images/new_switch_opened.jpg
new file mode 100644
index 0000000..df797ed
--- /dev/null
+++ b/edaweb/static/images/new_switch_opened.jpg
Binary files differ
diff --git a/edaweb/static/images/newdiscord.jpg b/edaweb/static/images/newdiscord.jpg
new file mode 100644
index 0000000..d446602
--- /dev/null
+++ b/edaweb/static/images/newdiscord.jpg
Binary files differ
diff --git a/edaweb/static/images/nicfan.jpg b/edaweb/static/images/nicfan.jpg
new file mode 100644
index 0000000..6c2415e
--- /dev/null
+++ b/edaweb/static/images/nicfan.jpg
Binary files differ
diff --git a/edaweb/static/images/notstolenvalour4.png b/edaweb/static/images/notstolenvalour4.png
new file mode 100644
index 0000000..f9e82e2
--- /dev/null
+++ b/edaweb/static/images/notstolenvalour4.png
Binary files differ
diff --git a/edaweb/static/images/notstolenvalour5.png b/edaweb/static/images/notstolenvalour5.png
new file mode 100644
index 0000000..87c09da
--- /dev/null
+++ b/edaweb/static/images/notstolenvalour5.png
Binary files differ
diff --git a/edaweb/static/images/notstolenvalour8.png b/edaweb/static/images/notstolenvalour8.png
new file mode 100644
index 0000000..dcddf27
--- /dev/null
+++ b/edaweb/static/images/notstolenvalour8.png
Binary files differ
diff --git a/edaweb/static/images/oldpic.png b/edaweb/static/images/oldpic.png
new file mode 100644
index 0000000..20314e4
--- /dev/null
+++ b/edaweb/static/images/oldpic.png
Binary files differ
diff --git a/edaweb/static/images/pfsenseswissvpnportforwards.png b/edaweb/static/images/pfsenseswissvpnportforwards.png
new file mode 100644
index 0000000..34c05dc
--- /dev/null
+++ b/edaweb/static/images/pfsenseswissvpnportforwards.png
Binary files differ
diff --git a/edaweb/static/images/photo_2022-12-07_14-06-03.jpg b/edaweb/static/images/photo_2022-12-07_14-06-03.jpg
new file mode 100644
index 0000000..224b748
--- /dev/null
+++ b/edaweb/static/images/photo_2022-12-07_14-06-03.jpg
Binary files differ
diff --git a/edaweb/static/images/photo_5857106224099739202_y.jpg b/edaweb/static/images/photo_5857106224099739202_y.jpg
new file mode 100644
index 0000000..c0a5820
--- /dev/null
+++ b/edaweb/static/images/photo_5857106224099739202_y.jpg
Binary files differ
diff --git a/edaweb/static/images/powerani.gif b/edaweb/static/images/powerani.gif
new file mode 100644
index 0000000..1eb1a18
--- /dev/null
+++ b/edaweb/static/images/powerani.gif
Binary files differ
diff --git a/edaweb/static/images/profile_images_1598652418580963328_ENk7xKDw.jpg b/edaweb/static/images/profile_images_1598652418580963328_ENk7xKDw.jpg
new file mode 100644
index 0000000..775d4e9
--- /dev/null
+++ b/edaweb/static/images/profile_images_1598652418580963328_ENk7xKDw.jpg
Binary files differ
diff --git a/edaweb/static/images/rOJXmjG.png b/edaweb/static/images/rOJXmjG.png
new file mode 100755
index 0000000..58a4ae4
--- /dev/null
+++ b/edaweb/static/images/rOJXmjG.png
Binary files differ
diff --git a/edaweb/static/images/rack1.jpg b/edaweb/static/images/rack1.jpg
new file mode 100644
index 0000000..b5049e9
--- /dev/null
+++ b/edaweb/static/images/rack1.jpg
Binary files differ
diff --git a/edaweb/static/images/rack2.jpg b/edaweb/static/images/rack2.jpg
new file mode 100644
index 0000000..1c9bc11
--- /dev/null
+++ b/edaweb/static/images/rack2.jpg
Binary files differ
diff --git a/edaweb/static/images/router.jpg b/edaweb/static/images/router.jpg
new file mode 100644
index 0000000..587f0e0
--- /dev/null
+++ b/edaweb/static/images/router.jpg
Binary files differ
diff --git a/edaweb/static/images/russian_isp.png b/edaweb/static/images/russian_isp.png
new file mode 100644
index 0000000..5b529c0
--- /dev/null
+++ b/edaweb/static/images/russian_isp.png
Binary files differ
diff --git a/edaweb/static/images/russian_isp_abuse.png b/edaweb/static/images/russian_isp_abuse.png
new file mode 100644
index 0000000..96da244
--- /dev/null
+++ b/edaweb/static/images/russian_isp_abuse.png
Binary files differ
diff --git a/edaweb/static/images/russian_nginx_logs.jpg b/edaweb/static/images/russian_nginx_logs.jpg
new file mode 100644
index 0000000..3fe22d3
--- /dev/null
+++ b/edaweb/static/images/russian_nginx_logs.jpg
Binary files differ
diff --git a/edaweb/static/images/russian_yt.jpg b/edaweb/static/images/russian_yt.jpg
new file mode 100644
index 0000000..16f3767
--- /dev/null
+++ b/edaweb/static/images/russian_yt.jpg
Binary files differ
diff --git a/edaweb/static/images/s-l1600.jpg b/edaweb/static/images/s-l1600.jpg
new file mode 100644
index 0000000..34a82cf
--- /dev/null
+++ b/edaweb/static/images/s-l1600.jpg
Binary files differ
diff --git a/edaweb/static/images/s-l16001.jpg b/edaweb/static/images/s-l16001.jpg
new file mode 100644
index 0000000..0c34348
--- /dev/null
+++ b/edaweb/static/images/s-l16001.jpg
Binary files differ
diff --git a/edaweb/static/images/s-l500.jpg b/edaweb/static/images/s-l500.jpg
new file mode 100644
index 0000000..92307ff
--- /dev/null
+++ b/edaweb/static/images/s-l500.jpg
Binary files differ
diff --git a/edaweb/static/images/sample_ede336afca555579dd78f051e4a23feaa838716d.jpg b/edaweb/static/images/sample_ede336afca555579dd78f051e4a23feaa838716d.jpg
new file mode 100644
index 0000000..b146bc4
--- /dev/null
+++ b/edaweb/static/images/sample_ede336afca555579dd78f051e4a23feaa838716d.jpg
Binary files differ
diff --git a/edaweb/static/images/selfie.jpg b/edaweb/static/images/selfie.jpg
new file mode 100644
index 0000000..17730e8
--- /dev/null
+++ b/edaweb/static/images/selfie.jpg
Binary files differ
diff --git a/edaweb/static/images/server.jpg b/edaweb/static/images/server.jpg
new file mode 100644
index 0000000..50bc1d7
--- /dev/null
+++ b/edaweb/static/images/server.jpg
Binary files differ
diff --git a/edaweb/static/images/server2.jpg b/edaweb/static/images/server2.jpg
new file mode 100644
index 0000000..dda70b1
--- /dev/null
+++ b/edaweb/static/images/server2.jpg
Binary files differ
diff --git a/edaweb/static/images/shark1.jpg b/edaweb/static/images/shark1.jpg
new file mode 100644
index 0000000..394846c
--- /dev/null
+++ b/edaweb/static/images/shark1.jpg
Binary files differ
diff --git a/edaweb/static/images/shark2.jpg b/edaweb/static/images/shark2.jpg
new file mode 100644
index 0000000..2c432cd
--- /dev/null
+++ b/edaweb/static/images/shark2.jpg
Binary files differ
diff --git a/edaweb/static/images/shark3.jpg b/edaweb/static/images/shark3.jpg
new file mode 100644
index 0000000..5343254
--- /dev/null
+++ b/edaweb/static/images/shark3.jpg
Binary files differ
diff --git a/edaweb/static/images/shark3.png b/edaweb/static/images/shark3.png
new file mode 100644
index 0000000..22e8ff9
--- /dev/null
+++ b/edaweb/static/images/shark3.png
Binary files differ
diff --git a/edaweb/static/images/shun-hashimoto-mio-chibana.gif b/edaweb/static/images/shun-hashimoto-mio-chibana.gif
new file mode 100644
index 0000000..869793f
--- /dev/null
+++ b/edaweb/static/images/shun-hashimoto-mio-chibana.gif
Binary files differ
diff --git a/edaweb/static/images/sicp.jpg b/edaweb/static/images/sicp.jpg
new file mode 100644
index 0000000..575a456
--- /dev/null
+++ b/edaweb/static/images/sicp.jpg
Binary files differ
diff --git a/edaweb/static/images/startech_rack_1.jpg b/edaweb/static/images/startech_rack_1.jpg
new file mode 100644
index 0000000..1f66734
--- /dev/null
+++ b/edaweb/static/images/startech_rack_1.jpg
Binary files differ
diff --git a/edaweb/static/images/startech_rack_2.jpg b/edaweb/static/images/startech_rack_2.jpg
new file mode 100644
index 0000000..47f9aac
--- /dev/null
+++ b/edaweb/static/images/startech_rack_2.jpg
Binary files differ
diff --git a/edaweb/static/images/stolenvalour.jpg b/edaweb/static/images/stolenvalour.jpg
new file mode 100644
index 0000000..d666623
--- /dev/null
+++ b/edaweb/static/images/stolenvalour.jpg
Binary files differ
diff --git a/edaweb/static/images/sun.gif b/edaweb/static/images/sun.gif
new file mode 100644
index 0000000..674e32a
--- /dev/null
+++ b/edaweb/static/images/sun.gif
Binary files differ
diff --git a/edaweb/static/images/switch.jpg b/edaweb/static/images/switch.jpg
new file mode 100644
index 0000000..64ab20c
--- /dev/null
+++ b/edaweb/static/images/switch.jpg
Binary files differ
diff --git a/edaweb/static/images/switches.jpg b/edaweb/static/images/switches.jpg
new file mode 100644
index 0000000..8b56651
--- /dev/null
+++ b/edaweb/static/images/switches.jpg
Binary files differ
diff --git a/edaweb/static/images/t30.jpg b/edaweb/static/images/t30.jpg
new file mode 100644
index 0000000..949d14b
--- /dev/null
+++ b/edaweb/static/images/t30.jpg
Binary files differ
diff --git a/edaweb/static/images/techdome.jpg b/edaweb/static/images/techdome.jpg
new file mode 100644
index 0000000..74fe69c
--- /dev/null
+++ b/edaweb/static/images/techdome.jpg
Binary files differ
diff --git a/edaweb/static/images/telegrampic.jpg b/edaweb/static/images/telegrampic.jpg
new file mode 100644
index 0000000..ddadb8b
--- /dev/null
+++ b/edaweb/static/images/telegrampic.jpg
Binary files differ
diff --git a/edaweb/static/images/telegrampic2.jpg b/edaweb/static/images/telegrampic2.jpg
new file mode 100644
index 0000000..8b306ff
--- /dev/null
+++ b/edaweb/static/images/telegrampic2.jpg
Binary files differ
diff --git a/edaweb/static/images/testimonials.PNG b/edaweb/static/images/testimonials.PNG
new file mode 100644
index 0000000..8584cdd
--- /dev/null
+++ b/edaweb/static/images/testimonials.PNG
Binary files differ
diff --git a/edaweb/static/images/theNVMEVault.png b/edaweb/static/images/theNVMEVault.png
new file mode 100644
index 0000000..2cf31d7
--- /dev/null
+++ b/edaweb/static/images/theNVMEVault.png
Binary files differ
diff --git a/edaweb/static/images/twitterpic.jpg b/edaweb/static/images/twitterpic.jpg
new file mode 100644
index 0000000..57f16b4
--- /dev/null
+++ b/edaweb/static/images/twitterpic.jpg
Binary files differ
diff --git a/edaweb/static/images/v2Yfoou.jpg b/edaweb/static/images/v2Yfoou.jpg
new file mode 100755
index 0000000..f3a3249
--- /dev/null
+++ b/edaweb/static/images/v2Yfoou.jpg
Binary files differ
diff --git a/edaweb/static/images/vcss-blue.gif b/edaweb/static/images/vcss-blue.gif
new file mode 100644
index 0000000..c373b2a
--- /dev/null
+++ b/edaweb/static/images/vcss-blue.gif
Binary files differ
diff --git a/edaweb/static/images/www.gif b/edaweb/static/images/www.gif
new file mode 100644
index 0000000..48fbd13
--- /dev/null
+++ b/edaweb/static/images/www.gif
Binary files differ
diff --git a/edaweb/static/images/wxbKYVv.png b/edaweb/static/images/wxbKYVv.png
new file mode 100755
index 0000000..75feb10
--- /dev/null
+++ b/edaweb/static/images/wxbKYVv.png
Binary files differ
diff --git a/edaweb/static/images/x200.jpg b/edaweb/static/images/x200.jpg
new file mode 100644
index 0000000..d7e48eb
--- /dev/null
+++ b/edaweb/static/images/x200.jpg
Binary files differ
diff --git a/edaweb/static/images/xeon.jpg b/edaweb/static/images/xeon.jpg
new file mode 100644
index 0000000..4b1eef9
--- /dev/null
+++ b/edaweb/static/images/xeon.jpg
Binary files differ
diff --git a/edaweb/static/images/xeon_motherboard.jpg b/edaweb/static/images/xeon_motherboard.jpg
new file mode 100644
index 0000000..a17819f
--- /dev/null
+++ b/edaweb/static/images/xeon_motherboard.jpg
Binary files differ
diff --git a/edaweb/static/index.md b/edaweb/static/index.md
new file mode 100644
index 0000000..e6bf91d
--- /dev/null
+++ b/edaweb/static/index.md
@@ -0,0 +1,38 @@
+site now also avaliable under the domain [boymoder.blog](https://boymoder.blog)!
+
+![yaoi](/img/shun-hashimoto-mio-chibana.gif)
+
+## haiiiiiii
+my name is eden and im a 23yo (boymoder/[fujoshi](https://www.urbandictionary.com/define.php?term=fujoshi)) computer science/robotics PhD student. i made my own website to encourage others to do so too.
+i'll post my thoughts on here sometimes, and use this site to link to other stuff i host [more about me](/thought?id=2).
+
+[click here for a random image of lio fotia](/random?tags=lio_fotia)
+
+[click here for a random KawoShin image](/random?tags=nagisa_kaworu+ikari_shinji+yaoi)
+
+## FOSS alternative services
+
+- [nextcloud - dropbox (+ much more!) alternative](https://nc.eda.gay)
+- [git server - github alternative](https://git.eda.gay/)
+- [jellyfin - web player for ~~legally downloaded~~ TV and films](https://jellyfin.eda.gay) - RIP emby!
+
+[see the services im running right now](/services) (takes a couple seconds to load)
+
+these sites are hosted on my [homelab system](https://wiki.eda.gay)
+
+![startech 8u rack cropped](/img/GcyexeCW0AAYssz.jpg?w=300&h=5000)
+![startech 12u rack cropped](/img/Startech.jpg?h=250&w=5000)
+
+## nice websites
+- [wiby.me](http://wiby.me/) - search engine for old style websites with limited javascript (my site used to be on here but it got blacklisted for some reason?)
+- [dysmorph.nekoweb.org](https://dysmorph.nekoweb.org/) - a site that is very based because it looks similar
+- [transsexual.org](https://web.archive.org/web/20010802032136/http://transsexual.org/Toon.html) - awesome and relatable transsexual comics from a website that's slightly older than me
+- [norfolkchurches.co.uk](http://www.norfolkchurches.co.uk/norwichintro.htm) - site about all the churches in norwich (and norfolk!), the city that has far too many medieval churches than it knows what to do with. this site is preciesly what the internet should be, the muted ramblings of someone with an expert knowledge on his preferred niche interest. without any javascript. nice if, like me, you have a middling interest in theology
+- [boymoder.network](https://boymoder.network/) - website for boymoder awareness
+- [4chan.org/lgbt/](https://boards.4channel.org/lgbt/) - but dont blame me if u catch brainworms
+- [https://www.math.uni-bielefeld.de/~sillke/Twister/fun/elevator-fun90.html](https://www.math.uni-bielefeld.de/~sillke/Twister/fun/elevator-fun90.html) any website with a URL like this is gonna be good
+- [boymoder.moe](https://nyaomidev.github.io/boymoder.moe/)
+- [boymoders.com](https://boymoders.com)
+- [john.citrons.xyz](https://john.citrons.xyz/) - for the nice 'ads' featured at the bottom of my page
+
+
diff --git a/edaweb/static/papers/aai.pdf b/edaweb/static/papers/aai.pdf
new file mode 100644
index 0000000..af2223b
--- /dev/null
+++ b/edaweb/static/papers/aai.pdf
Binary files differ
diff --git a/edaweb/static/papers/ar2.pdf b/edaweb/static/papers/ar2.pdf
new file mode 100644
index 0000000..2b1693f
--- /dev/null
+++ b/edaweb/static/papers/ar2.pdf
Binary files differ
diff --git a/edaweb/static/robots.txt b/edaweb/static/robots.txt
new file mode 100644
index 0000000..04154e9
--- /dev/null
+++ b/edaweb/static/robots.txt
@@ -0,0 +1,7 @@
+User-agent: *
+Allow: /
+User-agent: Googlebot-Image
+Disallow: *
+User-agent: *
+Disallow: /random*
+
diff --git a/edaweb/static/style.css b/edaweb/static/style.css
new file mode 100644
index 0000000..6069ebf
--- /dev/null
+++ b/edaweb/static/style.css
@@ -0,0 +1,211 @@
+html {
+ background-color: black;
+ font-family: 'Open Sans', sans-serif;
+ font-size: small;
+}
+
+body {
+ /* background: linear-gradient(red,orange,yellow,green,blue,indigo,violet); */
+ margin-left: 0px;
+ margin-right: 0px;
+ margin-bottom: 0px;
+ position: relative;
+ background: black;
+}
+
+#wrapper {
+ background-color: #f1f3f3;
+ max-width: 974px;
+ min-width: 850px;
+ margin: auto;
+ margin-top: -10px;
+}
+
+header {
+ background-color: #f1f3f3;
+}
+
+
+#headerflex {
+ display: flex;
+}
+
+#headers {
+ flex-grow: 1;
+}
+
+header nav {
+ flex-grow: 1;
+ display: inline;
+}
+
+#externallinks {
+ background-color: black;
+ text-align: center;
+}
+
+#externallinks nav ul li a {
+ color: #f1f3f3;
+ font-size: smaller;
+}
+
+header div {
+ padding-left: 20px;
+ /* padding-bottom: 10px; */
+}
+
+nav ul {
+ margin: 0;
+ padding: 0;
+}
+
+nav li {
+ display: inline-block;
+ list-style-type: none;
+}
+
+nav a {
+ text-decoration: none;
+ display: block;
+ padding: 5px 6px 5px 6px;
+ color: black;
+}
+
+#TheTitle {
+ text-decoration: none;
+ color: black;
+}
+
+#TheTitle h1 {
+ padding-left: 6px;
+}
+
+#articles {
+ text-align: left;
+ background-color: white;
+}
+
+article section table {
+ font-family: monospace;
+}
+
+article section table td {
+ text-align: right;
+}
+
+#randomImage img {
+ max-width: 65%;
+}
+
+#content img {
+ max-width: 65%;
+}
+
+aside {
+ width: 30%;
+ padding-left: 15px;
+ margin-left: 15px;
+ float: right;
+}
+
+#contents {
+ font-size: xx-small;
+}
+
+#contents ul {
+ padding-left: 12px;
+ line-height: 3px;
+ }
+
+#contents ul li {
+ list-style: none;
+}
+
+#tags {
+ font-size: xx-small;
+}
+
+#sidebarImage {
+ transform: translateX(10px);
+}
+
+.header_linker {
+ font-size: x-small;
+}
+
+#diaryentry {
+ list-style-type: none;
+}
+
+#diaryentry ul {
+ list-style-type: none;
+ display: inline-block;
+}
+
+#diaryentry ul li img {
+ max-height: 150px;
+ margin-top: 10px;
+}
+
+.qnaheader {
+ font-weight: bold;
+}
+
+blockquote span {
+ color: #789922;
+}
+
+blockquote span::before {
+ content: ">";
+}
+
+.running {
+ background-color: green;
+}
+
+.notRunning {
+ background-color: red;
+}
+
+header img {
+ max-height: 110px;
+}
+
+form #number_input {
+ width: 50px;
+ margin-left: 10px;
+}
+
+form #url_input {
+ margin-left: 10px;
+}
+
+body div div {
+ padding-left: 10px;
+ padding-right: 10px;
+}
+
+footer {
+ padding: 300px 10px 5px 20px;
+ margin-bottom: 300px;
+ font-size: xx-small;
+ align-content: center;
+}
+
+#footer_banners {
+ display: flex;
+ align-items: center;
+
+}
+
+@media (max-width: 1023px) {
+ body {
+ margin: 0;
+ padding: 0;
+ }
+
+ #wrapper {
+ min-width: 100%;
+ margin-top: 0;
+ }
+}
diff --git a/edaweb/templates/diary.html.j2 b/edaweb/templates/diary.html.j2
new file mode 100644
index 0000000..f6604f7
--- /dev/null
+++ b/edaweb/templates/diary.html.j2
@@ -0,0 +1,26 @@
+{% extends "template.html.j2" %}
+{% block content %}
+ <h4>this page might not be up-to-date if my diary account is search banned</h4>
+ <p>if in doubt check my <a href="https://twitter.com/{{ diary_account }}">diary account</a> <br> <a href="https://shadowban.yuzurisa.com/{{ diary_account }}">check if i'm currently search banned</a></p>
+ <dl>
+ {% for dt, entries in diary.items() %}
+ <dt><a href="{{ entries[0]['link'] }}">{{ dt }}</a></dt>
+ <dd>
+ <ol id="diaryentry">
+ {% for entry in entries %}
+ {% if entry["images"] != [] %}
+ <ul>
+ {% for img in entry["images"] %}
+ <li><a href="{{ img }}" target='_blank'><img src="{{ img }}"></a></li>
+ {% endfor %}
+ </ul>
+ {% endif %}
+ <li>
+ <p>{{ entry["text"] }}</p>
+ </li>
+ {% endfor %}
+ </ol>
+ </dd>
+ {% endfor %}
+ </dl>
+{% endblock %}
diff --git a/edaweb/templates/discord.html.j2 b/edaweb/templates/discord.html.j2
new file mode 100644
index 0000000..597fb4b
--- /dev/null
+++ b/edaweb/templates/discord.html.j2
@@ -0,0 +1,5 @@
+{% extends "template.html.j2" %}
+{% block content %}
+ <p>You can contact me on discord (telegram preferred):</p>
+ <h1>{{discord}}</h1>
+ {% endblock %} \ No newline at end of file
diff --git a/edaweb/templates/index.html.j2 b/edaweb/templates/index.html.j2
new file mode 100644
index 0000000..d6c08d8
--- /dev/null
+++ b/edaweb/templates/index.html.j2
@@ -0,0 +1,24 @@
+{% extends "template.html.j2" %}
+{% block content %}
+ <aside>
+ <a><i>{{ "%d days until FFS" % days_till_ffs.days }}</i></a>
+ <section id="recent_thoughts">
+ <h4>recent thoughts:</h4>
+ <ul>
+ {% for id_, title in featured_thoughts %}
+ <li><a href="{{'/thought?id=%i' % id_}}">{{title}}</a></li>
+ {% endfor %}
+ </ul>
+ </section>
+ <img id="sidebar_img" alt="{{sidebar_img[0]}}" src="{{sidebar_img[1]}}">
+ </aside>
+ {{markdown|safe}}
+ <section id="recent_commits">
+ <h3>recent git commits:</h3>
+ <ul>
+ {% for commit in commits %}
+ <li>[<a href="{{commit['git_repo_url']}}">{{commit["repo"]}}</a>] {+{{commit["stats"]["additions"]}}; -{{commit["stats"]["deletions"]}}} <a href="{{commit['git_commit_url']}}">{{commit["message"]}}</a> - <a href="{{commit['github_commit_url']}}">github mirror</a></li>
+ {% endfor %}
+ </ul>
+ </section>
+{% endblock %}
diff --git a/edaweb/templates/isocd.html.j2 b/edaweb/templates/isocd.html.j2
new file mode 100644
index 0000000..3c532c0
--- /dev/null
+++ b/edaweb/templates/isocd.html.j2
@@ -0,0 +1,32 @@
+{% extends "template.html.j2" %}
+{% block content %}
+ <p>As discussed <a href="https://nitter.eda.gay/estrogenizedboy/status/1490322471215636480#m">here</a>, I will post a GNU/Linux install CD to you for free (provided you live in the United Kingdom). Just fill out the form below:</p>
+ <form action="/getisocd" method="POST">
+ <label for="email">*Email:</label>
+ <input type="email" id="email" name="email" required><br><br>
+ <label for="iso">*ISO:</label>
+ <select name="iso" id="iso" required>
+ {% for iso in iso_options %}
+ <option value="{{ iso }}">{{ iso }}</option>
+ {% endfor %}
+ </select><br><br>
+ <p>Sadly there is an upper limit on ISOs of 700Mb coz thats what u can fit on a cd ;_; so big isos arent listed</p><br>
+ <label for="name">*Name:</label>
+ <input id="name" name="name" required><br><br>
+ <label for="house">*House Number/Name:</label>
+ <input id="house" name="house" required><br><br>
+ <label for="street">*Street:</label>
+ <input id="street" name="street" required><br><br>
+ <label for="city">City:</label>
+ <input id="city" name="city"><br><br>
+ <label for="county">County:</label>
+ <input id="county" name="county"><br><br>
+ <label for="postcode">*Postcode:</label>
+ <input id="postcode" name="postcode" required><br><br>
+ <input type="submit" value="Submit">
+ </form>
+ <p>I make no promises how long this will actually take, but you might get a special present too uwu</p>
+ <p>btw if u abuse this service i'll just ignore u</p>
+ <h3>Testimonials</h3>
+ <a href="https://nitter.eda.gay/stellarnate_/status/1492468706412269569"><img src="/img/testimonials.PNG"></a>
+{% endblock %} \ No newline at end of file
diff --git a/edaweb/templates/isocd_confirmation.html.j2 b/edaweb/templates/isocd_confirmation.html.j2
new file mode 100644
index 0000000..81045a8
--- /dev/null
+++ b/edaweb/templates/isocd_confirmation.html.j2
@@ -0,0 +1,16 @@
+{% extends "template.html.j2" %}
+{% block content %}
+ <p>Your order has been placed. Expect a confirmation email to {{ email }} when I can be bothered to sort it out.</p>
+ <br>
+ <p>Your order/reference id is {{ id_ }}</p>
+ <br>
+ <p>The details were as follows:</p>
+ <table>
+ {% for k, v in req.items() %}
+ <tr>
+ <td>{{ k.upper() }}</td>
+ <td>{{ v }}</td>
+ </tr>
+ {% endfor %}
+ </table>
+{% endblock %} \ No newline at end of file
diff --git a/edaweb/templates/nhdl.html.j2 b/edaweb/templates/nhdl.html.j2
new file mode 100644
index 0000000..5ab62c2
--- /dev/null
+++ b/edaweb/templates/nhdl.html.j2
@@ -0,0 +1,8 @@
+{% extends "template.html.j2" %}
+{% block content %}
+ <form action="/nhdlredirect" method="POST">
+ <label for="nhentai">nHentai.net number:</label>
+ <input type="text" id="number_input" name="number_input"><br><br>
+ <input type="submit" value="Download">
+ </form>
+{% endblock %} \ No newline at end of file
diff --git a/edaweb/templates/questions.html.j2 b/edaweb/templates/questions.html.j2
new file mode 100644
index 0000000..eb58380
--- /dev/null
+++ b/edaweb/templates/questions.html.j2
@@ -0,0 +1,17 @@
+{% extends "template.html.j2" %}
+{% block content %}
+ <h4><a href="{{ qnas_link }}">ask a question!</a></h4>
+ <dl>
+ {% for id_, link, dt, question, answer, host in qnas %}
+ {% if host == "curiouscat" %}
+ <dt class="qnaheader"><a href="{{ link }}">{{ dt.isoformat() }}</a> - {{ host }}</dt>
+ {% else %}
+ <dt class="qnaheader">{{ dt.isoformat() }} - {{ host }}</dt>
+ {% endif %}
+ <dd>
+ <dt class="question"><p>{{ question }}</p></dt>
+ <dd class="answer"><p>{{ answer }}</p></dd>
+ </dd>
+ {% endfor %}
+ </dl>
+{% endblock %}
diff --git a/edaweb/templates/random.html.j2 b/edaweb/templates/random.html.j2
new file mode 100644
index 0000000..76b433b
--- /dev/null
+++ b/edaweb/templates/random.html.j2
@@ -0,0 +1,22 @@
+{% extends "template.html.j2" %}
+{% block content %}
+ <aside id="tags">
+ <h1>current search tags: (click to remove)</h1>
+ <ul>
+ {% for tag in sbi.searchTags %}
+ <li><a href={{"/random?tags=" + "+".join(sbi.remove_tag(tag))}}>{{tag}}</a></li>
+ {% endfor %}
+ </ul>
+ <h1>this image's tags:</h1>
+ <ul>
+ {% for tag in sbi.tags %}
+ <li><a href={{"/random?tags=" + "+".join(sbi.searchTags + [tag])}}>{{tag}}</a></li>
+ {% endfor %}
+ </ul>
+ </aside>
+ <section id="randomImage">
+ <a href={{sbi.source}}><img src={{localimg}}></a>
+ <h1><a href={{"/random?tags=" + "+".join(sbi.searchTags)}}>generate another</a></h1>
+ <h2><a href={{sbi.source}}>artist link</a></h2>
+ </section>
+{% endblock %} \ No newline at end of file
diff --git a/edaweb/templates/services.html.j2 b/edaweb/templates/services.html.j2
new file mode 100644
index 0000000..9f42c7f
--- /dev/null
+++ b/edaweb/templates/services.html.j2
@@ -0,0 +1,59 @@
+{% extends "template.html.j2" %}
+{% block content %}
+<article id=statusTables>
+ <section id=docker>
+ <h2>docker</h2>
+ <ul>
+ {% for host, containers in docker["containers"].items() %}
+ <h4>{{ "%s - %s" % (host[0], host[1]) }}</h4>
+ <table>
+ {% for name, status, image in containers %}
+ <tr>
+ <td>{{ name }}</td>
+ {% if "Up" in status %}
+ <td class=running>{{ status }}</td>
+ {% else %}
+ <td class=notRunning>{{ status }}</td>
+ {% endif %}
+ <td>{{ image }}</td>
+ </tr>
+ {% endfor %}
+ </table>
+ {% endfor %}
+ </ul>
+ <p>{{ docker["cachetime"] }}</p>
+ </section>
+
+ <section id="torrents">
+ <h2>transmission</h2>
+ {% if trans == None %}
+ <p>Couldn't access the transmission API. Is docker container running?</p>
+ {% else %}
+ <table>
+ {% for k, v in trans.items() %}
+ <tr>
+ <td>{{ k }}</td>
+ <td>{{ v }}</td>
+ </tr>
+ {% endfor %}
+ </table>
+ {% endif %}
+ </section>
+
+ <section id=pihole>
+ <h2>pihole</h2>
+ {% if pihole == None %}
+ <p>Couldn't access the pihole API. Is docker container running?</p>
+ {% else %}
+ <table>
+ {% for k, v in pihole.items() %}
+ <tr>
+ <td>{{ k }}</td>
+ <td>{{ v }}</td>
+ </tr>
+ {% endfor %}
+ </table>
+ {% endif %}
+ </section>
+</article>
+{% endblock %} \ No newline at end of file
diff --git a/edaweb/templates/template.html.j2 b/edaweb/templates/template.html.j2
new file mode 100644
index 0000000..86618bc
--- /dev/null
+++ b/edaweb/templates/template.html.j2
@@ -0,0 +1,76 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta name="abuseipdb-verification" content="3IuG6dik" />
+ <link rel='stylesheet' href="{{url_for('static', filename='style.css')}}">
+ <link rel="shortcut icon" href="/img/greenboi.jpg?h=16&w=16">
+ <title>eda.gay :: {{title}}</title>
+
+ <meta content="{{title}}" property="og:title" />
+ <meta content="Formerly chuck's" property="og:description" />
+ <meta content="https://boymoder.blog" property="og:url" />
+ <meta content="/img/greenboi.jpg?h=512&w=512" property="og:image" />
+ </head>
+ <body>
+ <div id=wrapper>
+ <header>
+ <div id="externallinks">
+ <nav>
+ <ul>
+ {% for name, link in links %}
+ <li>
+ <a href="{{link}}">{{name}}</a>
+ </li>
+ {% endfor %}
+ </ul>
+ </nav>
+ </div>
+ <div id=headerflex>
+ <div id=headers>
+ <a href="/" id=TheTitle><h1>{{title}}</h1></a>
+ <nav id=articles>
+ <ul>
+ {% for name, link in articles %}
+ <li>
+ <a href="{{link}}">{{name}}</a>
+ </li>
+ {% endfor %}
+ </ul>
+ </nav>
+ </div>
+ <a id=sidebarImage href="/">
+ <img alt="{{image[0]}}" src="{{image[1]}}">
+ </a>
+ </div>
+ </header>
+ <div id="content">
+ {% block content %}
+ {% endblock %}
+ </div>
+ <footer>
+ <p>this site is <a href="/thought?id=3">javascript free</a></p>
+ <a href="https://git.eda.gay/eda.gay.git/tree/LICENSE">sauce code released under gplv3</a> <a href="https://github.com/jwansek/eda.gay">alternate git link</a>
+ <div id="footer_banners">
+ <p>
+ <a href="http://jigsaw.w3.org/css-validator/check/referer">
+ <img style="border:0;width:88px;height:31px"
+ src="/img/vcss-blue.gif"
+ alt="Valid CSS!" />
+ </a>
+ </p>
+ <a href="http://www.freebsd.org/">
+ <img alt="Powered By FreeBSD" src="/img/powerani.gif">
+ </a>
+ <a href="https://web3isgoinggreat.com/">
+ <img src="/img/anti_nft_b.png" alt="anti-nft site ">
+ </a>
+ <img src="/img/www.gif">
+ <img src="/img/bob.gif">
+ <img src="/img/sun.gif">
+ <img src="/img/cloudfree.png">
+ </div>
+ <iframe src="https://john.citrons.xyz/embed?ref=eda.gay" style="padding-top:20px;margin-left:auto;display:block;margin-right:auto;max-width:732px;width:100%;height:94px;border:none;"></iframe>
+ </footer>
+ </div>
+ </body>
+</html>
diff --git a/edaweb/templates/thought.html.j2 b/edaweb/templates/thought.html.j2
new file mode 100644
index 0000000..2a5b519
--- /dev/null
+++ b/edaweb/templates/thought.html.j2
@@ -0,0 +1,29 @@
+{% extends "template.html.j2" %}
+{% block content %}
+ <aside>
+ <h4>{{ dt }}</h4>
+ {% if contents_html != "" %}
+ <h5>contents:</h5>
+ <div id="contents">
+ {{ contents_html|safe }}
+ </div>
+ {% endif %}
+ <h5>this category:</h5>
+ <ul>
+ <li><b><a href="{{'/thoughts#'+category.replace(' ', '_')}}">{{category}}</a></b></li>
+ </ul>
+ <h5>related thoughts:</h5>
+ <ul>
+ {% for id_, title, dt, category in related %}
+ <li><a href="{{'/thought?id=%i' % id_}}">{{title}}</a></li>
+ {% endfor %}
+ </ul>
+ <h5>other categories:</h5>
+ {% for category_name in othercategories %}
+ <ul>
+ <li><a href="{{'/thoughts#'+category_name.replace(' ', '_')}}">{{category_name}}</a></li>
+ </ul>
+ {% endfor %}
+ </aside>
+ {{ md_html|safe }}
+{% endblock %} \ No newline at end of file
diff --git a/edaweb/templates/thoughts.html.j2 b/edaweb/templates/thoughts.html.j2
new file mode 100644
index 0000000..bf06f57
--- /dev/null
+++ b/edaweb/templates/thoughts.html.j2
@@ -0,0 +1,12 @@
+{% extends "template.html.j2" %}
+{% block content %}
+ {% for category_name, thoughts in tree.items() %}
+ <h3 id="{{category_name.replace(' ', '_')}}">{{category_name}}</h3>
+ <dl>
+ {% for id_, title, dt in thoughts %}
+ <dt><a href="{{'/thought?id=%i' % id_}}">{{title}}</a></dt>
+ <dd>{{dt}}</dd>
+ {% endfor %}
+ </dl>
+ {% endfor %}
+{% endblock %} \ No newline at end of file