aboutsummaryrefslogtreecommitdiffstats
path: root/edaweb
diff options
context:
space:
mode:
authorjwansek <eddie.atten.ea29@gmail.com>2025-04-29 18:51:40 +0100
committerjwansek <eddie.atten.ea29@gmail.com>2025-04-29 18:51:40 +0100
commit0e8ada1eff8799437c9be1bc2af10d6198fa8cad (patch)
tree65d40b0c83fc2477306d561d8b98741e8e6585c6 /edaweb
parent9e077a790919e2980111262fbde500a5cee828f8 (diff)
downloadeda.gay-0e8ada1eff8799437c9be1bc2af10d6198fa8cad.tar.gz
eda.gay-0e8ada1eff8799437c9be1bc2af10d6198fa8cad.zip
Added inline HTML into blogposts, refactored a bit
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.py365
-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
-rw-r--r--edaweb/static/images/GcyexeCW0AAYssz.jpgbin0 -> 992976 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
-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
-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
-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
-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
-rw-r--r--edaweb/static/images/vcss-blue.gifbin0 -> 1759 bytes
-rw-r--r--edaweb/static/images/www.gifbin0 -> 1531 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.md36
-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.txt2
-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
205 files changed, 1725 insertions, 0 deletions
diff --git a/edaweb/app.py b/edaweb/app.py
new file mode 100644
index 0000000..6902fe4
--- /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("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("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..dab56e7
--- /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 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;")
+ 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..87af050
--- /dev/null
+++ b/edaweb/services.py
@@ -0,0 +1,365 @@
+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):
+ 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"]:
+ 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__":
+ 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/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/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/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/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/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/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/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/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..a676d59
--- /dev/null
+++ b/edaweb/static/index.md
@@ -0,0 +1,36 @@
+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
+- [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..c2aab7e
--- /dev/null
+++ b/edaweb/static/robots.txt
@@ -0,0 +1,2 @@
+User-agent: *
+Disallow: / \ No newline at end of file
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