diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 5fd41de..0000000 --- a/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.DS_Store -.env -__pycache__ \ No newline at end of file diff --git a/README.md b/README.md index 5799d7b..487c548 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # [PKRM.DEV](https://pkrm.dev) -This is the repo for my personal website ([pkrm.dev](https://pkrm.dev)). +This is the repo for my personal website ([pkrm.dev](https://pkrm.dev)). Heavily inspired by the "Hello Friend NG" HUGO Theme, found [here](https://github.com/rhazdon/hugo-theme-hello-friend-ng). diff --git a/app/static/css/about.css b/app/static/css/about.css deleted file mode 100644 index 100e909..0000000 --- a/app/static/css/about.css +++ /dev/null @@ -1,109 +0,0 @@ -/* Import Montserrat font */ -@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@200;300;400;500;600;700&display=swap'); - -body { - margin: 0; - padding: 0; - font-family: 'Montserrat', sans-serif; - background-color: #242528; - color: #9F9FAA; -} - - -/* Navbar styles */ -nav { - display: flex; - justify-content: space-between; - align-items: center; - padding: 1rem 2rem; - background-color: #212223; - color: #9F9FAA; -} - -nav a { - text-decoration: none; - color: #9F9FAA; - font-size: 1.5rem; - margin: 0 1rem; -} - -nav a:hover { - color: #fff; -} - -.navbar-left { - display: flex; - justify-content: flex-start; - align-items: center; - margin-left: 15%; -} - -.navbar-right { - display: flex; - justify-content: flex-end; - align-items: center; - margin-right: 15%; -} - -/* Navbar media query for mobile */ -@media only screen and (max-width: 600px) { - nav a { - margin: 0; - font-size: 1rem; - } - - .navbar-left { - margin: 0 1rem; - } - - .navbar-right { - margin: 0 1rem; - } - - .navbar-right a { - margin-left: 1rem; - } -} - -/* Container styles */ -.container { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - font-size: 1.75rem; - text-align: center; - width: 600px; -} - -/* Continer media query for mobile */ -@media only screen and (max-width: 600px) { - .container { - font-size: 1.25rem; - width: 300px; - } - - .container p { - margin: 1rem 0 2rem 0; - } -} - - -/* Footer styles */ -footer { - position: absolute; - bottom: 0; - left: 50%; - transform: translateX(-50%); - font-size: 1rem; - text-align: center; - margin-bottom: 2rem; - width: 90vw; -} - -/* Footer media query for mobile */ -@media only screen and (max-width: 600px) { - footer { - bottom: -2rem; - } -} \ No newline at end of file diff --git a/app/static/css/contact.css b/app/static/css/contact.css deleted file mode 100644 index ab8e95b..0000000 --- a/app/static/css/contact.css +++ /dev/null @@ -1,186 +0,0 @@ -/* Import Montserrat font */ -@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@200;300;400;500;600;700&display=swap'); - -* { - font-family: 'Montserrat', sans-serif; -} - -body { - margin: 0; - padding: 0; - background-color: #242528; - color: #9F9FAA; -} - - -/* Navbar styles */ -nav { - display: flex; - justify-content: space-between; - align-items: center; - padding: 1rem 2rem; - background-color: #212223; - color: #9F9FAA; -} - -nav a { - text-decoration: none; - color: #9F9FAA; - font-size: 1.5rem; - margin: 0 1rem; -} - -nav a:hover { - color: #fff; -} - -.navbar-left { - display: flex; - justify-content: flex-start; - align-items: center; - margin-left: 15%; -} - -.navbar-right { - display: flex; - justify-content: flex-end; - align-items: center; - margin-right: 15%; -} - -/* Navbar media query for mobile */ -@media only screen and (max-width: 600px) { - nav a { - margin: 0; - font-size: 1rem; - } - - .navbar-left { - margin: 0 1rem; - } - - .navbar-right { - margin: 0 1rem; - } - - .navbar-right a { - margin-left: 1rem; - } -} - - -/* Container styles */ -.container { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - font-size: 1.75rem; - text-align: center; - border: 1px solid #9F9FAA; - border-radius: 5px; - padding: 2rem; - height: 450px; - width: 550px; -} - -.container h1 { - margin: 0 0 1rem 0; -} - -/* Continer media query for mobile */ -@media only screen and (max-width: 600px) { - .container { - font-size: 1.25rem; - width: 250px; - } - - .container h1 { - margin: 0 0 2rem 0; - } -} - -/* Form styles */ -form { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; -} - -input { - margin: 0.75rem 0; - padding: 0.5rem 1rem; - border: 1px solid #9F9FAA; - border-radius: 5px; - background-color: #242528; - color: #9F9FAA; - font-size: 1.25rem; - width: 500px; -} - -input[type="submit"] { - width: calc(500px + 2rem); - cursor: pointer; -} - -textarea { - margin: 0.75rem 0; - padding: 0.5rem 1rem; - border: 1px solid #9F9FAA; - border-radius: 5px; - background-color: #242528; - color: #9F9FAA; - font-size: 1.25rem; - height: 100px; - width: 500px; - resize: none; -} - -/* Form media query for mobile */ -@media only screen and (max-width: 600px) { - input { - width: 250px; - } - - input[type="submit"] { - width: calc(250px + 2rem); - } - - textarea { - width: 250px; - } -} - -/* Success/Error messages */ -.success-message { - color: #71A172; - font-size: 1.25rem; - margin-top: 0.5rem; -} - -.error-message { - color: #B16161; - font-size: 1.25rem; - margin-top: 0.5rem; -} - - -/* Footer styles */ -footer { - position: absolute; - bottom: 0; - left: 50%; - transform: translateX(-50%); - font-size: 1rem; - text-align: center; - margin-bottom: 2rem; - width: 90vw; -} - -/* Footer media query for mobile */ -@media only screen and (max-width: 600px) { - footer { - bottom: -2rem; - } -} \ No newline at end of file diff --git a/app/static/css/index.css b/app/static/css/index.css deleted file mode 100644 index 3e6d017..0000000 --- a/app/static/css/index.css +++ /dev/null @@ -1,155 +0,0 @@ -/* Import Montserrat font */ -@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@200;300;400;500;600;700&display=swap'); - -body { - margin: 0; - padding: 0; - font-family: 'Montserrat', sans-serif; - background-color: #242528; - color: #9F9FAA; -} - - -/* Navbar styles */ -nav { - display: flex; - justify-content: space-between; - align-items: center; - padding: 1rem 2rem; - background-color: #212223; - color: #9F9FAA; -} - -nav a { - text-decoration: none; - color: #9F9FAA; - font-size: 1.5rem; - margin: 0 1rem; -} - -nav a:hover { - color: #fff; -} - -.navbar-left { - display: flex; - justify-content: flex-start; - align-items: center; - margin-left: 15%; -} - -.navbar-right { - display: flex; - justify-content: flex-end; - align-items: center; - margin-right: 15%; -} - -/* Navbar media query for mobile */ -@media only screen and (max-width: 600px) { - nav a { - margin: 0; - font-size: 1rem; - } - - .navbar-left { - margin: 0 1rem; - } - - .navbar-right { - margin: 0 1rem; - } - - .navbar-right a { - margin-left: 1rem; - } -} - - -/* Container styles */ -.container { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - height: 80vh; - margin: 0; - text-align: center; - font-size: 1.75rem; -} - -.text { - width: 500px; -} - -.text h1 { - margin-bottom: 0; -} - -.text p { - margin: 2rem 0 3rem 0; -} - -/* Icon styles */ - -.icons { - width: 100%; -} - -.icon { - margin: 0 1rem; - height: 50px; - width: 50px; - color: #9F9FAA; -} - -/* Icons media query for mobile */ -@media only screen and (max-width: 600px) { - .icon { - margin: 0 0.5rem; - } -} - -.icon:hover { - cursor: pointer; - opacity: 0.6; - transition: opacity 0.25s ease-in-out; - -} - -/* Continer media query for mobile */ -@media only screen and (max-width: 600px) { - .text { - font-size: 1.5rem; - width: 300px; - } - - .text p { - margin: 1rem 0 2rem 0; - } - - .icon { - height: 40px; - width: 40px; - } -} - - -/* Footer styles */ -footer { - position: absolute; - bottom: 0; - left: 50%; - transform: translateX(-50%); - font-size: 1rem; - text-align: center; - margin-bottom: 2rem; - width: 90vw; -} - -/* Footer media query for mobile */ -@media only screen and (max-width: 600px) { - footer { - bottom: -2rem; - } -} \ No newline at end of file diff --git a/app/static/github.png b/app/static/github.png deleted file mode 100644 index 3cdff93..0000000 Binary files a/app/static/github.png and /dev/null differ diff --git a/app/static/gnupg.png b/app/static/gnupg.png deleted file mode 100644 index 7efc415..0000000 Binary files a/app/static/gnupg.png and /dev/null differ diff --git a/app/static/js/altcha.js b/app/static/js/altcha.js deleted file mode 100644 index a586871..0000000 --- a/app/static/js/altcha.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Minified by jsDelivr using Terser v5.19.2. - * Original file: /gh/altcha-org/altcha@main/dist/altcha.js - * - * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files - */ -var je=Object.defineProperty,Ge=(e,t,n)=>t in e?je(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,x=(e,t,n)=>(Ge(e,"symbol"!=typeof t?t+"":t,n),n);function Z(){}function de(e){return e()}function ie(){return Object.create(null)}function H(e){e.forEach(de)}function ge(e){return"function"==typeof e}function Ae(e,t){return e!=e?t==t:e!==t||e&&"object"==typeof e||"function"==typeof e}function Se(e){return 0===Object.keys(e).length}function m(e,t){e.appendChild(t)}function Ve(e,t,n){const r=Me(e);if(!r.getElementById(t)){const e=v("style");e.id=t,e.textContent=n,Ue(r,e)}}function Me(e){if(!e)return document;const t=e.getRootNode?e.getRootNode():e.ownerDocument;return t&&t.host?t:e.ownerDocument}function Ue(e,t){return m(e.head||e,t),t.sheet}function q(e,t,n){e.insertBefore(t,n||null)}function I(e){e.parentNode&&e.parentNode.removeChild(e)}function v(e){return document.createElement(e)}function G(e){return document.createElementNS("http://www.w3.org/2000/svg",e)}function Fe(e){return document.createTextNode(e)}function M(){return Fe(" ")}function D(e,t,n,r){return e.addEventListener(t,n,r),()=>e.removeEventListener(t,n,r)}function c(e,t,n){null==n?e.removeAttribute(t):e.getAttribute(t)!==n&&e.setAttribute(t,n)}function Ye(e){return Array.from(e.childNodes)}function le(e,t,n){e.classList.toggle(t,!!n)}function Ze(e,t,{bubbles:n=!1,cancelable:r=!1}={}){return new CustomEvent(e,{detail:t,bubbles:n,cancelable:r})}function Oe(e){const t={};return e.childNodes.forEach((e=>{t[e.slot||"default"]=!0})),t}let B;function O(e){B=e}function ee(){if(!B)throw new Error("Function called outside component initialization");return B}function Be(e){ee().$$.on_mount.push(e)}function He(e){ee().$$.on_destroy.push(e)}function We(){const e=ee();return(t,n,{cancelable:r=!1}={})=>{const o=e.$$.callbacks[t];if(o){const c=Ze(t,n,{cancelable:r});return o.slice().forEach((t=>{t.call(e,c)})),!c.defaultPrevented}return!0}}const F=[],z=[];let Y=[];const oe=[],me=Promise.resolve();let J=!1;function we(){J||(J=!0,me.then(C))}function Pe(){return we(),me}function Q(e){Y.push(e)}const K=new Set;let U=0;function C(){if(0!==U)return;const e=B;do{try{for(;U-1===e.indexOf(r)?t.push(r):n.push(r))),n.forEach((e=>e())),Y=t}const De=new Set;function Ke(e,t){e&&e.i&&(De.delete(e),e.i(t))}function ze(e,t,n){const{fragment:r,after_update:o}=e.$$;r&&r.m(t,n),Q((()=>{const t=e.$$.on_mount.map(de).filter(ge);e.$$.on_destroy?e.$$.on_destroy.push(...t):H(t),e.$$.on_mount=[]})),o.forEach(Q)}function Je(e,t){const n=e.$$;null!==n.fragment&&(Xe(n.after_update),H(n.on_destroy),n.fragment&&n.fragment.d(t),n.on_destroy=n.fragment=null,n.ctx=[])}function Qe(e,t){-1===e.$$.dirty[0]&&(F.push(e),we(),e.$$.dirty.fill(0)),e.$$.dirty[t/31|0]|=1<{const c=r.length?r[0]:n;return a.ctx&&o(a.ctx[t],a.ctx[t]=c)&&(!a.skip_bound&&a.bound[t]&&a.bound[t](c),u&&Qe(e,t)),n})):[],a.update(),u=!0,H(a.before_update),a.fragment=!!r&&r(a.ctx),t.target){if(t.hydrate){const e=Ye(t.target);a.fragment&&a.fragment.l(e),e.forEach(I)}else a.fragment&&a.fragment.c();t.intro&&Ke(e.$$.fragment),ze(e,t.target,t.anchor),C()}O(s)}let be;function P(e,t,n,r){var o;const c=null==(o=n[e])?void 0:o.type;if(t="Boolean"===c&&"boolean"!=typeof t?null!=t:t,!r||!n[e])return t;if("toAttribute"===r)switch(c){case"Object":case"Array":return null==t?null:JSON.stringify(t);case"Boolean":return t?"":null;case"Number":return t??null;default:return t}else switch(c){case"Object":case"Array":return t&&JSON.parse(t);case"Boolean":default:return t;case"Number":return null!=t?+t:t}}function tt(e,t,n,r,o,c){let i=class extends be{constructor(){super(e,n,o),this.$$p_d=t}static get observedAttributes(){return Object.keys(t).map((e=>(t[e].attribute||e).toLowerCase()))}};return Object.keys(t).forEach((e=>{Object.defineProperty(i.prototype,e,{get(){return this.$$c&&e in this.$$c?this.$$c[e]:this.$$d[e]},set(n){var r;n=P(e,n,t),this.$$d[e]=n,null==(r=this.$$c)||r.$set({[e]:n})}})})),r.forEach((e=>{Object.defineProperty(i.prototype,e,{get(){var t;return null==(t=this.$$c)?void 0:t[e]}})})),c&&(i=c(i)),e.element=i,i}"function"==typeof HTMLElement&&(be=class extends HTMLElement{constructor(e,t,n){super(),x(this,"$$ctor"),x(this,"$$s"),x(this,"$$c"),x(this,"$$cn",!1),x(this,"$$d",{}),x(this,"$$r",!1),x(this,"$$p_d",{}),x(this,"$$l",{}),x(this,"$$l_u",new Map),this.$$ctor=e,this.$$s=t,n&&this.attachShadow({mode:"open"})}addEventListener(e,t,n){if(this.$$l[e]=this.$$l[e]||[],this.$$l[e].push(t),this.$$c){const n=this.$$c.$on(e,t);this.$$l_u.set(t,n)}super.addEventListener(e,t,n)}removeEventListener(e,t,n){if(super.removeEventListener(e,t,n),this.$$c){const e=this.$$l_u.get(t);e&&(e(),this.$$l_u.delete(t))}}async connectedCallback(){if(this.$$cn=!0,!this.$$c){let e=function(e){return()=>{let t;return{c:function(){t=v("slot"),"default"!==e&&c(t,"name",e)},m:function(e,n){q(e,t,n)},d:function(e){e&&I(t)}}}};if(await Promise.resolve(),!this.$$cn)return;const t={},n=Oe(this);for(const r of this.$$s)r in n&&(t[r]=[e(r)]);for(const e of this.attributes){const t=this.$$g_p(e.name);t in this.$$d||(this.$$d[t]=P(t,e.value,this.$$p_d,"toProp"))}this.$$c=new this.$$ctor({target:this.shadowRoot||this,props:{...this.$$d,$$slots:t,$$scope:{ctx:[]}}});const r=()=>{this.$$r=!0;for(const e in this.$$p_d)if(this.$$d[e]=this.$$c.$$.ctx[this.$$c.$$.props[e]],this.$$p_d[e].reflect){const t=P(e,this.$$d[e],this.$$p_d,"toAttribute");null==t?this.removeAttribute(this.$$p_d[e].attribute||e):this.setAttribute(this.$$p_d[e].attribute||e,t)}this.$$r=!1};this.$$c.$$.after_update.push(r),r();for(const e in this.$$l)for(const t of this.$$l[e]){const n=this.$$c.$on(e,t);this.$$l_u.set(t,n)}this.$$l={}}}attributeChangedCallback(e,t,n){var r;this.$$r||(e=this.$$g_p(e),this.$$d[e]=P(e,n,this.$$p_d,"toProp"),null==(r=this.$$c)||r.$set({[e]:this.$$d[e]}))}disconnectedCallback(){this.$$cn=!1,Promise.resolve().then((()=>{this.$$cn||(this.$$c.$destroy(),this.$$c=void 0)}))}$$g_p(e){return Object.keys(this.$$p_d).find((t=>this.$$p_d[t].attribute===e||!this.$$p_d[t].attribute&&t.toLowerCase()===e))||e}});class nt{constructor(){x(this,"$$"),x(this,"$$set")}$destroy(){Je(this,1),this.$destroy=Z}$on(e,t){if(!ge(t))return Z;const n=this.$$.callbacks[e]||(this.$$.callbacks[e]=[]);return n.push(t),()=>{const e=n.indexOf(t);-1!==e&&n.splice(e,1)}}$set(e){this.$$set&&!Se(e)&&(this.$$.skip_bound=!0,this.$$set(e),this.$$.skip_bound=!1)}}const rt="4";typeof window<"u"&&(window.__svelte||(window.__svelte={v:new Set})).v.add(rt);const _e="KGZ1bmN0aW9uKCl7InVzZSBzdHJpY3QiO2NvbnN0IHI9bmV3IFRleHRFbmNvZGVyO2Z1bmN0aW9uIGMoZSl7cmV0dXJuWy4uLm5ldyBVaW50OEFycmF5KGUpXS5tYXAobj0+bi50b1N0cmluZygxNikucGFkU3RhcnQoMiwiMCIpKS5qb2luKCIiKX1hc3luYyBmdW5jdGlvbiBsKGUsbixhKXtyZXR1cm4gYyhhd2FpdCBjcnlwdG8uc3VidGxlLmRpZ2VzdChhLnRvVXBwZXJDYXNlKCksci5lbmNvZGUoZStuKSkpfWFzeW5jIGZ1bmN0aW9uIGkoZSxuLGE9IlNIQS0yNTYiLG89MWU3KXtjb25zdCBzPURhdGUubm93KCk7Zm9yKGxldCB0PTA7dDw9bzt0KyspaWYoYXdhaXQgbChuLHQsYSk9PT1lKXJldHVybntudW1iZXI6dCx0b29rOkRhdGUubm93KCktc307cmV0dXJuIG51bGx9b25tZXNzYWdlPWFzeW5jIGU9Pntjb25zdHthbGc6bixjaGFsbGVuZ2U6YSxtYXg6byxzYWx0OnN9PWUuZGF0YXx8e307aWYoYSYmcyl7Y29uc3QgdD1hd2FpdCBpKGEscyxuLG8pO3NlbGYucG9zdE1lc3NhZ2UodCYmey4uLnQsd29ya2VyOiEwfSl9ZWxzZSBzZWxmLnBvc3RNZXNzYWdlKG51bGwpfX0pKCk7Cg==",se=typeof window<"u"&&window.Blob&&new Blob([atob(_e)],{type:"text/javascript;charset=utf-8"});function it(){let e;try{if(e=se&&(window.URL||window.webkitURL).createObjectURL(se),!e)throw"";return new Worker(e)}catch{return new Worker("data:application/javascript;base64,"+_e)}finally{e&&(window.URL||window.webkitURL).revokeObjectURL(e)}}const lt=1e7,ot=new TextEncoder;function st(e){return[...new Uint8Array(e)].map((e=>e.toString(16).padStart(2,"0"))).join("")}async function ct(e=1e5,t="SHA-256"){const n=Date.now().toString(16),r=Math.round(Math.random()*e);return{algorithm:t,challenge:await $e(n,r,t),salt:n,signature:""}}async function $e(e,t,n){return st(await crypto.subtle.digest(n.toUpperCase(),ot.encode(e+t)))}async function at(e,t,n="SHA-256",r=lt){const o=Date.now();for(let c=0;c<=r;c++)if(await $e(t,c,n)===e)return{number:c,took:Date.now()-o};return null}var p=(e=>(e.ERROR="error",e.VERIFIED="verified",e.VERIFYING="verifying",e.UNVERIFIED="unverified",e))(p||{});function ft(e){Ve(e,"svelte-fqcw55",".altcha.svelte-fqcw55.svelte-fqcw55{background:var(--altcha-color-base, transparent);border:1px solid var(--altcha-color-border, #a0a0a0);border-radius:3px;color:var(--altcha-color-text, currentColor);display:flex;flex-direction:column;max-width:260px;overflow:hidden;position:relative;text-align:left}.altcha.svelte-fqcw55.svelte-fqcw55:focus-within{border-color:var(--altcha-color-border-focus, currentColor)}.altcha-main.svelte-fqcw55.svelte-fqcw55{align-items:center;display:flex;gap:0.4rem;padding:0.7rem}.altcha-label.svelte-fqcw55.svelte-fqcw55{flex-grow:1}.altcha-label.svelte-fqcw55 label.svelte-fqcw55{cursor:pointer}.altcha-logo.svelte-fqcw55.svelte-fqcw55{color:currentColor;opacity:0.3}.altcha-logo.svelte-fqcw55.svelte-fqcw55:hover{opacity:1}.altcha-error.svelte-fqcw55.svelte-fqcw55{color:var(--altcha-color-error-text, #f23939);display:flex;font-size:0.85rem;gap:0.3rem;padding:0 0.7rem 0.7rem}.altcha-footer.svelte-fqcw55.svelte-fqcw55{align-items:center;background-color:var(--altcha-color-footer-bg, transparent);display:flex;font-size:0.75rem;opacity:0.4;padding:0.2rem 0.7rem;text-align:right}.altcha-footer.svelte-fqcw55.svelte-fqcw55:hover{opacity:1}.altcha-footer.svelte-fqcw55>.svelte-fqcw55:first-child{flex-grow:1}.altcha-footer.svelte-fqcw55 a{color:currentColor}.altcha-checkbox.svelte-fqcw55.svelte-fqcw55{display:flex;align-items:center;height:24px;width:24px}.altcha-checkbox.svelte-fqcw55 input.svelte-fqcw55{width:18px;height:18px;margin:0}.altcha-hidden.svelte-fqcw55.svelte-fqcw55{display:none}.altcha-spinner.svelte-fqcw55.svelte-fqcw55{animation:svelte-fqcw55-altcha-spinner 0.75s infinite linear;transform-origin:center}@keyframes svelte-fqcw55-altcha-spinner{100%{transform:rotate(360deg)}}")}function ce(e){let t,n,r;return{c(){t=G("svg"),n=G("path"),r=G("path"),c(n,"d","M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"),c(n,"fill","currentColor"),c(n,"opacity",".25"),c(r,"d","M12,4a8,8,0,0,1,7.89,6.7A1.53,1.53,0,0,0,21.38,12h0a1.5,1.5,0,0,0,1.48-1.75,11,11,0,0,0-21.72,0A1.5,1.5,0,0,0,2.62,12h0a1.53,1.53,0,0,0,1.49-1.3A8,8,0,0,1,12,4Z"),c(r,"fill","currentColor"),c(r,"class","altcha-spinner svelte-fqcw55"),c(t,"width","24"),c(t,"height","24"),c(t,"viewBox","0 0 24 24"),c(t,"xmlns","http://www.w3.org/2000/svg")},m(e,o){q(e,t,o),m(t,n),m(t,r)},d(e){e&&I(t)}}}function ut(e){let t,n,r=e[9].label+"";return{c(){t=v("label"),c(t,"for",n=e[3]+"_checkbox"),c(t,"class","svelte-fqcw55")},m(e,n){q(e,t,n),t.innerHTML=r},p(e,o){512&o[0]&&r!==(r=e[9].label+"")&&(t.innerHTML=r),8&o[0]&&n!==(n=e[3]+"_checkbox")&&c(t,"for",n)},d(e){e&&I(t)}}}function ht(e){let t,n=e[9].verifying+"";return{c(){t=v("span")},m(e,r){q(e,t,r),t.innerHTML=n},p(e,r){512&r[0]&&n!==(n=e[9].verifying+"")&&(t.innerHTML=n)},d(e){e&&I(t)}}}function dt(e){let t,n,r,o=e[9].verified+"";return{c(){t=v("span"),n=M(),r=v("input"),c(r,"type","hidden"),c(r,"name",e[3]),r.value=e[4]},m(e,c){q(e,t,c),t.innerHTML=o,q(e,n,c),q(e,r,c)},p(e,n){512&n[0]&&o!==(o=e[9].verified+"")&&(t.innerHTML=o),8&n[0]&&c(r,"name",e[3]),16&n[0]&&(r.value=e[4])},d(e){e&&(I(t),I(n),I(r))}}}function ae(e){let t,n,r,o,i,l;return{c(){t=v("div"),n=v("a"),r=G("svg"),o=G("path"),i=G("path"),l=G("path"),c(o,"d","M2.33955 16.4279C5.88954 20.6586 12.1971 21.2105 16.4279 17.6604C18.4699 15.947 19.6548 13.5911 19.9352 11.1365L17.9886 10.4279C17.8738 12.5624 16.909 14.6459 15.1423 16.1284C11.7577 18.9684 6.71167 18.5269 3.87164 15.1423C1.03163 11.7577 1.4731 6.71166 4.8577 3.87164C8.24231 1.03162 13.2883 1.4731 16.1284 4.8577C16.9767 5.86872 17.5322 7.02798 17.804 8.2324L19.9522 9.01429C19.7622 7.07737 19.0059 5.17558 17.6604 3.57212C14.1104 -0.658624 7.80283 -1.21043 3.57212 2.33956C-0.658625 5.88958 -1.21046 12.1971 2.33955 16.4279Z"),c(o,"fill","currentColor"),c(i,"d","M3.57212 2.33956C1.65755 3.94607 0.496389 6.11731 0.12782 8.40523L2.04639 9.13961C2.26047 7.15832 3.21057 5.25375 4.8577 3.87164C8.24231 1.03162 13.2883 1.4731 16.1284 4.8577L13.8302 6.78606L19.9633 9.13364C19.7929 7.15555 19.0335 5.20847 17.6604 3.57212C14.1104 -0.658624 7.80283 -1.21043 3.57212 2.33956Z"),c(i,"fill","currentColor"),c(l,"d","M7 10H5C5 12.7614 7.23858 15 10 15C12.7614 15 15 12.7614 15 10H13C13 11.6569 11.6569 13 10 13C8.3431 13 7 11.6569 7 10Z"),c(l,"fill","currentColor"),c(r,"width","22"),c(r,"height","22"),c(r,"viewBox","0 0 20 20"),c(r,"fill","none"),c(r,"xmlns","http://www.w3.org/2000/svg"),c(n,"href",ve),c(n,"target","_blank"),c(n,"class","altcha-logo svelte-fqcw55")},m(e,c){q(e,t,c),m(t,n),m(n,r),m(r,o),m(r,i),m(r,l)},p:Z,d(e){e&&I(t)}}}function fe(e){let t,n,r,o,i,l=e[9].error+"";return{c(){t=v("div"),n=G("svg"),r=G("path"),o=M(),i=v("div"),c(r,"stroke-linecap","round"),c(r,"stroke-linejoin","round"),c(r,"d","M6 18L18 6M6 6l12 12"),c(n,"width","14"),c(n,"height","14"),c(n,"xmlns","http://www.w3.org/2000/svg"),c(n,"fill","none"),c(n,"viewBox","0 0 24 24"),c(n,"stroke-width","1.5"),c(n,"stroke","currentColor"),c(i,"title",e[8]),c(t,"class","altcha-error svelte-fqcw55")},m(e,c){q(e,t,c),m(t,n),m(n,r),m(t,o),m(t,i),i.innerHTML=l},p(e,t){512&t[0]&&l!==(l=e[9].error+"")&&(i.innerHTML=l),256&t[0]&&c(i,"title",e[8])},d(e){e&&I(t)}}}function ue(e){let t,n,r=e[9].footer+"";return{c(){t=v("div"),n=v("div"),c(n,"class","svelte-fqcw55"),c(t,"class","altcha-footer svelte-fqcw55")},m(e,o){q(e,t,o),m(t,n),n.innerHTML=r},p(e,t){512&t[0]&&r!==(r=e[9].footer+"")&&(n.innerHTML=r)},d(e){e&&I(t)}}}function gt(e){let t,n,r,o,i,l,s,a,u,h,d,f,$,g,w=e[5]===p.VERIFYING&&ce();function b(e,t){return e[5]===p.VERIFIED?dt:e[5]===p.VERIFYING?ht:ut}let y=b(e),x=y(e),C=!0!==e[2]&&ae(),E=e[8]&&fe(e),k=e[9].footer&&!0!==e[1]&&ue(e);return{c(){t=v("div"),n=v("div"),w&&w.c(),r=M(),o=v("div"),i=v("input"),a=M(),u=v("div"),x.c(),h=M(),C&&C.c(),d=M(),E&&E.c(),f=M(),k&&k.c(),c(i,"type","checkbox"),c(i,"id",l=e[3]+"_checkbox"),i.required=s="onsubmit"!==e[0],c(i,"class","svelte-fqcw55"),c(o,"class","altcha-checkbox svelte-fqcw55"),le(o,"altcha-hidden",e[5]===p.VERIFYING),c(u,"class","altcha-label svelte-fqcw55"),c(n,"class","altcha-main svelte-fqcw55"),c(t,"class","altcha svelte-fqcw55"),c(t,"data-state",e[5])},m(c,l){q(c,t,l),m(t,n),w&&w.m(n,null),m(n,r),m(n,o),m(o,i),i.checked=e[6],m(n,a),m(n,u),x.m(u,null),m(n,h),C&&C.m(n,null),m(t,d),E&&E.m(t,null),m(t,f),k&&k.m(t,null),e[23](t),$||(g=[D(i,"change",e[22]),D(i,"change",e[10]),D(i,"invalid",e[11])],$=!0)},p(e,a){e[5]===p.VERIFYING?w||(w=ce(),w.c(),w.m(n,r)):w&&(w.d(1),w=null),8&a[0]&&l!==(l=e[3]+"_checkbox")&&c(i,"id",l),1&a[0]&&s!==(s="onsubmit"!==e[0])&&(i.required=s),64&a[0]&&(i.checked=e[6]),32&a[0]&&le(o,"altcha-hidden",e[5]===p.VERIFYING),y===(y=b(e))&&x?x.p(e,a):(x.d(1),x=y(e),x&&(x.c(),x.m(u,null))),!0!==e[2]?C?C.p(e,a):(C=ae(),C.c(),C.m(n,null)):C&&(C.d(1),C=null),e[8]?E?E.p(e,a):(E=fe(e),E.c(),E.m(t,f)):E&&(E.d(1),E=null),e[9].footer&&!0!==e[1]?k?k.p(e,a):(k=ue(e),k.c(),k.m(t,null)):k&&(k.d(1),k=null),32&a[0]&&c(t,"data-state",e[5])},i:Z,o:Z,d(n){n&&I(t),w&&w.d(),x.d(),C&&C.d(),E&&E.d(),k&&k.d(),e[23](null),$=!1,H(g)}}}const ve="https://altcha.org/";function he(e){return JSON.parse(e)}function mt(e,t,n){let r,o,c,{auto:i}=t,{challengeurl:l}=t,{challengejson:s}=t,{debug:a=!1}=t,{hidefooter:u=!1}=t,{hidelogo:h=!1}=t,{name:d="altcha"}=t,{maxnumber:f}=t,{mockerror:$=!1}=t,{strings:m}=t,{test:g=!1}=t;const v=We(),w=["SHA-256","SHA-384","SHA-512"];let b,y=!1,x=null,C=null,E=null,k=p.UNVERIFIED;function I(...e){(a||e.some((e=>e instanceof Error)))&&console[e[0]instanceof Error?"error":"log"]("ALTCHA",...e)}function q(e){x&&"onsubmit"===i&&k===p.UNVERIFIED&&(e.preventDefault(),e.stopPropagation(),N().then((()=>{null==x||x.requestSubmit()})))}function _(){L()}async function G(e){let t=null;if("Worker"in window){try{t=await async function(e,t,n){const r=new it;return new Promise((o=>{r.addEventListener("message",(e=>{o(e.data)})),r.postMessage({alg:n,challenge:e,max:f,salt:t})}))}(e.challenge,e.salt,e.algorithm)}catch(e){I(e)}if(void 0!==(null==t?void 0:t.number))return{data:e,solution:t}}return{data:e,solution:await at(e.challenge,e.salt,e.algorithm,f)}}function L(e=p.UNVERIFIED){n(6,y=!1),n(8,C=null),n(4,E=null),n(5,k=e)}async function N(){return L(p.VERIFYING),async function(){if($)throw I("mocking error"),new Error("Mocked error.");if(r)return I("using provided json data"),r;if(g)return I("generating test challenge"),ct();{if(!l)throw new Error("Attribute challengeurl not set.");I("fetching challenge from",l);const e=await fetch(l);if(200!==e.status)throw new Error(`Server responded with ${e.status}.`);return e.json()}}().then((e=>(function(e){if(!e.algorithm)throw new Error("Invalid challenge. Property algorithm is missing.");if(void 0===e.signature)throw new Error("Invalid challenge. Property signature is missing.");if(!w.includes(e.algorithm.toUpperCase()))throw new Error(`Unknown algorithm value. Allowed values: ${w.join(", ")}`);if(!e.challenge||e.challenge.length<40)throw new Error("Challenge is too short. Min. 40 chars.");if(!e.salt||e.salt.length<10)throw new Error("Salt is too short. Min. 10 chars.")}(e),I("challenge",e),G(e)))).then((({data:e,solution:t})=>{if(I("solution",t),void 0===(null==t?void 0:t.number))throw new Error("Unexpected result returned.");I("verified"),n(5,k=p.VERIFIED),n(6,y=!0),n(4,E=function(e,t){return btoa(JSON.stringify({algorithm:e.algorithm,challenge:e.challenge,number:t.number,salt:e.salt,signature:e.signature,test:!!g||void 0,took:t.took}))}(e,t)),I("payload",E),Pe().then((()=>{v("verified",{payload:E})}))})).catch((e=>{I(e),n(5,k=p.ERROR),n(6,y=!1),n(8,C=e)}))}return He((()=>{x&&(x.removeEventListener("submit",q),x.removeEventListener("reset",_),x=null)})),Be((()=>{I("mounted","0.1.6"),g&&I("using test mode"),void 0!==i&&I("auto",i),x=b.closest("form"),x&&(x.addEventListener("submit",q),x.addEventListener("reset",_)),"onload"===i&&N()})),e.$$set=e=>{"auto"in e&&n(0,i=e.auto),"challengeurl"in e&&n(12,l=e.challengeurl),"challengejson"in e&&n(13,s=e.challengejson),"debug"in e&&n(14,a=e.debug),"hidefooter"in e&&n(1,u=e.hidefooter),"hidelogo"in e&&n(2,h=e.hidelogo),"name"in e&&n(3,d=e.name),"maxnumber"in e&&n(15,f=e.maxnumber),"mockerror"in e&&n(16,$=e.mockerror),"strings"in e&&n(17,m=e.strings),"test"in e&&n(18,g=e.test)},e.$$.update=()=>{8192&e.$$.dirty[0]&&(r=s?he(s):void 0),131072&e.$$.dirty[0]&&n(21,o=m?he(m):{}),2097152&e.$$.dirty[0]&&n(9,c={error:"Verification failed. Try again later.",footer:`Protected by ALTCHA`,label:"I'm not a robot",verified:"Verified",verifying:"Verifying...",waitAlert:"Verifying... please wait.",...o}),48&e.$$.dirty[0]&&v("statechange",{payload:E,state:k})},[i,u,h,d,E,k,y,b,C,c,function(){[p.UNVERIFIED,p.ERROR].includes(k)?N():n(6,y=!0)},function(){k===p.VERIFYING&&alert(c.waitAlert)},l,s,a,f,$,m,g,L,N,o,function(){y=this.checked,n(6,y)},function(e){z[e?"unshift":"push"]((()=>{b=e,n(7,b)}))}]}class wt extends nt{constructor(e){super(),et(this,e,mt,gt,Ae,{auto:0,challengeurl:12,challengejson:13,debug:14,hidefooter:1,hidelogo:2,name:3,maxnumber:15,mockerror:16,strings:17,test:18,reset:19,verify:20},ft,[-1,-1])}get auto(){return this.$$.ctx[0]}set auto(e){this.$$set({auto:e}),C()}get challengeurl(){return this.$$.ctx[12]}set challengeurl(e){this.$$set({challengeurl:e}),C()}get challengejson(){return this.$$.ctx[13]}set challengejson(e){this.$$set({challengejson:e}),C()}get debug(){return this.$$.ctx[14]}set debug(e){this.$$set({debug:e}),C()}get hidefooter(){return this.$$.ctx[1]}set hidefooter(e){this.$$set({hidefooter:e}),C()}get hidelogo(){return this.$$.ctx[2]}set hidelogo(e){this.$$set({hidelogo:e}),C()}get name(){return this.$$.ctx[3]}set name(e){this.$$set({name:e}),C()}get maxnumber(){return this.$$.ctx[15]}set maxnumber(e){this.$$set({maxnumber:e}),C()}get mockerror(){return this.$$.ctx[16]}set mockerror(e){this.$$set({mockerror:e}),C()}get strings(){return this.$$.ctx[17]}set strings(e){this.$$set({strings:e}),C()}get test(){return this.$$.ctx[18]}set test(e){this.$$set({test:e}),C()}get reset(){return this.$$.ctx[19]}get verify(){return this.$$.ctx[20]}}customElements.define("altcha-widget",tt(wt,{auto:{},challengeurl:{},challengejson:{},debug:{type:"Boolean"},hidefooter:{type:"Boolean"},hidelogo:{type:"Boolean"},name:{},maxnumber:{},mockerror:{type:"Boolean"},strings:{},test:{type:"Boolean"}},[],["reset","verify"],!1));export{wt as Altcha}; -//# sourceMappingURL=/sm/3946beb23f2e7b329f9d4f976183586f81ab9506ca82e19e9433e759dcf4e908.map \ No newline at end of file diff --git a/app/static/linkedin.png b/app/static/linkedin.png deleted file mode 100644 index f52d8a4..0000000 Binary files a/app/static/linkedin.png and /dev/null differ diff --git a/app/static/mail.png b/app/static/mail.png deleted file mode 100644 index c300991..0000000 Binary files a/app/static/mail.png and /dev/null differ diff --git a/app/static/xmpp.png b/app/static/xmpp.png deleted file mode 100644 index 13c9e17..0000000 Binary files a/app/static/xmpp.png and /dev/null differ diff --git a/app/templates/about.html b/app/templates/about.html deleted file mode 100644 index 7e34c46..0000000 --- a/app/templates/about.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - About Me | pkrm.dev - - - - -
-

About Me

-

I am a freshman in Computer Science who enjoys programming and learning more about different technologies. I manage a Debian based home server where I self-host tools like: DNSCrypt, AdGuard, WireGuard, Syncthing, XMPP, and many more media/system management utilities.

Find my work by viewing my open-sourced repositories on GitHub.

-
- -
PGP Fingerprint: FD5C 7FFF 860B 6E97 7049 E27E 505E D36F C12B 5D5E
- - \ No newline at end of file diff --git a/app/templates/contact.html b/app/templates/contact.html deleted file mode 100644 index 9da956d..0000000 --- a/app/templates/contact.html +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - - - - - - - - - - - - - - - Contact | pkrm.dev - - - - -
-

Contact Me

- -
- - - - - - -
- - {% if success %} -

Thank you for contacting me. I'll get back soon.

- {% endif %} - {% if error %} -

An error occured, please try again later.

- {% endif %} -
- -
PGP Fingerprint: FD5C 7FFF 860B 6E97 7049 E27E 505E D36F C12B 5D5E
- - - - \ No newline at end of file diff --git a/app/templates/index.html b/app/templates/index.html deleted file mode 100644 index f97509f..0000000 --- a/app/templates/index.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - Home | pkrm.dev - - - - -
-
-

Hello, I'm Parker

-

A student with a strong passion for technology and privacy.

-
- -
- Mail Logo - GitHub Logo - LinkedIn Logo - XMPP Logo - GnuPG Logo -
-
- -
PGP Fingerprint: FD5C 7FFF 860B 6E97 7049 E27E 505E D36F C12B 5D5E
- - \ No newline at end of file diff --git a/app/templates/pgp.html b/app/templates/pgp.html deleted file mode 100644 index f63c19c..0000000 --- a/app/templates/pgp.html +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - PGP Key | pkrm.dev - - - - -
-

Copy my key below, or click here to download it.

-

- -----BEGIN PGP PUBLIC KEY BLOCK----- -

- mDMEZyMGlhYJKwYBBAHaRw8BAQdAoO/hA2S3fx16L55Bx+2bRN+zZe+8+wyME8vQ - 7KANwn+0G1BhcmtlciBNIDxjb250YWN0QHBrcm0uZGV2PoiTBBMWCgA7AhsDBQsJ - CAcCAiICBhUKCQgLAgQWAgMBAh4HAheAFiEE/Vx//4YLbpdwSeJ+UF7Tb8ErXV4F - AmcjB84ACgkQUF7Tb8ErXV5MGQD/Uxy3OwbaGmSDYcJks3fT2xrcwrTLHpYHDU/e - MgIrFnUBALJ9X18ugx6s3CPtNgnyvm6cLU/ttzeRoRWdG8PuODYFiJMEExYKADsW - IQT9XH//hgtul3BJ4n5QXtNvwStdXgUCZyMGlgIbAwULCQgHAgIiAgYVCgkICwIE - FgIDAQIeBwIXgAAKCRBQXtNvwStdXjeOAP9qQ4qg2k0WamMec/SdOchux/p8wzTo - F0Z9yXwIXsjmeAD/RxeHB8x9ZCfe1t1eNk+oVcPBmw/fLPr8OALpjJkG9Aq4OARn - IwaWEgorBgEEAZdVAQUBAQdAyDo44/FnbZMNqQUxYHgaQY1qImz9YsLSD/fmHzrA - ymIDAQgHiHgEGBYKACAWIQT9XH//hgtul3BJ4n5QXtNvwStdXgUCZyMGlgIbDAAK - CRBQXtNvwStdXiqSAP4/QwITUsUE6OLRKUQZ5JN4WQhjWto18vIZI/NlNVvibAEA - knJ7V0TYPjX8LhOCno47dDkwSWAHT/S9oMbRg9rCigk= - =oX5k -
- -----END PGP PUBLIC KEY BLOCK----- -

-
- -
PGP Fingerprint: FD5C 7FFF 860B 6E97 7049 E27E 505E D36F C12B 5D5E
- - - - \ No newline at end of file diff --git a/app/views.py b/app/views.py deleted file mode 100644 index 415fac7..0000000 --- a/app/views.py +++ /dev/null @@ -1,115 +0,0 @@ -import flask -import os -import discord -import dotenv -import hashlib -import secrets -import hmac -import json -import base64 -import random - -app = flask.Flask(__name__) -dotenv.load_dotenv() -webhook_url = os.getenv("WEBHOOK_URL") -hmac_key = random.randbytes(500) - - -@app.route("/", methods=["GET"]) -def index(): - return flask.render_template("index.html") - - -@app.route("/about", methods=["GET"]) -def about(): - return flask.render_template("about.html") - - -@app.route("/contact", methods=["GET", "POST"]) -def contact(): - if flask.request.method == "GET": - return flask.render_template("contact.html") - - if flask.request.method == "POST": - try: - # Decode payload - data = json.loads( - base64.b64decode(flask.request.form["altcha"]).decode() - ) - - # Validate algorithm - if data["algorithm"] != "SHA-256": - return flask.render_template("contact.html", error=True) - # Validate challenge - expected_challenge = hashlib.sha256( - (data["salt"] + str(data["number"])).encode() - ).hexdigest() - if data["challenge"] != expected_challenge: - return flask.render_template("contact.html", error=True) - # Validate signature - signature = hmac.new( - hmac_key, data["challenge"].encode(), hashlib.sha256 - ).hexdigest() - if data["signature"] != signature: - return flask.render_template("contact.html", error=True) - - # All checks passed, send off form data - - name = flask.request.form["name"] - email = flask.request.form["email"] - message = flask.request.form["message"] - # Send the contact form to Discord via a webhook - webhook = discord.SyncWebhook.from_url(webhook_url) - - embed = discord.Embed( - title="New Message", - description=( - f"**Name:** ` {name} `\n**Email:** `" - f" {email} `\n**Message:** ` {message} `" - ), - color=0x85C0F7, - ) - webhook.send(embed=embed) - - return flask.render_template("contact.html", success=True) - # If any error happens for any reason, return the contact page with error - except: - return flask.render_template("contact.html", error=True) - - -@app.route("/altcha-challenge", methods=["GET"]) -def altcha_challenge(): - salt = secrets.token_urlsafe(25) - secret_number = random.randint(10000, 50000) - - challenge_data = f"{salt}{secret_number}".encode() - challenge = hashlib.sha256(challenge_data).hexdigest() - - signature = hmac.new( - hmac_key, challenge.encode(), hashlib.sha256 - ).hexdigest() - - response = { - "algorithm": "SHA-256", - "challenge": challenge, - "salt": salt, - "signature": signature, - } - - return flask.jsonify(response) - - -@app.route("/pgp", methods=["GET"]) -def pgp(): - return flask.render_template("pgp.html") - - -@app.route("/robots.txt", methods=["GET"]) -def robots(): - return flask.send_file("static/robots.txt") - - -@app.route("/parker.asc", methods=["GET"]) -def parker(): - # Send the file to download - return flask.send_file("static/parker.asc", as_attachment=True) diff --git a/css/index.css b/css/index.css new file mode 100644 index 0000000..4b58b69 --- /dev/null +++ b/css/index.css @@ -0,0 +1,215 @@ +html { + font-family: "Open Sans", Arial; + color: #454545; + scroll-behavior: smooth; +} + +html.dark * { + background-color: #242528; + color: #9F9FAA; +} + +html.dark svg, +html.dark #about a { + color: #9F9FAA; + fill: #9F9FAA; +} + +html.increased-contrast * { + background-color: #FFF; + color: #050505; +} + +html.increased-contrast svg, +html.increased-contrast #about a { + fill: #050505; + color: #050505; +} + +body { + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + min-height: 200vh; +} + +nav { + font-weight: 600; + position: absolute; + top: 0; + left: 50%; + transform: translateX(-50%); + font-size: 1rem; + text-align: center; + margin-top: 2rem; + width: 90vw; +} + +nav a { + margin: 0 1rem; + text-decoration: none; + color: #454545; +} + +nav a:hover { + text-decoration: underline; + cursor: pointer; + opacity: 0.6; + transition: opacity 0.25s ease-in-out; +} + +#toggle { + /* Move to the top right of the screen */ + position: absolute; + top: 0; + right: 0; + margin: 1rem; + font-size: 1.5rem; + color: #454545; +} + +#moon, #sun { + position: absolute; + transition: opacity 0.1s ease; +} + +#moon { + opacity: 0; +} + +#sun { + opacity: 1; +} + +/* Toggle media query for mobile */ +@media only screen and (max-width: 600px) { + #toggle { + display: none; + } +} + + +/* Container styles */ +header { + position: absolute; + top: 45%; + left: 50%; + transform: translate(-50%, -50%); + text-align: center; +} + +header h1 { + font-size: 45px; + width: 500px; +} + +header h2 { + font-size: 25px; + width: 500px; +} + +/* Header media query for mobile */ +@media only screen and (max-width: 600px) { + header h1 { + font-size: 1.5rem; + width: 300px; + } + + header h2 { + margin: 1rem 0 2rem 0; + width: 300px; + } +} + +header a { + text-decoration: none; +} + +svg { + margin: 0 1rem; + height: 50px; + width: 50px; +} + +/* Icons media query for mobile */ +@media only screen and (max-width: 600px) { + svg { + height: 40px; + width: 40px; + margin: 0 0.5rem; + } +} + +svg:hover { + cursor: pointer; + opacity: 0.6; + transition: opacity 0.25s ease-in-out; +} + +#down-arrow { + position: absolute; + bottom: 2%; + left: 50%; + transform: translateX(-50%); + color: #454545; + font-size: 2rem; +} + +#about { + margin-top: 100vh; + width: 1000px; +} + +#about h1 { + font-size: 25px; +} + +#about h2 { + font-size: 20px; +} + +#about p{ + font-size: 18px; +} + +#about ul li { + font-size: 18px; + margin-top: 7px; +} + +/* About media query for mobile */ +@media only screen and (max-width: 600px) { + #about { + width: 90%; + } + + #about h1 { + font-size: 1.5rem; + } + + #about h2 { + font-size: 1.25rem; + } + + #about p, #about ul li { + font-size: 1rem; + } + + #about ul li { + margin-top: 10px; + } +} + +#about a { + text-decoration: underline; + color: #454545; +} + +#about a:hover { + cursor: pointer; + opacity: 0.6; + transition: opacity 0.25s ease-in-out; +} \ No newline at end of file diff --git a/app/static/css/pgp.css b/css/pgp.css similarity index 100% rename from app/static/css/pgp.css rename to css/pgp.css diff --git a/index.html b/index.html new file mode 100644 index 0000000..1e3a93a --- /dev/null +++ b/index.html @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + Parker M. + + + +
+

Hello, I'm Parker

+

A student with a strong passion for technology and privacy.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+

About Me

+

I am a freshman in Computer Science at the University of Texas at Austin - also in FRI Quantum Computing. I manage a Debian based home server where I self-host everything I can. In addition to this, I actively work on personal projects, most of which are open-sourced.

+

View my projects on GitHub, or, if you prefer something lighter and not behind Cloudflare, check them out on my Cgit instance.

+
+

Things I run on my server

+
    +
  • WireGuard: Easy access to my home network when I'm away
  • +
  • Syncthing: Sync important files/databases across all of my machines
  • +
  • DNSCrypt: My own encrypted DNS resolver
  • +
  • AdGuard: DNS sinkhole to block ads + DNS hostnames
  • +
  • XMPP: I host my own personal XMPP server for people to contact me ( always use encryption )
  • +
  • Immich: Cloud photo storage with tons of features - alternative to Google Photos
  • +
  • Lavalink: Audio node for my Discord music bot
  • +
  • Guava: My open source and easily self-hostable Discord music bot
  • +
  • LinkLogger: My open source link shortener and IP logger
  • +
  • ... and a lot of media management services/tools
  • +
+
+

Personal Projects

+ +

Top 3

+
    +
  • Guava: Dead simple Discord music bot. Allows playing music from Spotify, Apple Music, SoundCloud, YouTube, Deezer, Bandcamp, and Twitch. Currently in >225 unique servers!
  • +
  • LinkLogger: Link shortener and IP logger. Features a full Web UI in React with a public API built on FastAPI.
  • +
  • EduStore (Inactive + Closed Source): Inventory management for school districts. Developed during my internship at and for Mansfield ISD. Won 1st place in the Congressional App Challenge for Texas' 6th District.
  • +
+ +

Others

+
    +
  • CordArr: Request new content for Radarr/Sonarr libraries and create temporary Jellyfin accounts through Discord commands.
  • +
  • PeakPass (Inactive): Web-based password manager that checks user hashes against previosuly breached passwords. Features strong encryption algorithms, password hashing + salting, and a custom implementation of breach detection ( rather than using an API like HIBP )
  • +
+
+ + \ No newline at end of file diff --git a/js/toggle.js b/js/toggle.js new file mode 100644 index 0000000..2399f7a --- /dev/null +++ b/js/toggle.js @@ -0,0 +1,30 @@ +const moon = document.getElementById('moon'); +const sun = document.getElementById('sun'); + +moon.addEventListener('click', () => { + document.documentElement.classList.remove('dark'); + + // Fade out moon and fade in sun + moon.style.opacity = '0'; + sun.style.opacity = '1'; + + // After the transition ends, hide the moon and show the sun (for accessibility) + setTimeout(() => { + moon.style.display = 'none'; + sun.style.display = 'block'; + }, 100); // Match the duration of the transition (100ms) +}); + +sun.addEventListener('click', () => { + document.documentElement.classList.add('dark'); + + // Fade out sun and fade in moon + sun.style.opacity = '0'; + moon.style.opacity = '1'; + + // After the transition ends, hide the sun and show the moon (for accessibility) + setTimeout(() => { + sun.style.display = 'none'; + moon.style.display = 'block'; + }, 100); // Match the duration of the transition (100ms) +}); diff --git a/app/static/parker.asc b/parker.asc similarity index 100% rename from app/static/parker.asc rename to parker.asc diff --git a/pkrm.service b/pkrm.service deleted file mode 100644 index 55ddc64..0000000 --- a/pkrm.service +++ /dev/null @@ -1,14 +0,0 @@ -# Systemd service file, feel free to edit to your needs, this isnt needed though - -[Unit] -Description=Keep PKRM.DEV Up and Running -After=network.target - -[Service] -Type=simple -WorkingDirectory=/home/parker/services/pkrm -ExecStart=/usr/bin/bash /home/parker/services/pkrm/pkrm.sh -Restart=always - -[Install] -WantedBy=multi-user.target \ No newline at end of file diff --git a/pkrm.sh b/pkrm.sh deleted file mode 100755 index f01bbf2..0000000 --- a/pkrm.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -gunicorn -b 0.0.0.0:4343 run:app \ No newline at end of file diff --git a/app/static/robots.txt b/robots.txt similarity index 100% rename from app/static/robots.txt rename to robots.txt diff --git a/run.py b/run.py deleted file mode 100644 index ea15d11..0000000 --- a/run.py +++ /dev/null @@ -1,4 +0,0 @@ -from app.views import app - -if __name__ == "__main__": - app.run(port="4343")