From c515f24ab51c35ebf84f3f9244bf682a328a9a54 Mon Sep 17 00:00:00 2001 From: Ari Archer Date: Thu, 6 Jun 2024 02:22:49 +0300 Subject: [PATCH] Improve index, add badges Signed-off-by: Ari Archer --- captcha.key | Bin 0 -> 2048 bytes requirements.txt | 2 + src/aw/__init__.py | 26 ++++ src/aw/c.py | 7 + src/aw/const.py | 7 + src/aw/models.py | 57 ++++++++- src/aw/views.py | 39 ++++++ src/static/badges/badge-yellow.png | Bin 0 -> 883 bytes src/static/badges/badge.png | Bin 0 -> 2731 bytes src/static/css/base.css | 14 +- src/static/css/index.css | 119 +++++++++++------ src/static/js/index.js | 21 ++- src/templates/base.j2 | 2 +- src/templates/index.j2 | 197 ++++++++++++++++++++++++++--- 14 files changed, 426 insertions(+), 65 deletions(-) create mode 100644 captcha.key create mode 100644 src/aw/c.py create mode 100644 src/static/badges/badge-yellow.png create mode 100644 src/static/badges/badge.png diff --git a/captcha.key b/captcha.key new file mode 100644 index 0000000000000000000000000000000000000000..4461e6e1c604441a6c94965f69b1d22e2c20983d GIT binary patch literal 2048 zcmV+b2>@Gy?D96p4qpZH|?e41&2uK0(d^{N>B)V^CRp?g0s6-5Xl< zz~-dh8>d9PkiL8XbP|wf^9La3_!2Dvq~r0OChfWAXe&m2w?g0&%^Bcv%BsT z0hut-?b_`I$XZASK|;Rd;3`f2*LHFx;g8ecf_N6oVJ;p&910 z^N%gN5?P&WobjE4$<6g*>HSShAK9>XVK{BgP8hHa05xX>Rass*B+~C*P%O>>Zo$S+ z)$P58F8;zPlYwcc)GqNSoojIw-aJ;aQ+$X$xq)p}PCP&3K~f(7ol7GqbmB6EZFP}iS>yL*I{v}CTh@cbBv7O8pLD8A1QKu@_AoP$ zB*^BU{gvnJH+MSMFm2hiFRmu#blP`CSn&K_lWXo{!03q4B6Ef3X{I1CY-i#`XDQcZ z!irZb2;;Y1vVjk(l$}4qYB-0;Z9BJ=hi5cLRi8XBE578$vbYe;7H~+KwKtsYg1lsJ zpy+hi_N`Gq1&JSvdAR4k4AEXLKrAFdawz}DIDRm}Y2qr9!q( zex9t)G`zORP3xVJ7jSms8uGRs8i+y$XqgB&?g6}zmYyf#5(tW$ucY?2*@A8FQ^F8v z+OaD;+~(=;A&f=`KR)G-Cm?h6Fl>z93Dz&IEteMTPSx?;W%9LZ>pA!x#u&-K49;le zKREsefUn7KHQ^T_qySDFXmZ5=?~K9;vpJdc@zo_AObg^kOg(FHKCfO>GZ?oZbBtZ= zegmRHfO^q2wde+H!|v7WMQ%?CmFuJYXsl;CVW@3wq`&tuIP|@Lu=7lya|#TbwT?6A z2Cxt>+5#PAZ)Q*(JVk{2*(Qj;?yKlqVnqpgMI42Ap_mEuqC%K_$(Tu6k;ymd+4?ZE zVnxa2KOxmpi=O|EW?ICKC?qD2l<)8 z^2+T2+DmMwaprE>1=VLZam%``%5<(sD;m6cD0L0Ba2!?62iDl~%2bXj)WS2A80(YS@UgSvmm3nD2DTmayH^>}JZ{wv)d9=8rW z=}F3qx)Ey}ZT5$!nOqZQQ{D~EQR4~$l{e(i{ggERj3^>yrAl{=aF3UizjmVf8%CJG zMlX&@mKEcfUHJpEQ;KOZ z^5{(+hjE2C#hYvQGU*bw&lv05T&ypFxKOKQKyU1;Zt_Wc@6)o%!_I(K+;1Y*Qzk5Q z|5A>6NgSzi{6JW7G17Zef=_sIxefA3Fgi{-p_H-qbUl#gV(Tc^D67=(7FbsZ5>w=H z^UHXBW!HXtoM`%|?@QGaCQnbnUqtgV$g9sVeAxmQ?34nNMuTj`$U^ybv4}Y?#g19f zenXy$cfz$`$ss@46oq+ch3PYCpBuxch7BkN+nx{LC_w*f|0+mJ|7B#OsDdNybB1f! zQ3a6`taLjYA_4Atgwa6=33jFk2)exap^zt8B|3HZq`RjG34^?>O3p4i! z5TmoTQA+yoq(=z?vUV3-4@kuEZE&)Dv$TiPbDdX>c!?PqsOKEMEfCD=5jns1(8`>S zT)9w|NQ`N}N&+_2J}QMUZ&&?5&myf$H+bZ*_&_MP#^V2W#5@CfJ;zVpM5;ktSWIAQ zpF&5Oc&SV1UCQ&DXmgg9izRFOd!OQ#)TPDTV963TRa$V7?fBKll zq;EY(3H?!5s}>2)8%7zl!w{=J1oBgJ+lk^Y+_C=SYI*apT)ki4_opF`q^#-inO9gg zzB~xTSzb^Dh_15Y``mGyHt9UWWtcmE#jFjX0o58(mx3eoR18n8t4hKp#*ivd{C&~* zp4HF3yN{Ik>QlH3u=rzAbBg?A1+TpbA59Oj$G(mp|X# em_fN^sYE5c2cs~&t6+loT2ZE6L0oOhge|K0Itn@f literal 0 HcmV?d00001 diff --git a/requirements.txt b/requirements.txt index a0cbb23..85f6972 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ flask types-flask flask-sqlalchemy +flask-ishuman +crc4 diff --git a/src/aw/__init__.py b/src/aw/__init__.py index 5dd8209..374ecfa 100644 --- a/src/aw/__init__.py +++ b/src/aw/__init__.py @@ -3,6 +3,7 @@ """ari.lt""" import datetime +import hashlib import os import sys from typing import Any @@ -23,6 +24,8 @@ def create_app(name: str) -> flask.Flask: app.config["PREFERRED_URL_SCHEME"] = "http" if app.debug else "https" app.config["DOMAIN"] = "ari.lt" + app.config["SECRET_KEY"] = os.urandom(4096) + app.config["SESSION_COOKIE_SAMESITE"] = "strict" app.config["SESSION_COOKIE_SECURE"] = True app.config["SESSION_COOKIE_HTTPONLY"] = True @@ -31,12 +34,35 @@ def create_app(name: str) -> flask.Flask: app.config["SQLALCHEMY_ENGINE_OPTIONS"] = {"pool_pre_ping": True} app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False + app.config["CAPTCHA_PEPPER_FILE"] = "captcha.key" + app.config["CAPTCHA_EXPIRY"] = 60 * 10 # 10 minutes + app.config["CAPTCHA_CHARSET"] = "abdefghmnqrtyABDEFGHLMNRTY2345689#@%?!" + app.config["CAPTCHA_RANGE"] = (4, 6) + app.config["USE_SESSION_FOR_NEXT"] = True + from .models import Admin, db + + with app.app_context(): + db.init_app(app) + db.create_all() + + # if db.session.query(Admin).count() < 1: + # print("Creating an admin account...") + + # full_name: str = input("Full name: ") + # email: str = input("Email: ") + # salt: bytes = os.urandom(64) + # pwhash: bytes = hashlib.sha3_512(salt + input("Password: ")).digest() + from .views import views app.register_blueprint(views, url_prefix="/") + from .c import c + + c.init_app(app) + @app.context_processor # type: ignore def _() -> Any: """Context processor""" diff --git a/src/aw/c.py b/src/aw/c.py new file mode 100644 index 0000000..a6aa029 --- /dev/null +++ b/src/aw/c.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""Captcha""" + +import flask_ishuman + +c: flask_ishuman.IsHuman = flask_ishuman.IsHuman() diff --git a/src/aw/const.py b/src/aw/const.py index c07181f..61c90ca 100644 --- a/src/aw/const.py +++ b/src/aw/const.py @@ -5,3 +5,10 @@ from typing import Final HUGEINT_MAX: Final[int] = (10**65) - 1 + +USERNAME_SIZE: Final[int] = 64 + +NAME_SIZE: Final[int] = 256 +WEBSITE_SIZE: Final[int] = 256 +EMAIL_CT_SIZE: Final[int] = 256 +COMMENT_SIZE: Final[int] = 1024 diff --git a/src/aw/models.py b/src/aw/models.py index 6b662d3..7b61033 100644 --- a/src/aw/models.py +++ b/src/aw/models.py @@ -2,15 +2,20 @@ # -*- coding: utf-8 -*- """DB Models""" +import datetime +import string import typing as t from decimal import Decimal +from secrets import SystemRandom +import crc4 from flask_sqlalchemy import SQLAlchemy -from sqlalchemy import DECIMAL, Dialect, TypeDecorator +from sqlalchemy import DECIMAL, DateTime, Dialect, TypeDecorator, Unicode from . import const db: SQLAlchemy = SQLAlchemy() +rand: SystemRandom = SystemRandom() class HugeUInt(TypeDecorator): # type: ignore @@ -54,7 +59,55 @@ class Counter(db.Model): id: int = db.Column( db.Integer, - primary_key=True, unique=True, + primary_key=True, ) count: int = db.Column(HugeUInt()) + + def __init__(self, count: int = 0) -> None: + assert count >= 0 and count <= const.HUGEINT_MAX, "count out of range" + self.count: int = count + + +class Comment(db.Model): + """Comment""" + + id: int = db.Column( + db.Integer, + unique=True, + primary_key=True, + ) + + name: str = db.Column(Unicode(const.NAME_SIZE)) + website: t.Optional[str] = db.Column(db.String(const.WEBSITE_SIZE), nullable=True) + + email_ct: bytes = db.Column(db.LargeBinary(length=const.EMAIL_CT_SIZE)) + key: bytes = db.Column(db.LargeBinary(length=32)) + + comment: str = db.Column(Unicode(const.COMMENT_SIZE)) + confirmed: bool = db.Column(db.Boolean, default=False) + posted: datetime.datetime = db.Column(DateTime, nullable=False) + + token: str = db.Column(db.String(32)) + + def __init__( + self, name: str, website: t.Optional[str], email: str, comment: str + ) -> None: + assert len(name) <= const.NAME_SIZE, "Name too long" + assert len(website or "") <= const.WEBSITE_SIZE, "Website too long" + assert len(email) <= const.EMAIL_CT_SIZE, "Email too long" + assert len(comment) <= const.COMMENT_SIZE, "Comment too long" + + self.name: str = name + self.website: t.Optional[str] = website + + self.key: bytes = rand.randbytes(32) + self.email_ct: bytes = crc4.rc4(self.email.encode(), self.key) # type: ignore + + self.comment: str = comment + self.confirmed: bool = False + self.posted: datetime.datetime = datetime.datetime.now(datetime.timezone.utc) + + self.token: str = "".join( + rand.choices(string.ascii_letters + string.digits, k=32) + ) diff --git a/src/aw/views.py b/src/aw/views.py index b3ba3cb..215c11d 100644 --- a/src/aw/views.py +++ b/src/aw/views.py @@ -8,6 +8,7 @@ import flask from werkzeug.wrappers import Response from .routing import Bp +from .c import c views: Bp = Bp("views", __name__) @@ -55,6 +56,14 @@ def license() -> flask.Response: with open("LICENSE", "r") as fp: return flask.Response(fp.read(), mimetype="text/plain") +@views.post("/") +def comment() -> flask.Response: + """publish a comment""" + return flask.Response( + c.new().rawpng(), + mimetype="image/png" + ) + @views.get("/git", defaults={"_": ""}) @views.get("/git/", defaults={"_": ""}) @@ -77,3 +86,33 @@ def favicon() -> Response: mimetype="image/vnd.microsoft.icon", ) ) + +@views.get("/captcha.png") +def captcha() -> flask.Response: + """CAPTCHA""" + return flask.Response( + c.new().rawpng(), + mimetype="image/png" + ) + + +@views.get("/badge.png") +def badge() -> Response: + """Website badge""" + return flask.redirect( + flask.url_for( + "static", + filename="badges/badge.png", + ) + ) + + +@views.get("/badge-yellow.png") +def badge_yellow() -> Response: + """Website badge (yellow)""" + return flask.redirect( + flask.url_for( + "static", + filename="badges/badge-yellow.png", + ) + ) diff --git a/src/static/badges/badge-yellow.png b/src/static/badges/badge-yellow.png new file mode 100644 index 0000000000000000000000000000000000000000..4f965c7f5a0c27d54155dc76718aea482fe24182 GIT binary patch literal 883 zcmV-(1C0EMP)aGfHBfLup_+{thnBV}}v+`xbWxNsc^ z&^cV>0x{kOPnf)&;m=A06z#GyXUG}y<8U}D_|0as`P0p<8G-k(yjrcAIheZP9x&SB zaA?Nj@p;+X{_<(p%*Uk}-tBgM;2r_DS|H$lyZ?1G|Mc^vm8*-i`?l4p0G4oHy3gI+ z!%Zt!7j5?}5v(b|MlIk@xt#=hPUoDm!XqaxU`ldTeJs)(`m@^ZQCw%cvDSS-9Ho<&ycqY7{o zy;wOPIxqj;-hb$7B7AHrL22hSt$^r2N1eQ&V}am7{^?F%H~=|@0fLk zfVBk6K?EE$L~Ci|9FE5=(CEdLw2di%S9QH!_iD)Tx0fQkYn_utez_K__k41k3)m~c z{@JB;H1eWo^?+Ja9$YNmHbR8V{+(K%>g?foaHuc!8yzg>4*(Nx^eAbaYfCVDFuEx{ z1n3p0&S6fCu07#LdfI~>FJi4f4{Bt$)UOK|FF4@ZA82&rWBbvesXbdoV3T@X?f3|S z+AQtU9#c;=uhj1#f?*IF^r9;jq0vGpFRdi>5HzxK8;3tV5SwPKJsa7&eoalPFYUX4 zODEfLsS=LkqTEcqe(_ltr4a*LCFNR)y^=9GRz-kqMWK*bHE3?mIfmYQ^t_wHjcR13 zd?=h$w3a-U_OMfrnyx7cSW7j9OJk8-oq0g*7==qH9D{3Hk|NZv_APwtgL3tY`8H+f zI#TH*1YA?95nQSU9#chNV|v}$SGhMu&%!RR?RHJr!II>^0~IfU&7ob?`L+tKGu@ zFpGSBaB6E{`=EKhG1M;fe;q#nT)Gjh2>1oa`Swdt53AJ2HH@nUOt1uK;muHoAyEui zf1~iC(vuBcPD^qGjQTFu-gTSq>)nssuvu>i90AY&0N5rjUI9;T^3|-cOa=e|002ov JPDHLkV1me`nmGUf literal 0 HcmV?d00001 diff --git a/src/static/badges/badge.png b/src/static/badges/badge.png new file mode 100644 index 0000000000000000000000000000000000000000..9187534f129de9bf8bb38d901ce7b8876bbdba1c GIT binary patch literal 2731 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 * { - padding: 2em; +.split { + display: grid; + grid-template-columns: 3fr 1fr; + grid-gap: 0.5em; + align-items: stretch; + height: 100%; } -.split { - display: grid; - grid-gap: 0.5em; - grid-template-columns: 3fr 1fr; +.split > * { + display: flex; + flex-direction: column; + justify-content: center; + padding: 2em; + height: 100%; +} + +.esplit { + grid-template-columns: 1fr 1fr; } .split > :first-child { @@ -24,25 +34,13 @@ position: absolute; left: 0; right: 0; - top: 50%; - bottom: 50%; + top: 0; + bottom: 0; border-right: 2px solid var(--red); animation: grow 0.5s forwards; pointer-events: none; } -@keyframes grow { - from { - top: 50%; - bottom: 50%; - } - - to { - top: 0; - bottom: 0; - } -} - canvas#particles { position: fixed; top: 0; @@ -53,28 +51,78 @@ canvas#particles { pointer-events: none; } -@media (prefers-reduced-motion: reduce) { - *, - *::before, - *::after { - -webkit-animation-duration: 0.01ms !important; - animation-duration: 0.01ms !important; +form { + background-color: #030303; + width: 100%; + padding: 1em; + margin: 1em; + border-bottom: 1px solid var(--red); +} - -webkit-animation-iteration-count: 1 !important; - animation-iteration-count: 1 !important; +form > button { + background-color: #111; + border: none; + padding: 0.5em; + width: 100%; + cursor: pointer; +} - -webkit-transition-duration: 0.01ms !important; - -o-transition-duration: 0.01ms !important; - transition-duration: 0.01ms !important; +.form-group { + display: grid; + grid-template-columns: 8em auto; + grid-gap: 1em; + margin: 0.4em; +} - scroll-behavior: auto !important; +.form-group * { + border: none; + padding: 0.3em; +} + +.form-group > :last-child { + background-color: #111; + resize: vertical; + min-height: 3em; +} + +.captcha { + display: grid; + place-items: center; + margin: 0.5em; +} + +.captcha > img { + display: block; + cursor: pointer; +} + +#comments > div { + background-color: #111; + padding: 0.5em; + margin: 0.5em; +} + +#comments > div button { + border: none; + background-color: #000; + cursor: pointer; + border-bottom: 1px solid var(--red); +} + +@keyframes grow { + from { + transform: scaleY(0); + } + to { + transform: scaleY(1); } } -@media only screen and (max-width: 1000px) { +@media only screen and (max-width: 1250px) { .split { grid-template-columns: 1fr; grid-template-rows: auto auto; + align-items: unset; } .split > :first-child { @@ -90,11 +138,6 @@ canvas#particles { display: none; } - .split { - grid-template-columns: 1fr; - border-bottom: 2px dashed gray; - } - .split > :last-child { word-break: break-all; } diff --git a/src/static/js/index.js b/src/static/js/index.js index 0951e53..1576515 100644 --- a/src/static/js/index.js +++ b/src/static/js/index.js @@ -33,8 +33,9 @@ document.addEventListener("DOMContentLoaded", function () { canvas.height = window.innerHeight; let particles = []; + const particle_density = 1.5e-5; + let num_particles; - const num_particles = 512; const particle_size = 2; const avoidance_radius = 48; @@ -87,6 +88,10 @@ document.addEventListener("DOMContentLoaded", function () { class Particle { constructor() { + this.reset(); + } + + reset() { this.x = Math.random() * canvas.width; this.y = Math.random() * canvas.height; this.velocity = { @@ -96,19 +101,21 @@ document.addEventListener("DOMContentLoaded", function () { } update(particles) { - if (this.x < 0 || this.x > canvas.width) { + if (this.x <= 0 || this.x >= canvas.width) { this.velocity.x *= -1; + this.x = Math.max(Math.min(this.x, canvas.width), 0); } - if (this.y < 0 || this.y > canvas.height) { + if (this.y <= 0 || this.y >= canvas.height) { this.velocity.y *= -1; + this.y = Math.max(Math.min(this.y, canvas.height), 0); } let dx = mouse.x - this.x; let dy = mouse.y - this.y; let distance = Math.sqrt(dx * dx + dy * dy); - if (distance < avoidance_radius) { + if (distance < avoidance_radius && distance > 0) { this.velocity.x += (dx / distance) * 0.5; this.velocity.y += (dy / distance) * 0.5; } @@ -149,13 +156,17 @@ document.addEventListener("DOMContentLoaded", function () { draw() { ctx.beginPath(); ctx.arc(this.x, this.y, particle_size, 0, Math.PI * 2); - ctx.fillStyle = "rgba(255, 81, 71, 0.2)"; + ctx.fillStyle = "rgba(255, 81, 71, 0.5)"; ctx.fill(); } } function init() { particles = []; + num_particles = Math.floor( + particle_density * canvas.width * canvas.height, + ); + num_particles = Math.min(num_particles, 1024); for (let i = 0; i < num_particles; i++) { particles.push(new Particle()); } diff --git a/src/templates/base.j2 b/src/templates/base.j2 index 23f0e69..ed048f3 100644 --- a/src/templates/base.j2 +++ b/src/templates/base.j2 @@ -38,7 +38,7 @@
{% block header %}{% endblock %}
{% block main %}{% endblock %}
diff --git a/src/templates/index.j2 b/src/templates/index.j2 index 8c76172..bd8b899 100644 --- a/src/templates/index.j2 +++ b/src/templates/index.j2 @@ -4,7 +4,7 @@ {% block description %}Ari-web: A personal website of a Lithuanian open source developer Ari Archer (Arija A.) who provides different free, private, and open source services for others.{% endblock %} -{% block keywords %}ari::web, services, foss services, homepage, portfolio{% endblock %} +{% block keywords %}ari::web, services, foss services, homepage, portfolio, resume{% endblock %} {% block head %} @@ -47,22 +47,44 @@ {% block header %}

Ari.lt: Free and open source world of Ari Archer.

+ + {% endblock %} {% block main %}
-

Welcome to my website, visitor {{ visitor }}!

+

Welcome to my website, visitor {{ visitor }}!

+ +
+ Before I start explaining who I am, if you're here on this page because of one of the users + using ari-web services and you want to report abuse, please contact me using one of the + contacts listed on this page (such as the ari@ari.lt email) + or see the list of staff. You may also leave a comment on the guestbook. +

My name is Ari Archer also known as Arija A., but most commonly referred to as - Ari. I am a {{ ari_age }} year old, neurodivergent, transgender, open source developer from Lithuania who does - fun stuff in primarily Python (~{{ python_exp }} years) - and C (89, 99) (~{{ c_exp }} years). + Ari. I am a {{ ari_age }} year old, neurodivergent, transgender, open source developer from Lithuania who does + fun stuff in primarily Python (~{{ python_exp }} years) + and C (89, 99) (~{{ c_exp }} years).

- I've picked up programming at first in Python, writing various programs on my phone, and later on discovering a communty + I've picked up programming at first in Python, writing various programs on my phone, and later on discovering a community where I could share my code. At the time I didn't have access to a computer, so I used to play around with basic JavaScript on my local library computers. At around 2019 I got access to a personal computer, which is where my main FOSS journey started - @@ -72,12 +94,12 @@

I hated the limiting feeling of being on Microsoft Windows 10 very limiting, so soon after getting a personal computer I installed a free and open source Linux - kernel distribution - and I've never went back to Windows ever since then. Democracy, freedom, shareability - and customization ("hackability") are huge values for me. + kernel distribution - and I've never went back to Windows ever since then. Democracy, freedom, shareability + and customization ("hackability") are huge values for me.

- Technology is a big part of my life, and I am way past the stage where I find just technology facinating - + Technology is a big part of my life, and I am way past the stage where I find just technology fascinating - I find various problems and their optimal solutions very interesting and I like to come up with my own - even if it's "reinventing the wheel" or "impractical" - I like to dig deep into it and understand how it works at its core, rather than relying on high-level features of an @@ -95,6 +117,18 @@

  • Cooking - I really enjoy putting together a healthy, vegan dish, which's recipe I can share with others.
  • And also blogging, as archiving things and thoughts has been a huge part of my life since forever.
  • + +

    + But generally, I am pretty much open to try everything at least once if I have the time and energy to do so. +

    + +

    + People often describe my personality as kind, accepting, open-minded, and nonjudgmental due to the way I interact + with the world and people - I am always accepting of people no matter what they've gone through or are + going through. They also see my logical side and describe me as intelligent, analytical, and creative + because of how I tend to approach various logical problems. Though, this side of me can sometimes be + overpowered by my emotional side if I lose control of my emotions. +

    @@ -106,10 +140,13 @@
  • Name: Arija A. (Ari Archer)
  • Pronouns: She/Her
  • Age: {{ ari_age }} years old.
  • +
  • Education: Primary school, middle school, high school (ongoing).
  • Country: Lithuania (Lietuva).
  • -
  • Languages: Lithuanian (native), English (B2), German (basic).
  • +
  • Languages: Lithuanian (native, {{ ari_age }}y), English (B2, {{ ari_age - 8 }}y), German (basic, {{ ari_age - 12 }}y).
  • Programming Experience: {{ programming_exp }} years ({{python_exp }}y in Python, {{ c_exp }}y in C).
  • Skills: Backend web development in Python using Flask, basic front-end development (HTML, JS, CSS, SCSS) and SEO (search engine optimization), software and library development in C and Python, Linux and intermediate Linux systems administration, technical documentation and specifications, application of different mathematical methods.
  • +
  • Learning: Better communication skills, self-improvement, cryptography (post-quantum cryptography), working on mental health and emotional grounding.
  • +
  • Personality: Recognised as kind, open-minded, intelligent, analytical, non-Judgmental, chill, anxious, generous, introverted, independent, caring, helpful, and supportive by people in my life.
  • Contacts @@ -125,14 +162,65 @@
    "Talk is cheap. Show me the code."
    -
    -        - Linus Torvalds, creator of Linux
    -        
    +
           - Linus Torvalds, creator of Linux
  • - That was a short introduction of who I am and what I do. + This is a brief description of me as a person, I probably cannot fit all of it in a single page. I do hope that + this is a good introduction to what I do, what skills I posses. +

    + +

    + Current status: +

    + +
    +
    This is a status
    +Hello world
    +Last updated: 2024-06-06 00:17:11 UTC
    +
    + + + +

    + Many of my links, social media, etc. can be found at: +

    + +
    +

    Contacts

    + +
    + +

    Activity

    + + + +

    Projects

    + + + +

    + Note that these are not the only projects I've ever worked on. These are a few + highlights that I find neat personally for differnt reasons. There don't showcase all + of my abilities, although these are practical examples of things I work on + sometimes.

    # Staff

    @@ -142,7 +230,8 @@ Feel free to visit their websites and show appriciation :).

    -
    +
    +
    @@ -170,6 +259,16 @@
    Namesulian.eu
    +
    + +
    +

    + Ari.lt is mainly a personal site, although it is not limited to being just a personal website. + I, together with a couple of staff members, host some services on + a shared server. Some of these services are a bit locked down or have aggressive policies to prevent spam + as our team is small. +

    +

    # Services

    @@ -236,7 +335,7 @@ pb.ari.lt - db.cubiq.dev + Private PocketBase instance Private database storage for Github: TheCubiq db.cubiq.dev @@ -246,10 +345,74 @@ t1nklas.lt - git.kappach.at + Forgejo instance for kappach.at Git forge instance of KappaChat - An extensible Matrix client written in C. git.kappach.at
    + +

    # Guestbook

    + +

    + If you want to interact with the community feel free to leave your comments here! You will require an + email address to prevent spam and impersonation, it will be listed, although not as text as it will + be encrypted server-side using RC4 (a fast, but insecure cipher) + with a 32-byte (256 bit) key. Check your mailbox when you comment as you will need to verify the comment. +

    + +
    +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + An image CAPTCHA + Click the image above to reload and get a new CAPTCHA +
    + +
    + + +
    + + +
    + +
    + +
    + Report any impersonation to the owner of this website: Ari Archer. +
    + +
    +
    +

    #1: Cool Person (https://example.com/) <> at 2024-06-05 11:11:11 UTC says...

    +
    Cool website, but you should kill yourself!
    +Meow!
    +
    + +
    +

    #1: Cool Person (https://example.com/) <> at 2024-06-05 11:11:11 UTC says...

    +
    Cool website, but you should kill yourself!
    +Meow!
    +
    +
    + {% endblock %}