Initialize project with clean ignore rules
4
.gitmodules
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[submodule "themes/blowfish"]
|
||||||
|
path = themes/blowfish
|
||||||
|
url = https://github.com/nunocoracao/blowfish.git
|
||||||
|
branch = main
|
||||||
6
README.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Blowfish Template
|
||||||
|
This is a template for the Blowfish Hugo Theme. Feel free to use this repo as a quick way to get started with Blowfish. Please visit [Blowfish's main website](https://github.com/nunocoracao/blowfish) to read the complete documentation.
|
||||||
|
|
||||||
|
The template was built using the [Git option](https://nunocoracao.github.io/blowfish/docs/installation/#install-using-git) from Blowfish's installations instructions.
|
||||||
|
|
||||||
|

|
||||||
10
archetypes/default.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
title: "{{ replace .Name "-" " " | title }}"
|
||||||
|
description: "{{ replace .Name "-" " " | title }}"
|
||||||
|
summary: ""
|
||||||
|
date: {{ .Date }}
|
||||||
|
lastmod: {{ .Date }}
|
||||||
|
author: wompmacho
|
||||||
|
draft: true
|
||||||
|
---
|
||||||
|
|
||||||
89
assets/css/custom.css
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
/* --- 1. Global Prose Rules --- */
|
||||||
|
.article-content.prose {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 65ch;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- 2. Absolute Isolation Layout --- */
|
||||||
|
.single-post-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column; /* On mobile, they stack normally */
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1280px) {
|
||||||
|
.single-post-wrapper {
|
||||||
|
display: block; /* Turns off Flexbox completely */
|
||||||
|
}
|
||||||
|
|
||||||
|
#main-content {
|
||||||
|
/* Standard block behavior: automatically centers prose within wrapper */
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#sidebar-toc {
|
||||||
|
/* Removes the TOC from document flow entirely */
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
|
||||||
|
/* Math: Center of wrapper (50%) + half the prose width (32.5ch) + 2rem gap */
|
||||||
|
left: calc(50% + 32.5ch + 2rem);
|
||||||
|
|
||||||
|
/* The aside needs to be exactly 100% height of the wrapper for sticky to work */
|
||||||
|
height: 100%;
|
||||||
|
width: 256px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- 3. Sticky TOC Logic --- */
|
||||||
|
#sidebar-toc .toc {
|
||||||
|
position: sticky;
|
||||||
|
top: 6rem;
|
||||||
|
max-height: calc(100vh - 8rem);
|
||||||
|
overflow-y: auto;
|
||||||
|
padding-right: 1rem;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- 4. Projects Page (Isolated) --- */
|
||||||
|
.projects-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Base Styles --- */
|
||||||
|
#resume-table p { padding: .5em }
|
||||||
|
.iframe-wrapper { position: relative; padding-bottom: 56.25%; padding-top: 25px; height: 0; }
|
||||||
|
.iframe-wrapper iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
|
||||||
|
#resume-table { border-collapse: collapse }
|
||||||
|
#resume-table .statements td { font-style: italic; padding: 1em; text-align: center }
|
||||||
|
#resume-table .header { padding-top: 1em; padding-left: 1em; font-weight: 700; text-transform: uppercase; text-align: left }
|
||||||
|
.google-blue { color: #4285f4; font-weight: bolder; }
|
||||||
|
.google-red { color: #ea4335; font-weight: bolder; }
|
||||||
|
.google-yellow { color: #fbbc04; font-weight: bolder; }
|
||||||
|
.google-green { color: #34a853; font-weight: bolder; }
|
||||||
|
.header-google-blue { border-bottom: 4px solid #4285f4 }
|
||||||
|
.header-google-red { border-bottom: 4px solid #ea4335 }
|
||||||
|
.header-google-yellow { border-bottom: 4px solid #fbbc04 }
|
||||||
|
.header-google-green { border-bottom: 4px solid #34a853 }
|
||||||
|
#resume-table td, #resume-table tr { border-width: 0 }
|
||||||
|
#resume-table ul { margin-top: 0; margin-bottom: 0 }
|
||||||
|
#resume-table .section-header td { padding-top: .66em; font-weight: 700 }
|
||||||
|
#resume-table .role { padding: .66em; text-align: left }
|
||||||
|
#resume-table .dates { text-align: right; padding-right: 1em }
|
||||||
|
#resume-table .quote { text-align: center }
|
||||||
|
#resume-table .twitter-blue { color: #1da1f2; font-weight: bolder }
|
||||||
|
.center { text-align: center; padding: .5em }
|
||||||
|
img[src$='#center']{ display: block; margin: 0.7rem auto; }
|
||||||
|
img[src$='#floatleft']{ float:left; margin: 0.7rem; }
|
||||||
|
img[src$='#floatright']{ float:right; margin: 0.7rem; }
|
||||||
|
.homepage .content { text-align: left; }
|
||||||
|
|
||||||
|
|
||||||
|
/* Hide any TOC link that points to an element with the 'no-toc' class */
|
||||||
|
.toc-right a.no-toc-link,
|
||||||
|
.toc-inside a.no-toc-link {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
BIN
assets/devops-lifecycle.png
Normal file
|
After Width: | Height: | Size: 163 KiB |
BIN
assets/devops_diagram.png
Normal file
|
After Width: | Height: | Size: 163 KiB |
57
assets/icons/SE_home_page_logo.svg
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<svg width="537" height="156" viewBox="0 0 537 156" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M132.816 86.9339C136.531 90.3333 141.168 92.1849 145.035 92.1849C149.263 92.1849 151.79 90.5897 151.79 87.8075C151.79 79.7742 132.303 84.2561 132.303 71.4276C132.303 65.246 137.614 61.3813 145.453 61.3813C150.147 61.3813 154.736 62.8721 158.08 65.0941L155.553 70.4496C151.638 68.0757 147.714 67.0977 144.987 67.0977C141.434 67.0977 139.115 68.4365 139.115 70.8579C139.115 78.7392 158.603 74.5137 158.546 87.4941C158.546 93.9321 152.874 97.9013 144.94 97.9013C139.267 97.9013 133.908 95.5843 130.193 92.1849L132.825 86.9339H132.816Z" fill="white"/>
|
||||||
|
<path d="M162.679 67.5535L168.551 63.8027V71.3232H176.647V75.7576H168.551V88.8995C168.551 91.7292 169.635 92.6122 171.487 92.6122C172.723 92.6122 174.015 92.2039 175.611 91.4253L177.103 95.9547C174.936 97.2461 172.666 97.9677 170.347 97.9677C166.224 97.9677 162.67 95.5939 162.67 89.982V67.5535H162.679Z" fill="white"/>
|
||||||
|
<path d="M180.562 70.2978H186.491V75.7577C188.343 72.045 191.65 70.0889 195.973 70.0889V75.7577C190.462 75.4444 186.956 78.6918 186.491 83.3826V97.7019H180.562V70.2978Z" fill="white"/>
|
||||||
|
<path d="M210.453 70.0889C220.097 70.0889 224.012 76.3749 223.29 86.2693H202.567C203.498 90.4948 206.738 93.1251 211.071 93.1251C214.064 93.1251 216.791 91.9951 218.805 89.9346L221.95 93.2865C219.271 96.2206 215.356 97.9203 210.605 97.9203C202.149 97.9203 196.581 92.3085 196.581 84.0663C196.581 75.8242 202.301 70.1553 210.444 70.1079L210.453 70.0889ZM218.131 81.9393C217.922 77.5049 214.986 74.8272 210.501 74.8272C206.016 74.8272 203.128 77.5619 202.463 81.9393H218.131Z" fill="white"/>
|
||||||
|
<path d="M235.832 81.2179H244.136V80.3918C244.136 76.9354 241.923 74.8748 237.59 74.8748C234.958 74.8748 232.231 75.8054 229.494 77.657L227.072 73.536C230.882 71.2665 233.666 70.0796 238.873 70.0796C245.932 70.0796 249.904 73.6879 249.961 79.7176L250.008 97.6927H244.184V94.3407C242.227 96.7621 239.082 97.949 235.11 97.949C229.181 97.949 225.466 94.2933 225.466 89.4505C225.466 84.6078 229.228 81.2559 235.823 81.2084L235.832 81.2179ZM236.355 93.2677C240.583 93.2677 243.832 91.0553 244.136 88.0642V85.5383H236.716C232.849 85.5383 231.043 86.5639 231.043 89.1941C231.043 91.663 233.058 93.2677 236.355 93.2677Z" fill="white"/>
|
||||||
|
<path d="M255.87 70.2978H261.742V75.6058C263.652 71.7411 267.158 70.0889 271.69 70.0889C276.584 70.0889 279.938 72.5102 281.23 76.6788C282.978 72.0924 286.646 70.0889 291.643 70.0889C297.934 70.0889 301.696 74.1055 301.696 80.6479V97.7019H295.767V82.3001C295.767 78.1316 293.297 75.6058 289.268 75.6058C284.574 75.7577 281.743 79.366 281.743 84.1613V97.7114H275.814V82.3096C275.814 78.1411 273.391 75.6153 269.372 75.6153C264.631 75.7672 261.742 79.3755 261.742 84.1708V97.7209H255.87V70.3168V70.2978Z" fill="white"/>
|
||||||
|
<path d="M491.861 67.5535L497.733 63.8027V71.3232H505.828V75.7576H497.733V88.8995C497.733 91.7292 498.816 92.6122 500.669 92.6122C501.904 92.6122 503.196 92.2039 504.793 91.4253L506.284 95.9547C504.118 97.2461 501.847 97.9677 499.529 97.9677C495.405 97.9677 491.852 95.5939 491.852 89.982V67.5535H491.861Z" fill="white"/>
|
||||||
|
<path d="M308.157 61.6374H333.935V67.0974H314.238V76.8303H331.864V82.2903H314.238V92.2321H334.553V97.692H308.157V61.6279V61.6374Z" fill="white"/>
|
||||||
|
<path d="M339.237 61.6382H345.166V97.7023H339.237V61.6382Z" fill="white"/>
|
||||||
|
<path d="M363.418 70.0889C373.062 70.0889 376.977 76.3749 376.255 86.2693H355.532C356.463 90.4948 359.703 93.1251 364.036 93.1251C367.029 93.1251 369.756 91.9951 371.77 89.9346L374.915 93.2865C372.236 96.2206 368.321 97.9203 363.57 97.9203C355.114 97.9203 349.546 92.3085 349.546 84.0663C349.546 75.8242 355.266 70.1553 363.409 70.1079L363.418 70.0889ZM371.095 81.9393C370.886 77.5049 367.95 74.8272 363.466 74.8272C358.981 74.8272 356.092 77.5619 355.427 81.9393H371.095Z" fill="white"/>
|
||||||
|
<path d="M380.578 70.2978H386.45V75.6058C388.36 71.7411 391.866 70.0889 396.398 70.0889C401.292 70.0889 404.646 72.5102 405.938 76.6788C407.686 72.0924 411.354 70.0889 416.351 70.0889C422.642 70.0889 426.404 74.1055 426.404 80.6479V97.7019H420.475V82.3001C420.475 78.1316 418.005 75.6058 413.976 75.6058C409.282 75.7577 406.451 79.366 406.451 84.1613V97.7114H400.522V82.3096C400.522 78.1411 398.099 75.6153 394.08 75.6153C389.339 75.7672 386.45 79.3755 386.45 84.1708V97.7209H380.578V70.3168V70.2978Z" fill="white"/>
|
||||||
|
<path d="M444.097 70.0889C453.741 70.0889 457.656 76.3749 456.933 86.2693H436.211C437.142 90.4948 440.382 93.1251 444.714 93.1251C447.707 93.1251 450.434 91.9951 452.449 89.9346L455.594 93.2865C452.914 96.2206 449 97.9203 444.249 97.9203C435.793 97.9203 430.225 92.3085 430.225 84.0663C430.225 75.8242 435.945 70.1553 444.087 70.1079L444.097 70.0889ZM451.774 81.9393C451.565 77.5049 448.629 74.8272 444.144 74.8272C439.66 74.8272 436.771 77.5619 436.106 81.9393H451.774Z" fill="white"/>
|
||||||
|
<path d="M461.256 70.2978H467.185V75.5488C469.142 71.7411 472.648 70.0889 477.285 70.0889C483.68 70.0889 487.49 74.1055 487.49 80.6479V97.7019H481.561V82.3002C481.561 78.1791 479.09 75.6533 474.967 75.6533C470.434 75.7007 467.593 78.8438 467.185 83.1263V97.7019H461.256V70.2978Z" fill="white"/>
|
||||||
|
<path d="M509.742 89.8677C512.117 91.9757 515.927 93.2671 518.92 93.2671C521.448 93.2671 523.405 92.3936 523.405 90.333C523.405 84.7211 508.972 87.8642 509.02 77.9698C509.02 72.4054 513.866 70.041 519.12 70.041C522.569 70.041 526.237 71.0665 528.707 72.8232L526.541 77.2007C524.118 75.7099 521.077 74.7793 518.654 74.7793C516.231 74.7793 514.635 75.5484 514.635 77.5045C514.635 82.5562 529.22 79.8214 529.173 89.7633C529.173 95.3752 524.175 97.901 518.654 97.901C514.635 97.901 510.359 96.5621 507.623 94.0933L509.732 89.8677H509.742Z" fill="white"/>
|
||||||
|
<path d="M68.4035 36.1133L70.9784 41.1174C88.0432 47.2326 99.7681 63.6979 99.7681 81.9293C99.7681 105.839 80.309 125.286 56.384 125.286C32.4591 125.286 13.0094 105.839 13.0094 81.9293C13.0094 63.6884 24.7343 47.2326 41.7991 41.1174L44.3741 36.1228C23.7082 41.5257 8.99976 60.3364 8.99976 81.9293C8.99976 108.042 30.2642 129.293 56.3935 129.293C82.5228 129.293 103.787 108.042 103.787 81.9293C103.787 60.3364 89.0789 41.5163 68.4035 36.1133Z" fill="url(#paint0_linear_131_34)"/>
|
||||||
|
<mask id="mask0_131_34" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="15" y="41" width="82" height="82">
|
||||||
|
<path d="M56.3919 122.296C78.7622 122.296 96.897 104.172 96.897 81.816C96.897 59.4597 78.7622 41.3364 56.3919 41.3364C34.0215 41.3364 15.8867 59.4597 15.8867 81.816C15.8867 104.172 34.0215 122.296 56.3919 122.296Z" fill="white"/>
|
||||||
|
</mask>
|
||||||
|
<g mask="url(#mask0_131_34)">
|
||||||
|
<path d="M96.9739 80.8568C96.7459 83.9334 91.9856 96.7239 86.1326 96.7239C83.7572 96.7239 83.2252 93.2485 82.8356 90.6658C81.7809 94.7868 74.5122 103.694 69.1248 103.694C59.3287 103.694 57.0484 83.0408 56.3832 80.2871C55.7181 83.0313 53.4378 103.694 43.6417 103.694C38.2543 103.694 30.9856 94.7963 29.9309 90.6658C29.5413 93.2485 29.0188 96.7239 26.6339 96.7239C20.7809 96.7239 16.0206 83.9334 15.7926 80.8568C14.5384 82.7085 14.5574 90.191 16.4197 94.9862C22.6337 110.986 38.1973 122.352 56.3737 122.352C74.5502 122.352 90.1138 110.986 96.3278 94.9862C98.1901 90.191 98.2091 82.718 96.9549 80.8568H96.9739Z" fill="url(#paint1_linear_131_34)"/>
|
||||||
|
<path d="M56.3922 103.751C58.0075 106.713 61.523 111.148 67.547 111.148C73.571 111.148 78.2553 104.938 79.823 102.317C79.3195 104.159 78.3028 108.1 81.5808 108.1C89.0966 108.1 94.5315 96.2207 94.5315 96.2207C88.6025 111.49 73.742 122.343 56.3922 122.343C39.0423 122.343 24.1819 111.49 18.2529 96.2207C18.2529 96.2207 23.6878 108.1 31.2036 108.1C34.4911 108.1 33.4649 104.149 32.9613 102.317C34.5291 104.938 39.0613 111.148 45.2374 111.148C51.2613 111.148 54.7674 106.704 56.3922 103.751Z" fill="url(#paint2_linear_131_34)"/>
|
||||||
|
<path d="M65.9417 117.073C62.2456 117.073 59.3666 115.924 56.3927 114.073C53.4187 115.915 50.5397 117.073 46.8436 117.073C41.2092 117.073 39.4989 115.411 37.4561 113.892C37.5131 115.829 39.4894 118.564 41.5322 119.551C46.0645 121.546 51.1573 122.343 56.3927 122.343C61.628 122.343 66.7209 121.536 71.2531 119.551C73.2959 118.554 75.2628 115.829 75.3293 113.892C73.2864 115.411 71.5762 117.073 65.9417 117.073Z" fill="url(#paint3_linear_131_34)"/>
|
||||||
|
</g>
|
||||||
|
<path d="M50.9209 62.1599L45.1059 74.4756C44.8684 74.9789 44.7829 75.5486 44.8779 76.1089L46.7212 87.2376C46.7782 87.5985 47.3103 87.5985 47.3673 87.2376L48.365 81.17C48.7735 78.6537 50.1132 76.3747 52.1181 74.789L50.9209 62.1504V62.1599Z" fill="#FF4800"/>
|
||||||
|
<path d="M61.8671 62.1599L67.6821 74.4756C67.9196 74.9789 68.0051 75.5486 67.9101 76.1089L66.0668 87.2471C66.0098 87.608 65.4872 87.608 65.4207 87.2471L64.423 81.17C64.0145 78.6537 62.6748 76.3747 60.6699 74.789L61.8671 62.1504V62.1599Z" fill="#FF4800"/>
|
||||||
|
<path d="M64.9825 42.8362L57.0107 27.3774C56.7542 26.8742 56.0416 26.8742 55.7755 27.3774L47.8037 42.8267C47.4712 43.4629 47.3477 44.1941 47.4522 44.8967L51.4713 73.1365L50.8727 75.7763L52.9916 81.5876C53.0391 81.7205 53.1626 81.806 53.2956 81.806H59.4621C59.5952 81.806 59.7187 81.7205 59.7662 81.5876L61.885 75.7763L61.2864 73.1365L65.3056 44.8967C65.4101 44.1846 65.2866 43.4629 64.954 42.8267L64.9825 42.8362Z" fill="url(#paint4_linear_131_34)"/>
|
||||||
|
<path d="M56.1181 68.4835L53.2866 75.4817C52.8496 76.5642 52.764 77.7607 53.0491 78.8906L56.0706 90.988C56.1561 91.3203 56.6312 91.3203 56.7072 90.988L59.7287 78.8906C60.0137 77.7607 59.9282 76.5642 59.4911 75.4817L56.6597 68.4835C56.5552 68.2366 56.2036 68.2366 56.1086 68.4835H56.1181Z" fill="#FF4800"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear_131_34" x1="56.3935" y1="9.19338" x2="56.3935" y2="159.451" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0.3" stop-color="#00ADFF"/>
|
||||||
|
<stop offset="1" stop-color="#1542FF"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear_131_34" x1="56.3927" y1="82.0248" x2="56.3927" y2="136.026" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#FCE3AD"/>
|
||||||
|
<stop offset="0.07" stop-color="#FCE2A4"/>
|
||||||
|
<stop offset="0.19" stop-color="#FCE08B"/>
|
||||||
|
<stop offset="0.33" stop-color="#FCDD64"/>
|
||||||
|
<stop offset="0.48" stop-color="#FCDA32"/>
|
||||||
|
<stop offset="0.66" stop-color="#F9BC22"/>
|
||||||
|
<stop offset="1" stop-color="#F57700"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint2_linear_131_34" x1="56.3922" y1="95.8409" x2="56.3922" y2="126.531" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#FCD619"/>
|
||||||
|
<stop offset="0.35" stop-color="#F9B811"/>
|
||||||
|
<stop offset="1" stop-color="#F57700"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint3_linear_131_34" x1="56.3927" y1="111.186" x2="56.3927" y2="125.334" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#F57700"/>
|
||||||
|
<stop offset="1" stop-color="#FF4800"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint4_linear_131_34" x1="56.3931" y1="27.0071" x2="56.3931" y2="98.3757" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#E4E4E4"/>
|
||||||
|
<stop offset="0.88" stop-color="#B6B8B8"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 10 KiB |
BIN
assets/me/2025_Resume_Michael_Braicu.pdf
Normal file
BIN
assets/me/me_stream_doom.PNG
Normal file
|
After Width: | Height: | Size: 756 KiB |
BIN
assets/me/wampyDance.gif
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
432
authors/index.html
Normal file
@@ -0,0 +1,432 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html
|
||||||
|
lang="en"
|
||||||
|
dir="ltr"
|
||||||
|
class="scroll-smooth"
|
||||||
|
data-default-appearance="light"
|
||||||
|
data-auto-appearance="true"><head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
|
||||||
|
<meta http-equiv="content-language" content="en">
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<meta name="theme-color">
|
||||||
|
|
||||||
|
|
||||||
|
<title>Authors · Blowfish</title>
|
||||||
|
<meta name="title" content="Authors · Blowfish">
|
||||||
|
|
||||||
|
|
||||||
|
<meta name="description" content="My awesome website">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="canonical" href="/authors/">
|
||||||
|
|
||||||
|
<link rel="alternate" type="application/rss+xml" href="/authors/index.xml" title="Blowfish" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<link
|
||||||
|
type="text/css"
|
||||||
|
rel="stylesheet"
|
||||||
|
href="/css/main.bundle.min.d7672814413dccdcf76c504d9b0ebb8d0f964dbe6b30a3d4b25b15809a94c2eb732d99898aded2009aacdd1fa8b17cea1369fd81560790cd355aee00bbd142fd.css"
|
||||||
|
integrity="sha512-12coFEE9zNz3bFBNmw67jQ+WTb5rMKPUslsVgJqUwutzLZmJit7SAJqs3R+osXzqE2n9gVYHkM01Wu4Au9FC/Q==">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script
|
||||||
|
type="text/javascript"
|
||||||
|
src="/js/appearance.min.516a16745bea5a9bd011138d254cc0fd3973cd55ce6e15f3dec763e7c7c2c7448f8fe7b54cca811cb821b0c7e12cd161caace1dd794ac3d34d40937cbcc9ee12.js"
|
||||||
|
integrity="sha512-UWoWdFvqWpvQERONJUzA/TlzzVXObhXz3sdj58fCx0SPj+e1TMqBHLghsMfhLNFhyqzh3XlKw9NNQJN8vMnuEg=="></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script
|
||||||
|
defer
|
||||||
|
type="text/javascript"
|
||||||
|
id="script-bundle"
|
||||||
|
src="/js/main.bundle.min.98bb8ce8f123e03c4d2171eeec151dd404b5c476beb5ef1068d0e8b1a2afb923065e6552fc14b2d867c85ee4bd8529a43193f729f874d3908517ba03b5d1cc57.js"
|
||||||
|
integrity="sha512-mLuM6PEj4DxNIXHu7BUd1AS1xHa+te8QaNDosaKvuSMGXmVS/BSy2GfIXuS9hSmkMZP3Kfh005CFF7oDtdHMVw=="
|
||||||
|
data-copy="Copy"
|
||||||
|
data-copied="Copied"></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script src="/lib/zoom/zoom.min.umd.a527109b68c082a70f3697716dd72a9d5aa8b545cf800cecbbc7399f2ca6f6e0ce3e431f2062b48bbfa47c9ea42822714060bef309be073f49b9c0e30d318d7b.js" integrity="sha512-pScQm2jAgqcPNpdxbdcqnVqotUXPgAzsu8c5nyym9uDOPkMfIGK0i7+kfJ6kKCJxQGC+8wm+Bz9JucDjDTGNew=="></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
|
||||||
|
<link rel="manifest" href="/site.webmanifest">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<meta property="og:url" content="/authors/">
|
||||||
|
<meta property="og:site_name" content="Blowfish">
|
||||||
|
<meta property="og:title" content="Authors">
|
||||||
|
<meta property="og:description" content="My awesome website">
|
||||||
|
<meta property="og:locale" content="en">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
|
||||||
|
|
||||||
|
<meta name="twitter:card" content="summary">
|
||||||
|
<meta name="twitter:title" content="Authors">
|
||||||
|
<meta name="twitter:description" content="My awesome website">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<meta name="author" content="Your name here">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script src="/lib/jquery/jquery.slim.min.b0dca576e87d7eaa5850ae4e61759c065786cdb6489d68fcc82240539eebd5da522bdb4fda085ffd245808c8fe2acb2516408eb774ef26b5f6015fc6737c0ea8.js" integrity="sha512-sNylduh9fqpYUK5OYXWcBleGzbZInWj8yCJAU57r1dpSK9tP2ghf/SRYCMj+KsslFkCOt3TvJrX2AV/Gc3wOqA=="></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body
|
||||||
|
class="flex flex-col h-screen px-6 m-auto text-lg leading-7 max-w-7xl bg-neutral text-neutral-900 dark:bg-neutral-800 dark:text-neutral sm:px-14 md:px-24 lg:px-32 scrollbar-thin scrollbar-track-neutral-200 scrollbar-thumb-neutral-400 dark:scrollbar-track-neutral-800 dark:scrollbar-thumb-neutral-600">
|
||||||
|
<div id="the-top" class="absolute flex self-center">
|
||||||
|
<a
|
||||||
|
class="px-3 py-1 text-sm -translate-y-8 rounded-b-lg bg-primary-200 focus:translate-y-0 dark:bg-neutral-600"
|
||||||
|
href="#main-content">
|
||||||
|
<span class="font-bold text-primary-600 ltr:pr-2 rtl:pl-2 dark:text-primary-400">↓</span>
|
||||||
|
Skip to main content
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="main-menu flex items-center justify-between px-4 py-6 sm:px-6 md:justify-start gap-x-3 pt-[2px] pr-0 pb-[3px] pl-0">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="flex flex-1 items-center justify-between">
|
||||||
|
<nav class="flex space-x-3">
|
||||||
|
|
||||||
|
<a href="/" class="text-base font-medium text-gray-500 hover:text-gray-900">
|
||||||
|
Blowfish
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<nav class="hidden md:flex items-center gap-x-5 md:ml-12 h-12">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<button
|
||||||
|
id="search-button"
|
||||||
|
aria-label="Search"
|
||||||
|
class="text-base hover:text-primary-600 dark:hover:text-primary-400"
|
||||||
|
title="Search (/)">
|
||||||
|
<span class="relative block icon"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="search" class="svg-inline--fa fa-search fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"/></svg>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class=" flex items-center">
|
||||||
|
<button
|
||||||
|
id="appearance-switcher"
|
||||||
|
aria-label="Dark mode switcher"
|
||||||
|
type="button"
|
||||||
|
class="text-base hover:text-primary-600 dark:hover:text-primary-400">
|
||||||
|
<div class="flex items-center justify-center dark:hidden">
|
||||||
|
<span class="relative block icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M32 256c0-123.8 100.3-224 223.8-224c11.36 0 29.7 1.668 40.9 3.746c9.616 1.777 11.75 14.63 3.279 19.44C245 86.5 211.2 144.6 211.2 207.8c0 109.7 99.71 193 208.3 172.3c9.561-1.805 16.28 9.324 10.11 16.95C387.9 448.6 324.8 480 255.8 480C132.1 480 32 379.6 32 256z"/></svg>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="items-center justify-center hidden dark:flex">
|
||||||
|
<span class="relative block icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 159.1c-53.02 0-95.1 42.98-95.1 95.1S202.1 351.1 256 351.1s95.1-42.98 95.1-95.1S309 159.1 256 159.1zM509.3 347L446.1 255.1l63.15-91.01c6.332-9.125 1.104-21.74-9.826-23.72l-109-19.7l-19.7-109c-1.975-10.93-14.59-16.16-23.72-9.824L256 65.89L164.1 2.736c-9.125-6.332-21.74-1.107-23.72 9.824L121.6 121.6L12.56 141.3C1.633 143.2-3.596 155.9 2.736 164.1L65.89 256l-63.15 91.01c-6.332 9.125-1.105 21.74 9.824 23.72l109 19.7l19.7 109c1.975 10.93 14.59 16.16 23.72 9.824L256 446.1l91.01 63.15c9.127 6.334 21.75 1.107 23.72-9.822l19.7-109l109-19.7C510.4 368.8 515.6 356.1 509.3 347zM256 383.1c-70.69 0-127.1-57.31-127.1-127.1c0-70.69 57.31-127.1 127.1-127.1s127.1 57.3 127.1 127.1C383.1 326.7 326.7 383.1 256 383.1z"/></svg>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="flex md:hidden items-center gap-x-5 md:ml-12 h-12">
|
||||||
|
<span></span>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<button
|
||||||
|
id="search-button-mobile"
|
||||||
|
aria-label="Search"
|
||||||
|
class="text-base hover:text-primary-600 dark:hover:text-primary-400"
|
||||||
|
title="Search (/)">
|
||||||
|
<span class="relative block icon"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="search" class="svg-inline--fa fa-search fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"/></svg>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<button
|
||||||
|
id="appearance-switcher-mobile"
|
||||||
|
aria-label="Dark mode switcher"
|
||||||
|
type="button"
|
||||||
|
class="text-base hover:text-primary-600 dark:hover:text-primary-400 ltr:mr-1 rtl:ml-1">
|
||||||
|
<div class="flex items-center justify-center dark:hidden">
|
||||||
|
<span class="relative block icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M32 256c0-123.8 100.3-224 223.8-224c11.36 0 29.7 1.668 40.9 3.746c9.616 1.777 11.75 14.63 3.279 19.44C245 86.5 211.2 144.6 211.2 207.8c0 109.7 99.71 193 208.3 172.3c9.561-1.805 16.28 9.324 10.11 16.95C387.9 448.6 324.8 480 255.8 480C132.1 480 32 379.6 32 256z"/></svg>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="items-center justify-center hidden dark:flex">
|
||||||
|
<span class="relative block icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 159.1c-53.02 0-95.1 42.98-95.1 95.1S202.1 351.1 256 351.1s95.1-42.98 95.1-95.1S309 159.1 256 159.1zM509.3 347L446.1 255.1l63.15-91.01c6.332-9.125 1.104-21.74-9.826-23.72l-109-19.7l-19.7-109c-1.975-10.93-14.59-16.16-23.72-9.824L256 65.89L164.1 2.736c-9.125-6.332-21.74-1.107-23.72 9.824L121.6 121.6L12.56 141.3C1.633 143.2-3.596 155.9 2.736 164.1L65.89 256l-63.15 91.01c-6.332 9.125-1.105 21.74 9.824 23.72l109 19.7l19.7 109c1.975 10.93 14.59 16.16 23.72 9.824L256 446.1l91.01 63.15c9.127 6.334 21.75 1.107 23.72-9.822l19.7-109l109-19.7C510.4 368.8 515.6 356.1 509.3 347zM256 383.1c-70.69 0-127.1-57.31-127.1-127.1c0-70.69 57.31-127.1 127.1-127.1s127.1 57.3 127.1 127.1C383.1 326.7 326.7 383.1 256 383.1z"/></svg>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="-my-2 md:hidden">
|
||||||
|
<div id="menu-button" class="block">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="relative flex flex-col grow">
|
||||||
|
<main id="main-content" class="grow">
|
||||||
|
|
||||||
|
|
||||||
|
<header>
|
||||||
|
|
||||||
|
<h1 class="mt-5 text-4xl font-extrabold text-neutral-900 dark:text-neutral">Authors</h1>
|
||||||
|
<div class="mt-1 mb-2 text-base text-neutral-500 dark:text-neutral-400 print:hidden">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="flex flex-row flex-wrap items-center">
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<section class="flex flex-wrap max-w-prose -mx-2 overflow-hidden">
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</main><footer id="site-footer" class="py-10 print:hidden">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
|
||||||
|
|
||||||
|
<p class="text-sm text-neutral-500 dark:text-neutral-400">
|
||||||
|
©
|
||||||
|
2025
|
||||||
|
Your name here
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p class="text-xs text-neutral-500 dark:text-neutral-400">
|
||||||
|
|
||||||
|
|
||||||
|
Powered by <a class="hover:underline hover:decoration-primary-400 hover:text-primary-500"
|
||||||
|
href="https://gohugo.io/" target="_blank" rel="noopener noreferrer">Hugo</a> & <a class="hover:underline hover:decoration-primary-400 hover:text-primary-500"
|
||||||
|
href="https://blowfish.page/" target="_blank" rel="noopener noreferrer">Blowfish</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
mediumZoom(document.querySelectorAll("img:not(.nozoom)"), {
|
||||||
|
margin: 24,
|
||||||
|
background: "rgba(0,0,0,0.5)",
|
||||||
|
scrollOffset: 0,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script
|
||||||
|
type="text/javascript"
|
||||||
|
src="/js/process.min.ee03488f19c93c2efb199e2e3014ea5f3cb2ce7d45154adb3399a158cac27ca52831db249ede5bb602700ef87eb02434139de0858af1818ab0fb4182472204a4.js"
|
||||||
|
integrity="sha512-7gNIjxnJPC77GZ4uMBTqXzyyzn1FFUrbM5mhWMrCfKUoMdsknt5btgJwDvh+sCQ0E53ghYrxgYqw+0GCRyIEpA=="></script>
|
||||||
|
|
||||||
|
|
||||||
|
</footer>
|
||||||
|
<div
|
||||||
|
id="search-wrapper"
|
||||||
|
class="invisible fixed inset-0 flex h-screen w-screen cursor-default flex-col bg-neutral-500/50 p-4 backdrop-blur-sm dark:bg-neutral-900/50 sm:p-6 md:p-[10vh] lg:p-[12vh] z-500"
|
||||||
|
data-url="/">
|
||||||
|
<div
|
||||||
|
id="search-modal"
|
||||||
|
class="flex flex-col w-full max-w-3xl min-h-0 mx-auto border rounded-md shadow-lg top-20 border-neutral-200 bg-neutral dark:border-neutral-700 dark:bg-neutral-800">
|
||||||
|
<header class="relative z-10 flex items-center justify-between flex-none px-2">
|
||||||
|
<form class="flex items-center flex-auto min-w-0">
|
||||||
|
<div class="flex items-center justify-center w-8 h-8 text-neutral-400">
|
||||||
|
<span class="relative block icon"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="search" class="svg-inline--fa fa-search fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"/></svg>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type="search"
|
||||||
|
id="search-query"
|
||||||
|
class="flex flex-auto h-12 mx-1 bg-transparent appearance-none focus:outline-dotted focus:outline-2 focus:outline-transparent"
|
||||||
|
placeholder="Search"
|
||||||
|
tabindex="0">
|
||||||
|
</form>
|
||||||
|
<button
|
||||||
|
id="close-search-button"
|
||||||
|
class="flex items-center justify-center w-8 h-8 text-neutral-700 hover:text-primary-600 dark:text-neutral dark:hover:text-primary-400"
|
||||||
|
title="Close (Esc)">
|
||||||
|
<span class="relative block icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M310.6 361.4c12.5 12.5 12.5 32.75 0 45.25C304.4 412.9 296.2 416 288 416s-16.38-3.125-22.62-9.375L160 301.3L54.63 406.6C48.38 412.9 40.19 416 32 416S15.63 412.9 9.375 406.6c-12.5-12.5-12.5-32.75 0-45.25l105.4-105.4L9.375 150.6c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L160 210.8l105.4-105.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-105.4 105.4L310.6 361.4z"/></svg>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</header>
|
||||||
|
<section class="flex-auto px-2 overflow-auto">
|
||||||
|
<ul id="search-results">
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
432
categories/index.html
Normal file
@@ -0,0 +1,432 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html
|
||||||
|
lang="en"
|
||||||
|
dir="ltr"
|
||||||
|
class="scroll-smooth"
|
||||||
|
data-default-appearance="light"
|
||||||
|
data-auto-appearance="true"><head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
|
||||||
|
<meta http-equiv="content-language" content="en">
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<meta name="theme-color">
|
||||||
|
|
||||||
|
|
||||||
|
<title>Categories · Blowfish</title>
|
||||||
|
<meta name="title" content="Categories · Blowfish">
|
||||||
|
|
||||||
|
|
||||||
|
<meta name="description" content="My awesome website">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="canonical" href="/categories/">
|
||||||
|
|
||||||
|
<link rel="alternate" type="application/rss+xml" href="/categories/index.xml" title="Blowfish" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<link
|
||||||
|
type="text/css"
|
||||||
|
rel="stylesheet"
|
||||||
|
href="/css/main.bundle.min.d7672814413dccdcf76c504d9b0ebb8d0f964dbe6b30a3d4b25b15809a94c2eb732d99898aded2009aacdd1fa8b17cea1369fd81560790cd355aee00bbd142fd.css"
|
||||||
|
integrity="sha512-12coFEE9zNz3bFBNmw67jQ+WTb5rMKPUslsVgJqUwutzLZmJit7SAJqs3R+osXzqE2n9gVYHkM01Wu4Au9FC/Q==">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script
|
||||||
|
type="text/javascript"
|
||||||
|
src="/js/appearance.min.516a16745bea5a9bd011138d254cc0fd3973cd55ce6e15f3dec763e7c7c2c7448f8fe7b54cca811cb821b0c7e12cd161caace1dd794ac3d34d40937cbcc9ee12.js"
|
||||||
|
integrity="sha512-UWoWdFvqWpvQERONJUzA/TlzzVXObhXz3sdj58fCx0SPj+e1TMqBHLghsMfhLNFhyqzh3XlKw9NNQJN8vMnuEg=="></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script
|
||||||
|
defer
|
||||||
|
type="text/javascript"
|
||||||
|
id="script-bundle"
|
||||||
|
src="/js/main.bundle.min.98bb8ce8f123e03c4d2171eeec151dd404b5c476beb5ef1068d0e8b1a2afb923065e6552fc14b2d867c85ee4bd8529a43193f729f874d3908517ba03b5d1cc57.js"
|
||||||
|
integrity="sha512-mLuM6PEj4DxNIXHu7BUd1AS1xHa+te8QaNDosaKvuSMGXmVS/BSy2GfIXuS9hSmkMZP3Kfh005CFF7oDtdHMVw=="
|
||||||
|
data-copy="Copy"
|
||||||
|
data-copied="Copied"></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script src="/lib/zoom/zoom.min.umd.a527109b68c082a70f3697716dd72a9d5aa8b545cf800cecbbc7399f2ca6f6e0ce3e431f2062b48bbfa47c9ea42822714060bef309be073f49b9c0e30d318d7b.js" integrity="sha512-pScQm2jAgqcPNpdxbdcqnVqotUXPgAzsu8c5nyym9uDOPkMfIGK0i7+kfJ6kKCJxQGC+8wm+Bz9JucDjDTGNew=="></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
|
||||||
|
<link rel="manifest" href="/site.webmanifest">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<meta property="og:url" content="/categories/">
|
||||||
|
<meta property="og:site_name" content="Blowfish">
|
||||||
|
<meta property="og:title" content="Categories">
|
||||||
|
<meta property="og:description" content="My awesome website">
|
||||||
|
<meta property="og:locale" content="en">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
|
||||||
|
|
||||||
|
<meta name="twitter:card" content="summary">
|
||||||
|
<meta name="twitter:title" content="Categories">
|
||||||
|
<meta name="twitter:description" content="My awesome website">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<meta name="author" content="Your name here">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script src="/lib/jquery/jquery.slim.min.b0dca576e87d7eaa5850ae4e61759c065786cdb6489d68fcc82240539eebd5da522bdb4fda085ffd245808c8fe2acb2516408eb774ef26b5f6015fc6737c0ea8.js" integrity="sha512-sNylduh9fqpYUK5OYXWcBleGzbZInWj8yCJAU57r1dpSK9tP2ghf/SRYCMj+KsslFkCOt3TvJrX2AV/Gc3wOqA=="></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body
|
||||||
|
class="flex flex-col h-screen px-6 m-auto text-lg leading-7 max-w-7xl bg-neutral text-neutral-900 dark:bg-neutral-800 dark:text-neutral sm:px-14 md:px-24 lg:px-32 scrollbar-thin scrollbar-track-neutral-200 scrollbar-thumb-neutral-400 dark:scrollbar-track-neutral-800 dark:scrollbar-thumb-neutral-600">
|
||||||
|
<div id="the-top" class="absolute flex self-center">
|
||||||
|
<a
|
||||||
|
class="px-3 py-1 text-sm -translate-y-8 rounded-b-lg bg-primary-200 focus:translate-y-0 dark:bg-neutral-600"
|
||||||
|
href="#main-content">
|
||||||
|
<span class="font-bold text-primary-600 ltr:pr-2 rtl:pl-2 dark:text-primary-400">↓</span>
|
||||||
|
Skip to main content
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="main-menu flex items-center justify-between px-4 py-6 sm:px-6 md:justify-start gap-x-3 pt-[2px] pr-0 pb-[3px] pl-0">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="flex flex-1 items-center justify-between">
|
||||||
|
<nav class="flex space-x-3">
|
||||||
|
|
||||||
|
<a href="/" class="text-base font-medium text-gray-500 hover:text-gray-900">
|
||||||
|
Blowfish
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<nav class="hidden md:flex items-center gap-x-5 md:ml-12 h-12">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<button
|
||||||
|
id="search-button"
|
||||||
|
aria-label="Search"
|
||||||
|
class="text-base hover:text-primary-600 dark:hover:text-primary-400"
|
||||||
|
title="Search (/)">
|
||||||
|
<span class="relative block icon"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="search" class="svg-inline--fa fa-search fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"/></svg>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class=" flex items-center">
|
||||||
|
<button
|
||||||
|
id="appearance-switcher"
|
||||||
|
aria-label="Dark mode switcher"
|
||||||
|
type="button"
|
||||||
|
class="text-base hover:text-primary-600 dark:hover:text-primary-400">
|
||||||
|
<div class="flex items-center justify-center dark:hidden">
|
||||||
|
<span class="relative block icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M32 256c0-123.8 100.3-224 223.8-224c11.36 0 29.7 1.668 40.9 3.746c9.616 1.777 11.75 14.63 3.279 19.44C245 86.5 211.2 144.6 211.2 207.8c0 109.7 99.71 193 208.3 172.3c9.561-1.805 16.28 9.324 10.11 16.95C387.9 448.6 324.8 480 255.8 480C132.1 480 32 379.6 32 256z"/></svg>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="items-center justify-center hidden dark:flex">
|
||||||
|
<span class="relative block icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 159.1c-53.02 0-95.1 42.98-95.1 95.1S202.1 351.1 256 351.1s95.1-42.98 95.1-95.1S309 159.1 256 159.1zM509.3 347L446.1 255.1l63.15-91.01c6.332-9.125 1.104-21.74-9.826-23.72l-109-19.7l-19.7-109c-1.975-10.93-14.59-16.16-23.72-9.824L256 65.89L164.1 2.736c-9.125-6.332-21.74-1.107-23.72 9.824L121.6 121.6L12.56 141.3C1.633 143.2-3.596 155.9 2.736 164.1L65.89 256l-63.15 91.01c-6.332 9.125-1.105 21.74 9.824 23.72l109 19.7l19.7 109c1.975 10.93 14.59 16.16 23.72 9.824L256 446.1l91.01 63.15c9.127 6.334 21.75 1.107 23.72-9.822l19.7-109l109-19.7C510.4 368.8 515.6 356.1 509.3 347zM256 383.1c-70.69 0-127.1-57.31-127.1-127.1c0-70.69 57.31-127.1 127.1-127.1s127.1 57.3 127.1 127.1C383.1 326.7 326.7 383.1 256 383.1z"/></svg>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="flex md:hidden items-center gap-x-5 md:ml-12 h-12">
|
||||||
|
<span></span>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<button
|
||||||
|
id="search-button-mobile"
|
||||||
|
aria-label="Search"
|
||||||
|
class="text-base hover:text-primary-600 dark:hover:text-primary-400"
|
||||||
|
title="Search (/)">
|
||||||
|
<span class="relative block icon"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="search" class="svg-inline--fa fa-search fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"/></svg>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<button
|
||||||
|
id="appearance-switcher-mobile"
|
||||||
|
aria-label="Dark mode switcher"
|
||||||
|
type="button"
|
||||||
|
class="text-base hover:text-primary-600 dark:hover:text-primary-400 ltr:mr-1 rtl:ml-1">
|
||||||
|
<div class="flex items-center justify-center dark:hidden">
|
||||||
|
<span class="relative block icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M32 256c0-123.8 100.3-224 223.8-224c11.36 0 29.7 1.668 40.9 3.746c9.616 1.777 11.75 14.63 3.279 19.44C245 86.5 211.2 144.6 211.2 207.8c0 109.7 99.71 193 208.3 172.3c9.561-1.805 16.28 9.324 10.11 16.95C387.9 448.6 324.8 480 255.8 480C132.1 480 32 379.6 32 256z"/></svg>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="items-center justify-center hidden dark:flex">
|
||||||
|
<span class="relative block icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 159.1c-53.02 0-95.1 42.98-95.1 95.1S202.1 351.1 256 351.1s95.1-42.98 95.1-95.1S309 159.1 256 159.1zM509.3 347L446.1 255.1l63.15-91.01c6.332-9.125 1.104-21.74-9.826-23.72l-109-19.7l-19.7-109c-1.975-10.93-14.59-16.16-23.72-9.824L256 65.89L164.1 2.736c-9.125-6.332-21.74-1.107-23.72 9.824L121.6 121.6L12.56 141.3C1.633 143.2-3.596 155.9 2.736 164.1L65.89 256l-63.15 91.01c-6.332 9.125-1.105 21.74 9.824 23.72l109 19.7l19.7 109c1.975 10.93 14.59 16.16 23.72 9.824L256 446.1l91.01 63.15c9.127 6.334 21.75 1.107 23.72-9.822l19.7-109l109-19.7C510.4 368.8 515.6 356.1 509.3 347zM256 383.1c-70.69 0-127.1-57.31-127.1-127.1c0-70.69 57.31-127.1 127.1-127.1s127.1 57.3 127.1 127.1C383.1 326.7 326.7 383.1 256 383.1z"/></svg>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="-my-2 md:hidden">
|
||||||
|
<div id="menu-button" class="block">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="relative flex flex-col grow">
|
||||||
|
<main id="main-content" class="grow">
|
||||||
|
|
||||||
|
|
||||||
|
<header>
|
||||||
|
|
||||||
|
<h1 class="mt-5 text-4xl font-extrabold text-neutral-900 dark:text-neutral">Categories</h1>
|
||||||
|
<div class="mt-1 mb-2 text-base text-neutral-500 dark:text-neutral-400 print:hidden">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="flex flex-row flex-wrap items-center">
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<section class="flex flex-wrap max-w-prose -mx-2 overflow-hidden">
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</main><footer id="site-footer" class="py-10 print:hidden">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
|
||||||
|
|
||||||
|
<p class="text-sm text-neutral-500 dark:text-neutral-400">
|
||||||
|
©
|
||||||
|
2025
|
||||||
|
Your name here
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p class="text-xs text-neutral-500 dark:text-neutral-400">
|
||||||
|
|
||||||
|
|
||||||
|
Powered by <a class="hover:underline hover:decoration-primary-400 hover:text-primary-500"
|
||||||
|
href="https://gohugo.io/" target="_blank" rel="noopener noreferrer">Hugo</a> & <a class="hover:underline hover:decoration-primary-400 hover:text-primary-500"
|
||||||
|
href="https://blowfish.page/" target="_blank" rel="noopener noreferrer">Blowfish</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
mediumZoom(document.querySelectorAll("img:not(.nozoom)"), {
|
||||||
|
margin: 24,
|
||||||
|
background: "rgba(0,0,0,0.5)",
|
||||||
|
scrollOffset: 0,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<script
|
||||||
|
type="text/javascript"
|
||||||
|
src="/js/process.min.ee03488f19c93c2efb199e2e3014ea5f3cb2ce7d45154adb3399a158cac27ca52831db249ede5bb602700ef87eb02434139de0858af1818ab0fb4182472204a4.js"
|
||||||
|
integrity="sha512-7gNIjxnJPC77GZ4uMBTqXzyyzn1FFUrbM5mhWMrCfKUoMdsknt5btgJwDvh+sCQ0E53ghYrxgYqw+0GCRyIEpA=="></script>
|
||||||
|
|
||||||
|
|
||||||
|
</footer>
|
||||||
|
<div
|
||||||
|
id="search-wrapper"
|
||||||
|
class="invisible fixed inset-0 flex h-screen w-screen cursor-default flex-col bg-neutral-500/50 p-4 backdrop-blur-sm dark:bg-neutral-900/50 sm:p-6 md:p-[10vh] lg:p-[12vh] z-500"
|
||||||
|
data-url="/">
|
||||||
|
<div
|
||||||
|
id="search-modal"
|
||||||
|
class="flex flex-col w-full max-w-3xl min-h-0 mx-auto border rounded-md shadow-lg top-20 border-neutral-200 bg-neutral dark:border-neutral-700 dark:bg-neutral-800">
|
||||||
|
<header class="relative z-10 flex items-center justify-between flex-none px-2">
|
||||||
|
<form class="flex items-center flex-auto min-w-0">
|
||||||
|
<div class="flex items-center justify-center w-8 h-8 text-neutral-400">
|
||||||
|
<span class="relative block icon"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="search" class="svg-inline--fa fa-search fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"/></svg>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type="search"
|
||||||
|
id="search-query"
|
||||||
|
class="flex flex-auto h-12 mx-1 bg-transparent appearance-none focus:outline-dotted focus:outline-2 focus:outline-transparent"
|
||||||
|
placeholder="Search"
|
||||||
|
tabindex="0">
|
||||||
|
</form>
|
||||||
|
<button
|
||||||
|
id="close-search-button"
|
||||||
|
class="flex items-center justify-center w-8 h-8 text-neutral-700 hover:text-primary-600 dark:text-neutral dark:hover:text-primary-400"
|
||||||
|
title="Close (Esc)">
|
||||||
|
<span class="relative block icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M310.6 361.4c12.5 12.5 12.5 32.75 0 45.25C304.4 412.9 296.2 416 288 416s-16.38-3.125-22.62-9.375L160 301.3L54.63 406.6C48.38 412.9 40.19 416 32 416S15.63 412.9 9.375 406.6c-12.5-12.5-12.5-32.75 0-45.25l105.4-105.4L9.375 150.6c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L160 210.8l105.4-105.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-105.4 105.4L310.6 361.4z"/></svg>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</header>
|
||||||
|
<section class="flex-auto px-2 overflow-auto">
|
||||||
|
<ul id="search-results">
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
47
config/_default/hugo.toml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# -- Site Configuration --
|
||||||
|
# Refer to the theme docs for more details about each of these parameters.
|
||||||
|
# https://nunocoracao.github.io/blowfish/docs/getting-started/
|
||||||
|
|
||||||
|
theme = ["hugo-admonitions", "blowfish"]
|
||||||
|
baseURL = "https://wiki.wompmacho.com"
|
||||||
|
defaultContentLanguage = "en"
|
||||||
|
relativeURLs = true
|
||||||
|
|
||||||
|
enableRobotsTXT = true
|
||||||
|
summaryLength = 5
|
||||||
|
|
||||||
|
buildDrafts = true
|
||||||
|
buildFuture = true
|
||||||
|
enableEmoji = true
|
||||||
|
|
||||||
|
# googleAnalytics = "G-XXXXXXXXX"
|
||||||
|
|
||||||
|
[taxonomies]
|
||||||
|
tag = "tags"
|
||||||
|
category = "categories"
|
||||||
|
author = "authors"
|
||||||
|
series = "series"
|
||||||
|
|
||||||
|
[sitemap]
|
||||||
|
changefreq = 'daily'
|
||||||
|
filename = 'sitemap.xml'
|
||||||
|
priority = 0.5
|
||||||
|
|
||||||
|
[outputs]
|
||||||
|
home = ["HTML", "RSS", "JSON"]
|
||||||
|
|
||||||
|
[frontmatter]
|
||||||
|
# Hugo will check for 'lastmod' in the file first,
|
||||||
|
# then the Git commit date, then the 'date' field.
|
||||||
|
lastmod = ["lastmod", ":git", "date"]
|
||||||
|
|
||||||
|
# Required if you want to use the Git commit date automatically
|
||||||
|
enableGitInfo = true
|
||||||
|
|
||||||
|
[markup.tableOfContents]
|
||||||
|
endLevel = 3
|
||||||
|
ordered = false
|
||||||
|
startLevel = 2
|
||||||
|
|
||||||
|
[params.article]
|
||||||
|
showTableOfContents = true
|
||||||
27
config/_default/languages.en.toml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
languageCode = "en"
|
||||||
|
languageName = "English"
|
||||||
|
weight = 1
|
||||||
|
title = "wiki.wompmacho.com"
|
||||||
|
|
||||||
|
[params]
|
||||||
|
displayName = "EN"
|
||||||
|
isoCode = "en"
|
||||||
|
rtl = false
|
||||||
|
description = "my personal wiki"
|
||||||
|
dateFormat = "2 January 2006"
|
||||||
|
|
||||||
|
[params.author]
|
||||||
|
name = "Michael"
|
||||||
|
image = "me/wampyDance.gif"
|
||||||
|
headline = "aka `Mitch` / aka `WompMacho`"
|
||||||
|
bio = "some dude that works on datacenters, plays guitar, streams, has a lot of side projects and unhealthy addiction to ow"
|
||||||
|
|
||||||
|
[params.author.social]
|
||||||
|
gitlab = "https://git.wompmacho.com/wompmacho"
|
||||||
|
email = "michael@wompmacho.com"
|
||||||
|
streamelements = "https://streamelements.com/wompmacho/tip"
|
||||||
|
discord = "https://discord.com/invite/P5aZU7jHbD"
|
||||||
|
twitch = "https://www.twitch.tv/wompmacho"
|
||||||
|
youtube = "https://www.youtube.com/wompmacho?sub_confirmation=1"
|
||||||
|
|
||||||
|
|
||||||
19
config/_default/markup.toml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# -- Markup --
|
||||||
|
# These settings are required for the theme to function.
|
||||||
|
|
||||||
|
[goldmark]
|
||||||
|
[goldmark.renderer]
|
||||||
|
unsafe = true
|
||||||
|
|
||||||
|
[markup]
|
||||||
|
[markup.tableOfContents]
|
||||||
|
startLevel = 2
|
||||||
|
endLevel = 4
|
||||||
|
ordered = false
|
||||||
|
|
||||||
|
[highlight]
|
||||||
|
noClasses = false
|
||||||
|
|
||||||
|
[markup.goldmark.parser.attribute]
|
||||||
|
block = true
|
||||||
|
title = true
|
||||||
57
config/_default/menus.en.toml
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# -- Main Menu --
|
||||||
|
# The main menu is displayed in the header at the top of the page.
|
||||||
|
# Acceptable parameters are name, pageRef, page, url, title, weight.
|
||||||
|
#
|
||||||
|
# The simplest menu configuration is to provide:
|
||||||
|
# name = The name to be displayed for this menu link
|
||||||
|
# pageRef = The identifier of the page or section to link to
|
||||||
|
#
|
||||||
|
# By default the menu is ordered alphabetically. This can be
|
||||||
|
# overridden by providing a weight value. The menu will then be
|
||||||
|
# ordered by weight from lowest to highest.
|
||||||
|
|
||||||
|
[[main]]
|
||||||
|
name = "projects"
|
||||||
|
pageRef = "projects"
|
||||||
|
weight = 10
|
||||||
|
|
||||||
|
[[main]]
|
||||||
|
name = "posts"
|
||||||
|
pageRef = "posts"
|
||||||
|
weight = 20
|
||||||
|
|
||||||
|
[[main]]
|
||||||
|
name = "stream"
|
||||||
|
pageRef = "stream"
|
||||||
|
weight = 30
|
||||||
|
|
||||||
|
[[subnavigation]]
|
||||||
|
name = "lab"
|
||||||
|
pageRef = "lab_setup"
|
||||||
|
weight = 10
|
||||||
|
|
||||||
|
[[subnavigation]]
|
||||||
|
name = "resume"
|
||||||
|
pageRef = "resume"
|
||||||
|
weight = 20
|
||||||
|
|
||||||
|
# [[subnavigation]]
|
||||||
|
# name = "My Awesome Category"
|
||||||
|
# pageRef = "categories/awesome"
|
||||||
|
# weight = 20
|
||||||
|
|
||||||
|
# -- Footer Menu --
|
||||||
|
# The footer menu is displayed at the bottom of the page, just before
|
||||||
|
# the copyright notice. Configure as per the main menu above.
|
||||||
|
|
||||||
|
# config/_default/menus.toml
|
||||||
|
|
||||||
|
# [[footer]]
|
||||||
|
# name = "Tags"
|
||||||
|
# pageRef = "tags"
|
||||||
|
# weight = 10
|
||||||
|
|
||||||
|
# [[footer]]
|
||||||
|
# name = "Categories"
|
||||||
|
# pageRef = "categories"
|
||||||
|
# weight = 20
|
||||||
3
config/_default/module.toml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[hugoVersion]
|
||||||
|
extended = false
|
||||||
|
min = "0.87.0"
|
||||||
111
config/_default/params.toml
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
# -- Theme Options --
|
||||||
|
# These options control how the theme functions and allow you to
|
||||||
|
# customise the display of your website.
|
||||||
|
#
|
||||||
|
# Refer to the theme docs for more details about each of these parameters.
|
||||||
|
# https://nunocoracao.github.io/blowfish/docs/configuration/#theme-parameters
|
||||||
|
|
||||||
|
colorScheme = "github"
|
||||||
|
defaultAppearance = "dark" # valid options: light or dark
|
||||||
|
autoSwitchAppearance = true
|
||||||
|
|
||||||
|
enableSearch = true
|
||||||
|
enableCodeCopy = true
|
||||||
|
customCSS = true
|
||||||
|
|
||||||
|
# mainSections = ["section1", "section2"]
|
||||||
|
# robots = ""
|
||||||
|
|
||||||
|
[header]
|
||||||
|
layout = "fixed" # valid options: basic, fixed
|
||||||
|
|
||||||
|
[footer]
|
||||||
|
# showMenu = true
|
||||||
|
showCopyright = true
|
||||||
|
showThemeAttribution = true
|
||||||
|
showAppearanceSwitcher = true
|
||||||
|
showScrollToTop = true
|
||||||
|
|
||||||
|
[homepage]
|
||||||
|
layout = "background" # valid options: page, profile, hero, card, background, custom
|
||||||
|
homepageImage = "me/me_stream_doom.PNG" # used in: hero, and card
|
||||||
|
showRecent = false
|
||||||
|
showRecentItems = 5
|
||||||
|
showMoreLink = false
|
||||||
|
showMoreLinkDest = "/posts"
|
||||||
|
cardView = false
|
||||||
|
cardViewScreenWidth = false
|
||||||
|
layoutBackgroundBlur = false # only used when layout equals background
|
||||||
|
|
||||||
|
[search]
|
||||||
|
enable = true
|
||||||
|
type = "fuse"
|
||||||
|
showPercent = true
|
||||||
|
showWidth = true
|
||||||
|
|
||||||
|
[article]
|
||||||
|
showDate = true
|
||||||
|
showViews = false
|
||||||
|
showLikes = false
|
||||||
|
showDateUpdated = true
|
||||||
|
showHero = true
|
||||||
|
heroStyle = "basic" # valid options: basic, big, background
|
||||||
|
showBreadcrumbs = true
|
||||||
|
showDraftLabel = true
|
||||||
|
showEdit = true
|
||||||
|
# editURL = "https://github.com/username/repo/"
|
||||||
|
editAppendPath = true
|
||||||
|
seriesOpened = true
|
||||||
|
showHeadingAnchors = true
|
||||||
|
showPagination = true
|
||||||
|
invertPagination = false
|
||||||
|
showReadingTime = true
|
||||||
|
showTableOfContents = true
|
||||||
|
showTaxonomies = true
|
||||||
|
showWordCount = true
|
||||||
|
showSummary = true
|
||||||
|
sharingLinks = [ "linkedin", "twitter", "whatsapp", "pinterest", "reddit", "facebook", "email"]
|
||||||
|
showAuthor = true
|
||||||
|
showAuthorsBadges = true # This helps style the author profile
|
||||||
|
|
||||||
|
[list]
|
||||||
|
showBreadcrumbs = true
|
||||||
|
showSummary = true
|
||||||
|
showTableOfContents = true
|
||||||
|
showCards = true
|
||||||
|
groupByYear = true
|
||||||
|
cardView = true
|
||||||
|
cardViewScreenWidth = false
|
||||||
|
columnCount = 3
|
||||||
|
showReadMore = true
|
||||||
|
|
||||||
|
[sitemap]
|
||||||
|
excludedKinds = []
|
||||||
|
|
||||||
|
[taxonomy]
|
||||||
|
showTermCount = true
|
||||||
|
|
||||||
|
[firebase]
|
||||||
|
# apiKey = "XXXXXX"
|
||||||
|
# authDomain = "XXXXXX"
|
||||||
|
# projectId = "XXXXXX"
|
||||||
|
# storageBucket = "XXXXXX"
|
||||||
|
# messagingSenderId = "XXXXXX"
|
||||||
|
# appId = "XXXXXX"
|
||||||
|
# measurementId = "XXXXXX"
|
||||||
|
|
||||||
|
[fathomAnalytics]
|
||||||
|
# site = "ABC12345"
|
||||||
|
# domain = "llama.yoursite.com"
|
||||||
|
|
||||||
|
[verification]
|
||||||
|
# google = ""
|
||||||
|
# bing = ""
|
||||||
|
# pinterest = ""
|
||||||
|
# yandex = ""
|
||||||
|
|
||||||
|
[app]
|
||||||
|
# Some versions of Blowfish look for 'mainIcon' or 'themeIcon'
|
||||||
|
# Try both if one fails.
|
||||||
|
mainIcon = "icons/icon.svg"
|
||||||
|
themeIcon = "icons/icon.svg"
|
||||||
16
content/_index.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
description: This is just the beginning...
|
||||||
|
title: HOME
|
||||||
|
date: 2023-06-14T09:37:13.298Z
|
||||||
|
lastmod: 2025-01-02
|
||||||
|
tags:
|
||||||
|
author: wompmacho
|
||||||
|
---
|
||||||
|
|
||||||
|
# Hi there 👋
|
||||||
|
|
||||||
|
The goal of this site is to document my home apps, services, infrastructure and other projects I am working on. Additionally I am using it as a learning tool, to document things I already know, fill in some gaps, add to that knowledge and further refine my understanding on complicated topics. I find its much easier to master something if you are forced to explain it to someone else. With that spirit in mind, lemme see what I can float your way.
|
||||||
|
|
||||||
|
> [!Success] Favorite Quote
|
||||||
|
> Never attribute to malice what can be explained by incompetence
|
||||||
|
|
||||||
5
content/posts/_index.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
date: '2025-12-31T18:07:42Z'
|
||||||
|
draft: true
|
||||||
|
title: 'Posts'
|
||||||
|
---
|
||||||
11
content/posts/firstpost.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
date: 2025-02-02
|
||||||
|
lastmod: 2026-01-06
|
||||||
|
author: "wompmacho"
|
||||||
|
authors: ["wompmacho"]
|
||||||
|
title: First Post
|
||||||
|
---
|
||||||
|
|
||||||
|
Yo 👋
|
||||||
|
|
||||||
|
If you actually come to find this then props to ya man. Thanks for dropping by. idk if this will be worth ever doing, but this was fun for me to set up... and who knows maybe this is of some use to others... so fuck it --> Enjoy.
|
||||||
BIN
content/posts/liberal/RDT_20250206_005732.mp4
Normal file
BIN
content/posts/liberal/Screenshot_20250205-234147.png
Normal file
|
After Width: | Height: | Size: 297 KiB |
20
content/posts/liberal/index.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
date: 2026-03-08
|
||||||
|
lastmod: 2026-03-08
|
||||||
|
author: "wompmacho"
|
||||||
|
authors: ["wompmacho"]
|
||||||
|
title: Liberal
|
||||||
|
showHero: false # needed to hide "hero banner"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Liberal is bad.
|
||||||
|
|
||||||
|
Wait, wasn't this group touting to be "liberal" once? Tell me more about why Liberal is bad. You know what… that person looks funny and bothers me and calls themself a liberal so I don’t wanna be in that catagory anymore. No wait, actually lets just demonize a group because fox news says so. NO WAIT, let's not use words correctly because words, logic, facts... These things don't matter.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## quit being a fucking sheep
|
||||||
|
|
||||||
|
this sums up my thoughts on the matter
|
||||||
|
|
||||||
|
{{< video src="RDT_20250206_005732.mp4" >}}
|
||||||
27
content/posts/performance_reviews.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
title: Performance Reviews
|
||||||
|
description: Performance Reviews are dumb
|
||||||
|
date: 2025-02-08
|
||||||
|
lastmod: 2026-01-06
|
||||||
|
author: "wompmacho"
|
||||||
|
authors: ["wompmacho"]
|
||||||
|
---
|
||||||
|
|
||||||
|
The Hidden Flaw of Performance Reviews.
|
||||||
|
|
||||||
|
<!-- more -->
|
||||||
|
|
||||||
|
> [!quote] Goodhart's law
|
||||||
|
> [Goodhart's law](https://en.wikipedia.org/wiki/Goodhart%27s_law) is an adage often stated as, "When a measure becomes a target, it ceases to be a good measure". It is named after British economist Charles Goodhart, who is credited with expressing the core idea of the adage in a 1975 article on monetary policy in the United Kingdom:
|
||||||
|
>
|
||||||
|
> > Any observed statistical regularity will tend to collapse once pressure is placed upon it for control purposes.
|
||||||
|
>
|
||||||
|
> It was used to criticize the British Thatcher government for trying to conduct monetary policy on the basis of targets for broad and narrow money, but the law reflects a much more general phenomenon.
|
||||||
|
|
||||||
|
{{< rawhtml >}}
|
||||||
|
<div class="iframe-wrapper" style="display: flex; flex-direction: row; align-items: center; justify-content: center;">
|
||||||
|
<iframe width="" height="" src="https://www.youtube.com/embed/XQT8_SAwwUY?si=prH2i9-fEOHSyg2V" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
|
||||||
|
</div>
|
||||||
|
{{< /rawhtml >}}
|
||||||
|
|
||||||
|
In other words, when we use a measure to reward performance, we provide an incentive to manipulate the measure in order to receive the reward. This can sometimes result in actions that actually reduce the effectiveness of the measured system while paradoxically improving the measurement of system performance.
|
||||||
91
content/projects/Docker/index.md
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
---
|
||||||
|
title: Docker
|
||||||
|
description: Quick overview of docker and setup
|
||||||
|
date: 2023-11-26T01:14:53.675Z
|
||||||
|
lastmod: 2025-02-11
|
||||||
|
author: wompmacho
|
||||||
|
showHero: false # needed to hide "hero banner"
|
||||||
|
---
|
||||||
|
|
||||||
|
## What is Docker?
|
||||||
|
|
||||||
|
Docker is an awesome platform that anyone hoping to get into software /
|
||||||
|
development or any homelab-er should become familiar with. Docker is a platform
|
||||||
|
designed to help developers build, share, and run container applications. The
|
||||||
|
most important aspect to docker is its ability to be implemented in version
|
||||||
|
control via simple config files. This allowing a team of people to share a code
|
||||||
|
base working in the same environments consistently.
|
||||||
|
|
||||||
|
Almost anything can be deployed as a service via docker. It is a fantastic tool
|
||||||
|
to learn about apps, software, test operating systems, do things like home
|
||||||
|
automation, run web servers, media servers, host your own proxy/reverse proxy,
|
||||||
|
email, dns, network monitoring, websites etc.
|
||||||
|
|
||||||
|
{{< rawhtml >}}
|
||||||
|
<div class="iframe-wrapper" style="display: flex; flex-direction: row; align-items: center; justify-content: center;">
|
||||||
|
<iframe width="" height="" src="https://www.youtube.com/embed/NPguawVjbN0?si=rCFmobnPOrK9yl--" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
|
||||||
|
</div>
|
||||||
|
{{< /rawhtml >}}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Docker Environment Setup
|
||||||
|
|
||||||
|
I am doing things with Ubuntu, so for my case I will follow this [docker.com -
|
||||||
|
GUIDE](https://docs.docker.com/engine/install/ubuntu/) for setting up the
|
||||||
|
initial docker environment on my ubuntu machine and then running the test
|
||||||
|
hello-world docker container app to verify that my docker environment is
|
||||||
|
working.
|
||||||
|
|
||||||
|
- Set up Docker's apt repository.
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
# Add Docker's official GPG key:
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install ca-certificates curl gnupg
|
||||||
|
sudo install -m 0755 -d /etc/apt/keyrings
|
||||||
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||||
|
sudo chmod a+r /etc/apt/keyrings/docker.gpg
|
||||||
|
|
||||||
|
# Add the repository to Apt sources:
|
||||||
|
echo \
|
||||||
|
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
|
||||||
|
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
|
||||||
|
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||||
|
sudo apt-get update
|
||||||
|
```
|
||||||
|
|
||||||
|
- Install the Docker packages.
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||||
|
```
|
||||||
|
|
||||||
|
- Verify that the Docker Engine installation is successful by running the
|
||||||
|
hello-world image.
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
sudo docker run hello-world
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Docker Compose
|
||||||
|
|
||||||
|
## .env Variables
|
||||||
|
|
||||||
|
## Docker Files
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> [dockerfile](https://docs.docker.com/reference/dockerfile/)
|
||||||
|
>
|
||||||
|
> Example rebuild for mkdocs with some mods
|
||||||
|
>
|
||||||
|
> ```
|
||||||
|
> FROM squidfunk/mkdocs-material
|
||||||
|
> RUN pip install mkdocs-macros-plugin
|
||||||
|
> RUN pip install mkdocs-glightbox
|
||||||
|
> ```
|
||||||
|
|
||||||
|
## Mounting remote storage
|
||||||
|
|
||||||
BIN
content/projects/NAS/adding_smb_windows.png
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
content/projects/NAS/creating_a_cronjob_with_truenas.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
292
content/projects/NAS/index.md
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
---
|
||||||
|
title: NAS
|
||||||
|
description: NAS
|
||||||
|
date: 2024-05-04
|
||||||
|
lastmod: 2024-05-11
|
||||||
|
author: wompmacho
|
||||||
|
summary: "NAS build and some tips and tricks to get things working with your docker containers"
|
||||||
|
showTableOfContents: true
|
||||||
|
showHero: false # needed to hide "hero banner"
|
||||||
|
---
|
||||||
|
|
||||||
|
## What is a NAS?
|
||||||
|
|
||||||
|
A Network Attached Storage (NAS) device is essentially a small, self-contained
|
||||||
|
computer that's designed solely for storing and sharing files. Think of it as
|
||||||
|
your own personal cloud storage, but instead of relying on a third-party
|
||||||
|
service, you own and control the hardware.
|
||||||
|
|
||||||
|
Here's why someone might use a NAS:
|
||||||
|
|
||||||
|
- **Centralized Storage:** A NAS provides a single location to store all your
|
||||||
|
files - documents, photos, videos, music, etc. This makes it easy to access
|
||||||
|
your data from any device on your network.
|
||||||
|
- **File Sharing:** NAS devices make it simple to share files between multiple
|
||||||
|
users and devices. This is great for families who want to share photos and
|
||||||
|
videos, or for small businesses who need to collaborate on documents.
|
||||||
|
- **Backup and Redundancy:** Many NAS devices offer features like automatic
|
||||||
|
backups and RAID configurations, which help protect your data from hard drive
|
||||||
|
failures.
|
||||||
|
- **Media Streaming:** NAS devices can be used to stream media files (movies,
|
||||||
|
music) to devices throughout your home, like smart TVs, game consoles, and
|
||||||
|
mobile devices.
|
||||||
|
- **Remote Access:** Some NAS devices allow you to access your files remotely
|
||||||
|
over the internet, so you can retrieve important documents or share photos
|
||||||
|
even when you're away from home.
|
||||||
|
|
||||||
|
Essentially, a NAS is a versatile and convenient way to manage and share your
|
||||||
|
digital data. It offers more control and privacy than cloud storage services,
|
||||||
|
and it can be a valuable tool for both individuals and businesses.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## TrueNAS
|
||||||
|
|
||||||
|
TrueNAS is an Open Source NAS operating System / infrastructure solution. In
|
||||||
|
addition to powerful scale-out storage capabilities, TrueNAS SCALE adds Linux
|
||||||
|
Containers and VMs (KVM) so your organization can run workloads closer to data.
|
||||||
|
|
||||||
|
### Why I switched
|
||||||
|
|
||||||
|
Recently I switched over to TrueNas from my off the shelf
|
||||||
|
[Terramaster](https://shop.terra-master.com/products/terramaster-f5-422-10gbe-nas-5-bay-network-storage-server-intel-quad-core-cpu-with-hardware-encryption-diskless)
|
||||||
|
device. I actually really liked the Terramaster, it allowed a 5 drive pool with
|
||||||
|
raid 1 on a BTRFS filesystem. Which meant it was easy to upgrade the drives from
|
||||||
|
2TB --> 6TB giving me a decent ~24TB size pool *(one drive as parity)*. I got
|
||||||
|
this originally so that I could safely back up my data and store my 10TB+ of VOD
|
||||||
|
recordings from the Live Stream & Youtube. The Terramaster had some pretty big
|
||||||
|
drawbacks. It was only really good for being a simple NAS share.
|
||||||
|
|
||||||
|
The proprietary operating System is actual hot garbage.
|
||||||
|
|
||||||
|
- the GUI is extremely slow and freezes up a lot
|
||||||
|
- the built in docker containers and other special features rarely work
|
||||||
|
- the Recycle bin is hot garbage and runs even when you turn it off (discovered
|
||||||
|
nothing had **EVER** been deleted)
|
||||||
|
- the underlying linux OS somehow struggles to do basic things like deleting
|
||||||
|
files
|
||||||
|
- networking sometimes just broke, ignored static IPs and would ignore DNS due
|
||||||
|
to not properly turning off ipv6
|
||||||
|
- there is little to no documentation or support outside of Terramaster official
|
||||||
|
forums, which is also hot garbage.
|
||||||
|
|
||||||
|
Couple years later my data has continued to grow, including my
|
||||||
|
[jellyfin](../homelab/containers/jellyfin.md) media and other hoarding, so I
|
||||||
|
needed some space. This gave me a nice opertunity to upgrade. I have an older,
|
||||||
|
but still nice PC sitting around as a spare, so this was a good chance to
|
||||||
|
upgrade my NAS with some nice compute as well.
|
||||||
|
|
||||||
|
### Why TrueNAS
|
||||||
|
|
||||||
|
I went with TrueNAS SCALE because it used the newer [ZFS2
|
||||||
|
filesystem](https://github.com/openzfs/zfs/releases) which allows for expansion
|
||||||
|
of pools. This would allow me to buy some extra drives, move over my data and
|
||||||
|
then expand using the old drive pool. SCALE also moved over to docker
|
||||||
|
containerization. Side benefit of allowing me to host some extra containers if I
|
||||||
|
want. Its also free and there is a lot of support / documentation out there. It
|
||||||
|
has come a long way from the FreeBSD days.
|
||||||
|
|
||||||
|
### Refurbished Drives
|
||||||
|
|
||||||
|
I had some issues when getting sourcing drives. Things are still pretty
|
||||||
|
expensive atm, so I went with just getting more 6TB and expanding the pool. *Can
|
||||||
|
upgrade size later when the prices chill out*. Managed to find a good price on
|
||||||
|
refurbished 6TB drives from amazon. **However**; when they arrived I found that
|
||||||
|
they were all heavily used 4 years+ uptime, reused from some Datacenter
|
||||||
|
somewhere. **Fucking scummy Amazon seller**. To top it off, some were SAS drives
|
||||||
|
out of NETAPP appliances.
|
||||||
|
|
||||||
|
> [!ERROR] **Fuck you Netapp**
|
||||||
|
>
|
||||||
|
> Netapp is an older *shit* brand that would lock down their drives with special
|
||||||
|
> formatting that forced the customer to use only drives sourced from Netapp.
|
||||||
|
> These old Netapp appliances are starting to flood the market as newer /
|
||||||
|
> cheaper to run hardware is being deployed.
|
||||||
|
|
||||||
|
> [!SUCCESS]
|
||||||
|
> Luckily, smart people can reformat the drives from their shit Data Integrity
|
||||||
|
> Feature (DIF) format back to a normal. This is a long and time consuming process
|
||||||
|
> *(Took DAYS)* as the entire drive has to be reformatted with a normal 512 chunk
|
||||||
|
> size.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Thank you [smart guy from reddit](https://reddit.com/r/truenas/comments/12w68uc/how_to_get_rid_of_data_integrity_feature_from/) that pointed me to the [smart guy on TrueNAS forum](https://www.truenas.com/community/threads/troubleshooting-disk-format-warnings-in-truenas-scale.106051/) that showed me how to fix these un-usable drives.
|
||||||
|
>
|
||||||
|
> TrueNAS has `sg_format` built in. With this you can reformat all the drives at the **same time**.
|
||||||
|
>
|
||||||
|
> ``` shell
|
||||||
|
> # formatting
|
||||||
|
> sg_format --format --size=512 /dev/sdb
|
||||||
|
>
|
||||||
|
> # progress
|
||||||
|
> sudo sg_turs --progress /dev/sdb
|
||||||
|
> ```
|
||||||
|
|
||||||
|
> [!warning]
|
||||||
|
> This still took multiple days with a 6TB drive :(
|
||||||
|
|
||||||
|
### Safely copying files
|
||||||
|
|
||||||
|
One problem I ran into was; how do I make sure everything is copied over safely from one pool to another? I could drag and drop folders, but that would have taken months and risked missing data. Best bet was to use `rsync` *(which is also the fastest way to transfer)*. `rsync` has the added benefit of using checksum checking to verify all data is safely transferred with no errors. Luckily both systems were on linux, which made this easier.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> I started by doing this after logging into my old Terramaster NAS and performing the `rsync` operation from there. This was a bad idea and too longer, because the OS is slow and CPU can not handle handling all this plus 10GB networking at once. If you do this, do this from a system with a decent CPU.
|
||||||
|
|
||||||
|
- Mount your systems together via the device with the best CPU
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
#mount in fstab
|
||||||
|
# <file system> <dir> <type> <options> <dump> <pass>
|
||||||
|
nas:/mnt/md0/VODS /mnt/tnas/vods nfs defaults 0 0
|
||||||
|
```
|
||||||
|
|
||||||
|
- Run `rsync` in the shell and move your folders using recursive options
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
# Coping folders recursively with Progress & Stats
|
||||||
|
sudo rsync -avh -A --no-perms --progress --stats /mnt/tnas/store/Backups/ /mnt/store/vault/Backups/ &
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> `rsync` keeps logs and will run faster the next time around. Recommend running it a few times to add a extra verification that all your files have transferred.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> You can use `--progress --stats` and the `&` operator to send the job to the background. This will alow you to bring the job to the foreground whenever you want to check on progress. This is super useful when transferring terabytes of data.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> If doing this from TrueNAS, might be better to set this up as a one time cronjob. TrueNAS might kill this job if you lose connection to the shell while transferring.
|
||||||
|
> - add the job using the user interface *(do not enable the job)*
|
||||||
|
>
|
||||||
|
> 
|
||||||
|
>
|
||||||
|
> - run the job when you are ready to move files
|
||||||
|
>
|
||||||
|
> 
|
||||||
|
|
||||||
|
### How to connect to a NAS
|
||||||
|
|
||||||
|
#### CIFS
|
||||||
|
|
||||||
|
Common Internet File System (CIFS) is a network file sharing protocol that
|
||||||
|
allows applications on computers to read and write files and request other
|
||||||
|
services from remote servers. Think of it as a way for your computer to talk to
|
||||||
|
another computer (or storage device) to access files. It's most commonly
|
||||||
|
associated with Windows environments, but it's used by other operating systems
|
||||||
|
as well. It is relatively secure, requiring username / password login to remote
|
||||||
|
systems.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> You might need this if you want to connect a Windows machine to one
|
||||||
|
running linux like a common NAS *(my use case)*.
|
||||||
|
|
||||||
|
One example of a use case is a jellyfin container that needs persistent data
|
||||||
|
access for media (movies / tv shows) served from your nas. This will need this
|
||||||
|
to be mounted to the OS docker is running on and pass this through with the
|
||||||
|
volumes option in your docker compose file.
|
||||||
|
|
||||||
|
**To add CIFS to Linux**
|
||||||
|
|
||||||
|
For this you will also need the `keyutils` & `cifs-utils` packages. The `keyutils`
|
||||||
|
package is a library and a set of utilities for accessing the kernel keyring
|
||||||
|
facility. `cifs-utils` package provides a means for mounting SMB/CIFS shares on a
|
||||||
|
Linux system.
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
apt-get install keyutils && apt install cifs-utils -y
|
||||||
|
```
|
||||||
|
|
||||||
|
Then we will need to mount the remote storage via fstab so that it will
|
||||||
|
automatically mount to the OS every time the os boots.
|
||||||
|
|
||||||
|
- create a file in your home directory "~/.smb"
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
vim ~/.smb
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!INFO]
|
||||||
|
> The file should contain your NAS credentials *(domain optional/depends
|
||||||
|
on your nas settings)*
|
||||||
|
>
|
||||||
|
> ``` bash
|
||||||
|
> username=NAS_USERNAME
|
||||||
|
> password=NAS_PASSWORD
|
||||||
|
> domain=NAS_DOMAIN_GROUP
|
||||||
|
> ```
|
||||||
|
|
||||||
|
- Create an entry in the fstab
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
vim /etc/fstab
|
||||||
|
```
|
||||||
|
|
||||||
|
- Add an entry to the bottom line of the file
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
# //{Nas_IP/Hostname}/{Nas_Mount_Point} /mnt/{mount_name_on_docker_os} cifs credentials=/[path_to_credentials].smb,x-systemd.automount 0 0
|
||||||
|
|
||||||
|
# Example:
|
||||||
|
//nas.home/store /mnt/store cifs credentials=/home/wompmacho/.smb,x-systemd.automount 0 0
|
||||||
|
```
|
||||||
|
|
||||||
|
- Save your file and re-mount all
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
mount -a
|
||||||
|
```
|
||||||
|
|
||||||
|
- make sure your mount section of your docker compose matches the
|
||||||
|
`mount_name_on_docker_os` and reboot your system
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
# example:
|
||||||
|
volumes:
|
||||||
|
- /app/jellyfin/config:/config
|
||||||
|
- /mnt/store:/data/store
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!SUCCESS]
|
||||||
|
>
|
||||||
|
> You can check that they are mounted by navigating to where you mounted the files
|
||||||
|
>
|
||||||
|
> ``` bash
|
||||||
|
> wompmacho@docker:~$ cd /mnt/store/MediaServer/
|
||||||
|
> Movies/ Music/ Torrent/ Tv Shows/
|
||||||
|
> ```
|
||||||
|
|
||||||
|
#### NFS
|
||||||
|
|
||||||
|
NFS (Network File System) is a distributed file system protocol that allows users to access files and directories over a network as if they were located on their local computer. It's a way for your computer to talk to another computer (or storage device) to access files, similar to CIFS, but more commonly used in Unix/Linux environments.
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> There is `NO SECURITY` on NFS. It uses existing ACL groups to manage permissions. Only use this on a local network and for trusted devices.
|
||||||
|
|
||||||
|
> [!SUCCESS]
|
||||||
|
> On TrueNAS you can limit access to on IP address or limit within your local domain.
|
||||||
|
>
|
||||||
|
> 
|
||||||
|
|
||||||
|
> [!Info]
|
||||||
|
> One thing to consider when working with TrueNas:
|
||||||
|
> - When creating the initial dataset in your pool, set the zfs **aclmode** on the dataset in question to `passthrough`.
|
||||||
|
> - Special thanks to `anodos` you solved am issue plaguing me --> [Truenas Forum](https://www.truenas.com/community/threads/cannot-chmod-nfs-operation-not-permitted.97247/)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### SMB
|
||||||
|
|
||||||
|
Server Message Block (SMB) is a network communication protocol that allows computers to share files, printers, and other resources with each other. It's the foundation of file sharing in **Windows** environments, but it's also used by other operating systems like macOS and Linux.
|
||||||
|
|
||||||
|
**To connect to a SMB share on Windows:**
|
||||||
|
|
||||||
|
- Right Click to add a `Network Location`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- Use the IP address or hostname of the NAS and the share path provided to your folder access
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> For windows you will need to enter a username / password for access to the share
|
||||||
|
|
||||||
BIN
content/projects/NAS/running_cron_job.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
content/projects/NAS/selecting_network_path_for_smb.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
content/projects/NAS/truenas_nfs_limit_to_local_network.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
245
content/projects/Networking/index.md
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
---
|
||||||
|
title: networking
|
||||||
|
description: DHCP, DNS, PROXY
|
||||||
|
author: wompmacho
|
||||||
|
date: 2024-04-27T23:53:26.059Z
|
||||||
|
lastmod: 2025-02-08
|
||||||
|
editor: markdown
|
||||||
|
---
|
||||||
|
|
||||||
|
## IP Address
|
||||||
|
|
||||||
|
An Internet Protocol address (IP) address is a numerical label assigned to each
|
||||||
|
device connected to a computer network that uses the Internet Protocol for
|
||||||
|
communication. Think of it like a street address for your computer on the
|
||||||
|
internet. It's how devices find each other and exchange information.
|
||||||
|
|
||||||
|
Here's a breakdown:
|
||||||
|
|
||||||
|
* **Numerical Identifier:** An IP address is a set of numbers, typically
|
||||||
|
represented in dotted decimal notation (e.g., 192.168.1.1). There are two
|
||||||
|
main versions: IPv4 (the older version) and IPv6 (the newer version, which
|
||||||
|
uses a different format to accommodate more addresses).
|
||||||
|
* **Device Identification:** Every device that connects to a network (computers,
|
||||||
|
smartphones, tablets, servers, etc.) needs a unique IP address to be
|
||||||
|
identified and communicate.
|
||||||
|
* **Location Information:** While not precise, parts of an IP address can
|
||||||
|
provide some general information about the device's location.
|
||||||
|
* **Routing:** IP addresses are used by routers to direct network traffic to the
|
||||||
|
correct destination. When you send data over the internet, routers use IP
|
||||||
|
addresses to figure out where to send it.
|
||||||
|
|
||||||
|
In short, an IP address is a crucial element of networking. It's the unique
|
||||||
|
identifier that allows devices to communicate with each other over a network,
|
||||||
|
whether it's a local network or the vast expanse of the internet.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## IPv4 & IPv6
|
||||||
|
|
||||||
|
IPv4 and IPv6 are two versions of the Internet Protocol (IP), which is the
|
||||||
|
fundamental protocol that enables devices to communicate over the internet.
|
||||||
|
They are essentially addressing systems that allow devices to be uniquely
|
||||||
|
identified and located on a network.
|
||||||
|
|
||||||
|
Here's a breakdown:
|
||||||
|
|
||||||
|
* **IPv4 (Internet Protocol version 4):** This is the original version of IP,
|
||||||
|
using 32-bit addresses represented in dotted decimal notation (e.g.,
|
||||||
|
192.168.1.1). It offers roughly 4.3 billion unique addresses. Due to the
|
||||||
|
explosive growth of the internet, IPv4 addresses are now largely exhausted.
|
||||||
|
|
||||||
|
* **IPv6 (Internet Protocol version 6):** This is the newer version of IP,
|
||||||
|
designed to address the limitations of IPv4. It uses 128-bit addresses
|
||||||
|
represented in hexadecimal notation (e.g.,
|
||||||
|
2001:0db8:85a3:0000:0000:8a2e:0370:7334). IPv6 offers a vastly larger address
|
||||||
|
space, virtually eliminating the problem of address exhaustion.
|
||||||
|
|
||||||
|
Key Differences and Why IPv6 is Needed:
|
||||||
|
|
||||||
|
* **Address Space:** IPv4 has a limited number of addresses, while IPv6 offers a
|
||||||
|
practically unlimited number.
|
||||||
|
* **Address Format:** IPv4 uses dotted decimal notation, while IPv6 uses
|
||||||
|
hexadecimal notation.
|
||||||
|
* **Automatic Configuration:** IPv6 supports more advanced automatic
|
||||||
|
configuration features, simplifying network management.
|
||||||
|
* **Security:** IPv6 includes built-in security features, such as IPSec, which
|
||||||
|
enhances network security.
|
||||||
|
|
||||||
|
In short, IPv4 is the older, widely used addressing system that is now facing
|
||||||
|
address exhaustion. IPv6 is the newer, more robust addressing system designed to
|
||||||
|
replace IPv4 and provide the necessary address space for the continued growth of
|
||||||
|
the internet. The transition to IPv6 is ongoing.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DHCP
|
||||||
|
|
||||||
|
Dynamic Host Configuration Protocol (DHCP) is a network management protocol that
|
||||||
|
automates the process of assigning IP addresses and other network configuration
|
||||||
|
parameters to devices on a network. Think of it as a way to automatically give
|
||||||
|
each device on your network its own "address" so it can communicate with other
|
||||||
|
devices and the internet.
|
||||||
|
|
||||||
|
Here's a breakdown:
|
||||||
|
|
||||||
|
* **Automatic IP Assignment:** DHCP eliminates the need to manually configure IP
|
||||||
|
addresses for each device on a network. This is especially useful in large
|
||||||
|
networks where it would be tedious to assign addresses manually.
|
||||||
|
* **Lease-Based System:** DHCP uses a "lease" system, where IP addresses are
|
||||||
|
assigned to devices for a specific period of time. When the lease expires, the
|
||||||
|
device must renew it or the IP address becomes available for other devices.
|
||||||
|
This helps ensure that IP addresses are used efficiently.
|
||||||
|
* **Centralized Management:** DHCP allows network administrators to manage IP
|
||||||
|
addresses from a central server. This simplifies network administration and
|
||||||
|
makes it easier to track which devices have which IP addresses.
|
||||||
|
* **Other Configuration Parameters:** In addition to IP addresses, DHCP can also
|
||||||
|
provide other network configuration parameters, such as subnet mask, default
|
||||||
|
gateway, and DNS server addresses.
|
||||||
|
|
||||||
|
Why someone might use DHCP:
|
||||||
|
|
||||||
|
* **Simplified Network Administration:** DHCP makes it much easier to manage IP
|
||||||
|
addresses in a network, especially in large networks.
|
||||||
|
* **Reduced Configuration Errors:** Manual IP address configuration can lead to
|
||||||
|
errors, such as duplicate IP addresses, which can cause network conflicts.
|
||||||
|
DHCP helps prevent these errors.
|
||||||
|
* **Efficient IP Address Usage:** The lease-based system ensures that IP
|
||||||
|
addresses are used efficiently and that addresses that are no longer in use
|
||||||
|
are reclaimed.
|
||||||
|
* **Plug-and-Play Networking:** DHCP allows devices to connect to a network and
|
||||||
|
automatically receive the necessary network configuration, making it easier to
|
||||||
|
add new devices to the network.
|
||||||
|
|
||||||
|
In short, DHCP is a valuable tool for network administrators that simplifies IP
|
||||||
|
address management and makes networks more efficient and reliable.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Static IP
|
||||||
|
|
||||||
|
A static IP address is a manually assigned IP address that remains constant for
|
||||||
|
a specific device on a network. Unlike a dynamic IP address (assigned by DHCP),
|
||||||
|
a static IP doesn't change. This makes it useful for devices that need a
|
||||||
|
consistent and predictable address, such as servers, printers, or network
|
||||||
|
devices. However, it requires manual configuration and careful management to
|
||||||
|
avoid IP address conflicts.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DNS
|
||||||
|
|
||||||
|
Domain Name System (DNS) is essentially the phone book of the internet. It
|
||||||
|
translates human-readable domain names (like **google.com**) into the numerical
|
||||||
|
IP addresses (like 172.217.160.142) that computers use to communicate with each
|
||||||
|
other.
|
||||||
|
|
||||||
|
Here's a breakdown:
|
||||||
|
|
||||||
|
* **Human-Friendly to Machine-Friendly:** We remember names like "google.com"
|
||||||
|
easily, but computers communicate using IP addresses. DNS bridges this gap by
|
||||||
|
converting domain names into their corresponding IP addresses.
|
||||||
|
* **Distributed Database:** DNS is a massive, distributed database. It's not
|
||||||
|
stored in one single location, but rather spread across a network of servers
|
||||||
|
around the world. This makes it robust and efficient.
|
||||||
|
* **Hierarchical Structure:** DNS is organized in a hierarchical structure, like
|
||||||
|
a tree. This structure helps to manage the vast number of domain names and IP
|
||||||
|
addresses.
|
||||||
|
* **Resolution Process:** When you type a domain name into your browser, your
|
||||||
|
computer initiates a DNS resolution process. It queries various DNS servers to
|
||||||
|
find the IP address associated with that domain name.
|
||||||
|
|
||||||
|
Why someone might use DNS:
|
||||||
|
|
||||||
|
* **Easy Access to Websites:** DNS allows us to access websites by using
|
||||||
|
easy-to-remember domain names instead of complex IP addresses.
|
||||||
|
* **Email Delivery:** DNS is also used to route email to the correct mail
|
||||||
|
servers.
|
||||||
|
* **Internet Functionality:** DNS is a fundamental component of the internet,
|
||||||
|
without which we wouldn't be able to easily browse the web or send emails.
|
||||||
|
|
||||||
|
In short, DNS is a critical part of the internet infrastructure. It's the system
|
||||||
|
that allows us to use domain names to access websites and other internet
|
||||||
|
resources, making the internet user-friendly and accessible.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PROXY
|
||||||
|
|
||||||
|
A proxy acts as an intermediary between a client (like your computer) and a
|
||||||
|
server (like a website). Instead of your computer directly connecting to the
|
||||||
|
server, it connects to the proxy server, which then forwards the request to the
|
||||||
|
server. The server's response comes back to the proxy, which then forwards it to
|
||||||
|
your computer. Think of it like a middleman.
|
||||||
|
|
||||||
|
Here's a breakdown:
|
||||||
|
|
||||||
|
* **Intermediary:** The core function of a proxy is to act as a go-between for
|
||||||
|
client and server.
|
||||||
|
* **Hiding IP Address:** One common use of a proxy is to mask the client's IP
|
||||||
|
address. The server sees the proxy's IP address, not the client's, providing a
|
||||||
|
degree of anonymity.
|
||||||
|
* **Caching:** Proxies often cache frequently accessed content. If a client
|
||||||
|
requests something that's already in the cache, the proxy can serve it
|
||||||
|
directly, speeding up access.
|
||||||
|
* **Filtering and Security:** Proxies can be used to filter content, block
|
||||||
|
access to certain websites, or scan for malware. This is common in corporate
|
||||||
|
environments.
|
||||||
|
* **Load Balancing:** In some situations, proxies can distribute traffic across
|
||||||
|
multiple servers, helping to balance the load and improve performance.
|
||||||
|
|
||||||
|
In short, a proxy server provides a layer of separation between clients and
|
||||||
|
servers, offering a variety of benefits related to privacy, security,
|
||||||
|
performance, and network management.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Reverse Proxy
|
||||||
|
|
||||||
|
A reverse proxy sits in front of one or more backend servers, intercepting
|
||||||
|
client requests and forwarding them to the appropriate server. It acts as a
|
||||||
|
gateway, but unlike a regular proxy (which protects clients), a reverse proxy
|
||||||
|
protects the servers. Clients connect to the reverse proxy, which then handles
|
||||||
|
the connection to the actual servers.
|
||||||
|
|
||||||
|
Here's a breakdown:
|
||||||
|
|
||||||
|
* **Server Protection:** Reverse proxies shield backend servers from direct
|
||||||
|
exposure to the internet, enhancing security by preventing direct attacks.
|
||||||
|
* **Load Balancing:** They can distribute client traffic across multiple
|
||||||
|
servers, preventing any single server from becoming overloaded.
|
||||||
|
* **Caching:** Reverse proxies can cache content, reducing the load on backend
|
||||||
|
servers and speeding up response times for clients.
|
||||||
|
* **SSL Termination:** They can handle SSL encryption and decryption, offloading
|
||||||
|
this task from the backend servers.
|
||||||
|
* **URL Rewriting:** Reverse proxies can modify URLs, making them more
|
||||||
|
user-friendly or hiding the internal structure of the backend servers.
|
||||||
|
|
||||||
|
In short, a reverse proxy acts as a gatekeeper for backend servers, providing a
|
||||||
|
range of benefits related to security, performance, scalability, and
|
||||||
|
flexibility. It's a common component in modern web architectures.
|
||||||
|
|
||||||
|
## SSL
|
||||||
|
|
||||||
|
Secure Sockets Layer (SSL) is a security protocol that creates an encrypted
|
||||||
|
connection between a web server and a web browser. This ensures that any data
|
||||||
|
exchanged between them remains private and secure. Think of it as a secret
|
||||||
|
tunnel that prevents eavesdropping and tampering.
|
||||||
|
|
||||||
|
Here's a breakdown:
|
||||||
|
|
||||||
|
* **Encryption:** SSL encrypts the data transmitted between the browser and the
|
||||||
|
server, making it unreadable to anyone who might try to intercept it. This
|
||||||
|
protects sensitive information like passwords, credit card numbers, and
|
||||||
|
personal details.
|
||||||
|
* **Authentication:** SSL verifies the identity of the website, assuring users
|
||||||
|
that they are connecting to the legitimate website and not a fake one. This
|
||||||
|
helps prevent phishing attacks.
|
||||||
|
* **Data Integrity:** SSL ensures that the data transmitted between the browser
|
||||||
|
and the server is not altered or corrupted during transit. This guarantees
|
||||||
|
that the information received is exactly what was sent.
|
||||||
|
|
||||||
|
In short, SSL is a crucial security technology that protects online
|
||||||
|
communication and helps build trust between websites and their users. It's the
|
||||||
|
foundation of secure online transactions and a vital component of a safe
|
||||||
|
internet experience.
|
||||||
108
content/projects/Pihole/index.md
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
---
|
||||||
|
title: pihole
|
||||||
|
description: pihole
|
||||||
|
author: wompmacho
|
||||||
|
date: 2024-04-27T23:53:26.059Z
|
||||||
|
lastmod: 2025-02-08
|
||||||
|
editor: markdown
|
||||||
|
showHero: false # needed to hide "hero banner"
|
||||||
|
---
|
||||||
|
|
||||||
|
The Pi-hole is a DNS sinkhole that protects your devices from unwanted content,
|
||||||
|
without installing any client-side software. Useful for blocking ad services at
|
||||||
|
a DNS level. It uses a list of known ad services stored on github, can add your
|
||||||
|
own. It can also operate as a internal dns router and dhcp server.
|
||||||
|
|
||||||
|
## Pihole Setup
|
||||||
|
|
||||||
|
If you have a raspberry-pi or another device, its super easy to get things
|
||||||
|
going.
|
||||||
|
|
||||||
|
- [pihole setup](https://github.com/pi-hole/pi-hole/?tab=readme-ov-file#one-step-automated-install).
|
||||||
|
|
||||||
|
Any debian based system should be able to get things going quickly. Then all you
|
||||||
|
need to do is set your devices to use your pihole as the **primary** dns server.
|
||||||
|
|
||||||
|
**Debian based one-step install**
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
curl -sSL https://install.pi-hole.net | bash
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> I find this to be a little flaky when it comes to DNS, often times OS will need reboots and cache to be cleared in order to actively start using pihole DNS.
|
||||||
|
>
|
||||||
|
> Browsers also store dns info so many things can conflict before your DNS
|
||||||
|
switches over. I find that using Pihole as primary DHCP server forces your
|
||||||
|
devices to use the correct DNS server and fixes a lot of problems.
|
||||||
|
>
|
||||||
|
> Also keep in mind that **ipv6** can interfere if you are like me and have a ISP
|
||||||
|
provider that tries to force their DNS
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Setup on Proxmox VM
|
||||||
|
|
||||||
|
My pihole is operating as a Debian GNU/Linux 12 (bookworm) virtual machine
|
||||||
|
running on Proxmox. I use it as a internal DNS router & DHCP server which makes
|
||||||
|
dns much easier in my case - due to my internet provider trying to force me to
|
||||||
|
use there dns servers. This setup is a little weird and in-order to get
|
||||||
|
everything to work a couple extra steps are need.
|
||||||
|
|
||||||
|
Will need to setup your pihole as a dhcp server, disable the existing dhcp
|
||||||
|
server on the router, reserve static ip addressed for proxmox and pihole so that
|
||||||
|
it can connect to the gateway, set the pihole as primary dns server on proxmox,
|
||||||
|
set proxmox to use dhcp rather than static ip and finally set pihole VM to
|
||||||
|
automatically boot first so that when it connects to the gateway devices
|
||||||
|
connected to the gateway are issued ip address from the pihole.
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> If you are using pihole for DHCP / DNS, keep in mind if the device goes down
|
||||||
|
that is hosting your pihole server, so will your DNS / DHCP. This May prevent
|
||||||
|
you from connecting to your network until you re-enable a dhcp server such as
|
||||||
|
the one in your router.
|
||||||
|
|
||||||
|
- reserve a ip address in router/gateway for proxmox server & pihole
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- set pihole to enable DHCP
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- set proxmox to get DHCP on boot rather than Static IP which is default
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
root@laptop-proxmox:~# cat /etc/network/interfaces
|
||||||
|
auto lo
|
||||||
|
iface lo inet loopback
|
||||||
|
|
||||||
|
iface enp3s0 inet manual
|
||||||
|
|
||||||
|
auto vmbr0
|
||||||
|
iface vmbr0 inet dhcp
|
||||||
|
bridge-ports enp3s0
|
||||||
|
bridge-stp off
|
||||||
|
bridge-fd 0
|
||||||
|
|
||||||
|
iface wlp4s0 inet manual
|
||||||
|
```
|
||||||
|
|
||||||
|
- set proxmox default DNS server to pihole reserved address
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- set pihole to automatically start on boot with highest priority boot order
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- set sattic ip and gateway info for pihole netwoking configuration '
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- Disable DHCP server in gateway / router settings
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- If router has option to set default DNS, set to pihole reserved address
|
||||||
BIN
content/projects/Pihole/pihole_boot_order.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
content/projects/Pihole/pihole_dhcp_example.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 28 KiB |
BIN
content/projects/Pihole/proxmox_dns_example.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
content/projects/Pihole/reserved_ip_example.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
BIN
content/projects/Pihole/router_disable_dhcp_example.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
72
content/projects/Pterodactyl/index.md
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
---
|
||||||
|
title: pterodactyl
|
||||||
|
description: pterodactyl
|
||||||
|
author: wompmacho
|
||||||
|
date: 2024-05-04T20:00:25.298Z
|
||||||
|
lastmod: 2025-02-08
|
||||||
|
showHero: false # needed to hide "hero banner"
|
||||||
|
---
|
||||||
|
|
||||||
|
## What is Pterodactyl?
|
||||||
|
|
||||||
|
[Pterodactyl](https://pterodactyl.io/) is a free, open-source game server management panel built with PHP,
|
||||||
|
React, and Go. Designed with security in mind, Pterodactyl runs all game servers
|
||||||
|
in isolated Docker containers while exposing a beautiful and intuitive UI to end
|
||||||
|
users.
|
||||||
|
|
||||||
|
## pterodactyl & ssl
|
||||||
|
|
||||||
|
ssl with pterodactyl is really annoying if you are using it behind a reverse
|
||||||
|
proxy (nginx) - might be easier to run this on its own server so you can just
|
||||||
|
use the default port 80 for web. reverse proxy is designed for normal web
|
||||||
|
traffic, not game servers.
|
||||||
|
|
||||||
|
If you are annoying like me and wanna put things on a single server and save
|
||||||
|
money... here is what you can do.
|
||||||
|
|
||||||
|
- [Creating SSL
|
||||||
|
Certificates](https://pterodactyl.io/tutorials/creating_ssl_certificates.html#method-2:-acme.sh-(using-cloudflare-api))
|
||||||
|
- [Youtube Guide](https://www.youtube.com/watch?v=cbr8tddvAWw)
|
||||||
|
- [Webserver
|
||||||
|
Configuration](https://pterodactyl.io/panel/1.0/webserver_configuration.html#nginx-with-ssl)
|
||||||
|
- [NGINX Specific
|
||||||
|
Configuration](https://pterodactyl.io/panel/1.0/additional_configuration.html#nginx-specific-configuration)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
https://pterodactyl.io/panel/0.7/configuration.html
|
||||||
|
# idk... couldn't get it to work
|
||||||
|
# OpenSSL Self-Signed Certificate Command:
|
||||||
|
openssl req -sha256 -addext "subjectAltName = DNS:games.local" -newkey rsa:4096 -nodes -keyout privkeyselfsigned.pem -x509 -days 3650 -out fullchainselfsigned.pem
|
||||||
|
|
||||||
|
|
||||||
|
# nginx-proxy-manager with cloudflare ssl cert setup
|
||||||
|
# proxy side should be http
|
||||||
|
# do not force ssl on cert side
|
||||||
|
# go to http after getting to the site
|
||||||
|
|
||||||
|
# .env file
|
||||||
|
/var/www/pterodactyl/.env
|
||||||
|
APP_URL="http://domain"
|
||||||
|
TRUSTED_PROXIES=*
|
||||||
|
# you don't have to do this - i'd rather not
|
||||||
|
PTERODACTYL_TELEMETRY_ENABLED=false
|
||||||
|
RECAPTCHA_ENABLED=false
|
||||||
|
|
||||||
|
# config.yml
|
||||||
|
/etc/pterodactyl/config.yml
|
||||||
|
# use auto config remote: http:
|
||||||
|
|
||||||
|
# nginx pterodactyl.conf
|
||||||
|
/etc/nginx/sites-enabled/pterodactyl.conf
|
||||||
|
|
||||||
|
# add to proxy-manager special settings
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_redirect off;
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_request_buffering off;
|
||||||
|
|
||||||
|
sudo systemctl restart nginx && systemctl restart wings
|
||||||
|
```
|
||||||
4
content/projects/_index.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
title: 'Projects'
|
||||||
|
layout: "card"
|
||||||
|
---
|
||||||
52
content/projects/audiobookshelf/index.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
---
|
||||||
|
title: audiobookshelf
|
||||||
|
description: Quick overview of audiobookshelf and setup
|
||||||
|
date: 2025-02-04
|
||||||
|
lastmod: 2025-02-04
|
||||||
|
author: wompmacho
|
||||||
|
showHero: false # needed to hide "hero banner"
|
||||||
|
---
|
||||||
|
|
||||||
|
## What is Audiobookshelf?
|
||||||
|
|
||||||
|
`Audiobookshelf` is a self-hosted, open-source server designed to manage and stream your personal audiobook and podcast collections. It acts as a private, web-based alternative to services like Audible, giving you full control over your media files. Key features include:
|
||||||
|
|
||||||
|
## Docker Compose Example
|
||||||
|
|
||||||
|
> [!IMPORTANT] Audiobookshelf requires a websocket connection.
|
||||||
|
|
||||||
|
``` yaml
|
||||||
|
# audiobookshelf - https://github.com/advplyr/audiobookshelf/blob/master/docker-compose.yml
|
||||||
|
---
|
||||||
|
version: "3.7"
|
||||||
|
services:
|
||||||
|
audiobookshelf:
|
||||||
|
container_name: audiobookshelf
|
||||||
|
image: ghcr.io/advplyr/audiobookshelf:latest
|
||||||
|
# ABS runs on port 13378 by default. If you want to change
|
||||||
|
# the port, only change the external port, not the internal port
|
||||||
|
ports:
|
||||||
|
- 13378:80
|
||||||
|
volumes:
|
||||||
|
# These volumes are needed to keep your library persistent
|
||||||
|
# and allow media to be accessed by the ABS server.
|
||||||
|
# The path to the left of the colon is the path on your computer,
|
||||||
|
# and the path to the right of the colon is where the data is
|
||||||
|
# available to ABS in Docker.
|
||||||
|
|
||||||
|
# You can change these media directories or add as many as you want
|
||||||
|
- /mnt/store/MediaServer/Audio_Books:/audiobooks
|
||||||
|
- /mnt/store/MediaServer/podcasts:/podcasts
|
||||||
|
- /mnt/store/app/audiobookshelf/metadata:/metadata
|
||||||
|
|
||||||
|
# The config directory needs to be on the same physical machine
|
||||||
|
# you are running ABS on
|
||||||
|
- /app/audiobookshelf/config:/config
|
||||||
|
restart: unless-stopped
|
||||||
|
# You can use the following environment variable to run the ABS
|
||||||
|
# docker container as a specific user. You will need to change
|
||||||
|
# the UID and GID to the correct values for your user.
|
||||||
|
#environment:
|
||||||
|
# - user=1000:1000
|
||||||
|
```
|
||||||
|
|
||||||
53
content/projects/code-server/code-server.md
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
---
|
||||||
|
title: code-server
|
||||||
|
description: Quick overview of code-server and setup
|
||||||
|
date: 2025-02-04
|
||||||
|
lastmod: 2025-02-04
|
||||||
|
author: wompmacho
|
||||||
|
showHero: false # needed to hide "hero banner"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Whats is code-server?
|
||||||
|
|
||||||
|
`code-server` is a self-hosted instance of Visual Studio Code that runs on a remote server and is accessible directly through your web browser. It effectively turns any machine with a CPU and RAM into a fully functional cloud-based development environment.
|
||||||
|
|
||||||
|
## Docker Compose Example
|
||||||
|
|
||||||
|
``` yaml
|
||||||
|
# code-server -- https://hub.docker.com/r/linuxserver/code-server
|
||||||
|
---
|
||||||
|
services:
|
||||||
|
code-server:
|
||||||
|
image: lscr.io/linuxserver/code-server:latest
|
||||||
|
container_name: code-server
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=America/New_York
|
||||||
|
- PASSWORD=password #optional
|
||||||
|
- HASHED_PASSWORD= #optional
|
||||||
|
- SUDO_PASSWORD=password #optional
|
||||||
|
- SUDO_PASSWORD_HASH= #optional
|
||||||
|
- PROXY_DOMAIN=code-server.domain.com #optional
|
||||||
|
- DEFAULT_WORKSPACE=/apps #optional
|
||||||
|
volumes:
|
||||||
|
- code-server-nfs:/config
|
||||||
|
- apps:/apps
|
||||||
|
ports:
|
||||||
|
- 8443:8443
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
code-server-nfs:
|
||||||
|
name: code-server-nfs
|
||||||
|
driver_opts:
|
||||||
|
type: nfs
|
||||||
|
o: addr=truenas,nolock,soft,rw
|
||||||
|
device: :/mnt/store/vault/app/code-server
|
||||||
|
apps:
|
||||||
|
name: apps
|
||||||
|
driver_opts:
|
||||||
|
type: nfs
|
||||||
|
o: addr=truenas,nolock,soft,rw
|
||||||
|
device: :/mnt/store/vault/app/
|
||||||
|
```
|
||||||
107
content/projects/frigate/index.md
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
---
|
||||||
|
title: frigate
|
||||||
|
description: frigate dvr
|
||||||
|
author: wompmacho
|
||||||
|
date: 2025-02-04
|
||||||
|
lastmod: 2025-02-04
|
||||||
|
showHero: false # needed to hide "hero banner"
|
||||||
|
---
|
||||||
|
|
||||||
|
## What is Frigate?
|
||||||
|
|
||||||
|
`Frigate` is a complete and local NVR designed for Home Assistant with AI object detection. Uses OpenCV and Tensorflow to perform realtime object detection locally for IP cameras.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# frigate - https://docs.frigate.video/frigate/installation/
|
||||||
|
---
|
||||||
|
version: "3.9"
|
||||||
|
services:
|
||||||
|
frigate:
|
||||||
|
container_name: frigate
|
||||||
|
privileged: true # this may not be necessary for all setups
|
||||||
|
restart: unless-stopped
|
||||||
|
image: ghcr.io/blakeblackshear/frigate:stable
|
||||||
|
shm_size: "64mb" # update for your cameras based on calculation above
|
||||||
|
volumes:
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
- /app/frigate/config:/config
|
||||||
|
- /mnt/store/app/frigate:/media/frigate
|
||||||
|
- type: tmpfs # Optional: 1GB of memory, reduces SSD/SD Card wear
|
||||||
|
target: /tmp/cache
|
||||||
|
tmpfs:
|
||||||
|
size: 1000000000
|
||||||
|
ports:
|
||||||
|
- "5000:5000"
|
||||||
|
- "8554:8554" # RTSP feeds
|
||||||
|
- "8555:8555/tcp" # WebRTC over tcp
|
||||||
|
- "8555:8555/udp" # WebRTC over udp
|
||||||
|
environment:
|
||||||
|
FRIGATE_RTSP_PASSWORD: "password"
|
||||||
|
```
|
||||||
|
|
||||||
|
requires to set a `config.yml` in the `/config` volume.
|
||||||
|
|
||||||
|
## My current config
|
||||||
|
|
||||||
|
Am not currently running the optimized setup for this, but testing things out.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
mqtt:
|
||||||
|
enabled: false
|
||||||
|
cameras:
|
||||||
|
front:
|
||||||
|
birdseye:
|
||||||
|
order: 1
|
||||||
|
ffmpeg:
|
||||||
|
inputs:
|
||||||
|
- path: rtsp://USERNAME:PASSWORD@IPADDR:554/path_to_stream
|
||||||
|
roles:
|
||||||
|
- detect
|
||||||
|
- record
|
||||||
|
objects:
|
||||||
|
track:
|
||||||
|
- person
|
||||||
|
detect:
|
||||||
|
width: 1920
|
||||||
|
height: 1080
|
||||||
|
|
||||||
|
record:
|
||||||
|
sync_recordings: True
|
||||||
|
enabled: True
|
||||||
|
retain:
|
||||||
|
days: 7
|
||||||
|
mode: motion
|
||||||
|
events:
|
||||||
|
# Optional: Number of seconds before the event to include (default: shown below)
|
||||||
|
pre_capture: 5
|
||||||
|
# Optional: Number of seconds after the event to include (default: shown below)
|
||||||
|
post_capture: 5
|
||||||
|
|
||||||
|
detectors:
|
||||||
|
cpu1:
|
||||||
|
type: cpu
|
||||||
|
num_threads: 3
|
||||||
|
|
||||||
|
# Include all cameras by default in Birdseye view
|
||||||
|
birdseye:
|
||||||
|
enabled: True
|
||||||
|
mode: continuous
|
||||||
|
width: 1280
|
||||||
|
height: 720
|
||||||
|
quality: 8
|
||||||
|
inactivity_threshold: 30
|
||||||
|
```
|
||||||
|
|
||||||
|
## Proxy fixes
|
||||||
|
|
||||||
|
For nginx proxy - add this to advanced options for proxy host
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_redirect off;
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_request_buffering off;
|
||||||
|
```
|
||||||
38
content/projects/homarr/index.md
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
title: homarr
|
||||||
|
description: homarr
|
||||||
|
author: wompmacho
|
||||||
|
date: 2025-02-04
|
||||||
|
lastmod: 2025-02-04
|
||||||
|
showHero: false # needed to hide "hero banner"
|
||||||
|
---
|
||||||
|
|
||||||
|
## What is homarr?
|
||||||
|
|
||||||
|
`homarr` is a nice little dashboard app that can be used to organize your homelab with a simple webpage interface. Great for Quick links, updates on weather, time, seeing if a server is down, monitoring your webcams or torrents, etc.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Personally I have moved on to [homepage](https://gethomepage.dev/). Looks a little nicer in my opinion. Not the biggest fan of homarr interface, Though I may try again after some updates.
|
||||||
|
|
||||||
|
## Docker Compose Example
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> For docker support, extend a volume to the docker.sock
|
||||||
|
> - `/var/run/docker.sock:/var/run/docker.sock`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# homarr - docker compose
|
||||||
|
---
|
||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
homarr:
|
||||||
|
container_name: homarr
|
||||||
|
image: ghcr.io/ajnart/homarr:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- /app/homarr/configs:/app/data/configs
|
||||||
|
- /app/homarr/icons:/app/public/icons
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
ports:
|
||||||
|
- '7575:7575'
|
||||||
|
```
|
||||||
31
content/projects/homepage/index.md
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
title: homepage
|
||||||
|
description: homepage
|
||||||
|
author: wompmacho
|
||||||
|
date: 2025-02-04
|
||||||
|
lastmod: 2025-02-04
|
||||||
|
showHero: false # needed to hide "hero banner"
|
||||||
|
---
|
||||||
|
|
||||||
|
## What is homepage?
|
||||||
|
|
||||||
|
`homepage` is an open-source, highly customizable, and static site-based dashboard designed to organize your self-hosted services into a beautiful, central hub.
|
||||||
|
|
||||||
|
Unlike other dashboards that require complex databases or heavy backend services, homepage runs as a lightweight, Docker-based container that reads a single configuration file (YAML).
|
||||||
|
|
||||||
|
## Docker Compose Example
|
||||||
|
|
||||||
|
``` yaml
|
||||||
|
# homepage - docker compose
|
||||||
|
---
|
||||||
|
services:
|
||||||
|
homepage:
|
||||||
|
container_name: homepage
|
||||||
|
image: ghcr.io/gethomepage/homepage:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- /mnt/store/app/homepage/configs:/app/config # Make sure your local config directory exists
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock # (optional) For docker integrations
|
||||||
|
ports:
|
||||||
|
- 7676:3000
|
||||||
|
```
|
||||||
52
content/projects/jellyfin/index.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
---
|
||||||
|
title: jellyfin
|
||||||
|
description: jellyfin
|
||||||
|
author: wompmacho
|
||||||
|
date: 2025-02-04
|
||||||
|
lastmod: 2025-02-04
|
||||||
|
showHero: false # needed to hide "hero banner"
|
||||||
|
---
|
||||||
|
|
||||||
|
## What is jellyfin?
|
||||||
|
|
||||||
|
`Jellyfin` is a media server. I like it because its simple, free, doesnt require
|
||||||
|
online accounts and lets you serve up your movies, tv shows and music. Is very
|
||||||
|
similar to apps like Plex and Emby. You can manage your media and auto download
|
||||||
|
things like episode names, artwork etc. Has plugin support and is basically
|
||||||
|
trying to be a better open source version of Plex. It has apps/support for
|
||||||
|
android, google tvs, firestick, iphone etc.
|
||||||
|
|
||||||
|
## Docker Compose Example
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Jellyfin - docker compose
|
||||||
|
---
|
||||||
|
services:
|
||||||
|
jellyfin:
|
||||||
|
container_name: jellyfin
|
||||||
|
image: lscr.io/linuxserver/jellyfin:latest
|
||||||
|
environment:
|
||||||
|
- PUID=0
|
||||||
|
- PGID=0
|
||||||
|
- TZ=America/New_York
|
||||||
|
ports:
|
||||||
|
- 8096:8096
|
||||||
|
- 8920:8920 #optional https
|
||||||
|
- 7359:7359/udp #optional discovery
|
||||||
|
- 1900:1900/udp #optional discovery
|
||||||
|
volumes:
|
||||||
|
- /app/jellyfin:/config # config for your jellyfin
|
||||||
|
- /mnt/store/:/data/store # where your media lives (movies/tv etc.)
|
||||||
|
restart: unless-stopped
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Recomend storing the metadata & cache on NAS and not on the OS docker host.
|
||||||
|
The files start to get LARGE for jellyfin due to mass amount of metadata stored
|
||||||
|
for media. Set this under the jellyfin `general` settings after jellyfin is
|
||||||
|
running. {.is-warning}
|
||||||
|
|
||||||
|
Once you server is running, head over to the to your opened port (docker_container_ip:8096) to start the setup proccess. When adding libraries -
|
||||||
|
select the content type, set the display name and then click the FOLDERS +
|
||||||
|
option. This is where you will select the path to your media that you set up in
|
||||||
|
the volumes.
|
||||||
31
content/projects/jellyseer/index.md
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
title: jellyseer
|
||||||
|
description: jellyseer
|
||||||
|
author: wompmacho
|
||||||
|
date: 2025-02-04
|
||||||
|
lastmod: 2025-02-04
|
||||||
|
showHero: false # needed to hide "hero banner"
|
||||||
|
---
|
||||||
|
|
||||||
|
## What is jellyseer?
|
||||||
|
|
||||||
|
`Jellyseerr` is a free, open-source, and highly intuitive media request management tool designed for the Jellyfin (and Plex/Emby) ecosystem. It essentially acts as a "gateway" between your users and your media server.
|
||||||
|
|
||||||
|
## Docker Compose Example
|
||||||
|
|
||||||
|
``` yaml
|
||||||
|
# jellyseerr - docker compose
|
||||||
|
---
|
||||||
|
services:
|
||||||
|
jellyseerr:
|
||||||
|
image: fallenbagel/jellyseerr:latest
|
||||||
|
container_name: jellyseerr
|
||||||
|
environment:
|
||||||
|
- LOG_LEVEL=debug
|
||||||
|
- TZ=America/New_York
|
||||||
|
ports:
|
||||||
|
- 5055:5055
|
||||||
|
volumes:
|
||||||
|
- /mnt/store/app/jellyseerr/config:/app/config
|
||||||
|
restart: unless-stopped
|
||||||
|
```
|
||||||
BIN
content/projects/lab_setup/atl_home_network_2019.jpg
Normal file
|
After Width: | Height: | Size: 177 KiB |
32
content/projects/lab_setup/index.md
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
title: Lab Setup
|
||||||
|
description: My Home Network and Projects
|
||||||
|
author: wompmacho
|
||||||
|
date: 2023-07-04T05:33:31.158Z
|
||||||
|
lastmod: 2026-03-08
|
||||||
|
showHero: false # needed to hide "hero banner"
|
||||||
|
---
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2024 Home Lab
|
||||||
|

|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2023 Home Lab
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2020 Home Lab
|
||||||
|

|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2019 Home Lab
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
---
|
||||||
|
After Width: | Height: | Size: 1.1 MiB |
BIN
content/projects/lab_setup/sc_apartment_network_diagram_2024.png
Normal file
|
After Width: | Height: | Size: 2.9 MiB |
|
After Width: | Height: | Size: 2.6 MiB |
45
content/projects/linkstack/index.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
---
|
||||||
|
title: linkstacks
|
||||||
|
description: linkstacks
|
||||||
|
author: wompmacho
|
||||||
|
date: 2025-02-04
|
||||||
|
lastmod: 2025-02-04
|
||||||
|
showHero: false # needed to hide "hero banner"
|
||||||
|
---
|
||||||
|
|
||||||
|
## What is linkstacks?
|
||||||
|
|
||||||
|
`Linkstacks` is a nice little linktr.ee clone that allows you to set up a simple
|
||||||
|
link page. It can also be expanded to add multiple users and you can host
|
||||||
|
multiple people's pages with their own user accounts and everything.
|
||||||
|
|
||||||
|
## Docker Compose Example
|
||||||
|
|
||||||
|
``` yaml
|
||||||
|
# Linkstacks - docker compose
|
||||||
|
version: "3.8"
|
||||||
|
---
|
||||||
|
services:
|
||||||
|
linkstack:
|
||||||
|
container_name: 'linkstack'
|
||||||
|
hostname: 'linkstack'
|
||||||
|
image: 'linkstackorg/linkstack:latest'
|
||||||
|
user: '0:0'
|
||||||
|
environment:
|
||||||
|
TZ: 'America/New_york'
|
||||||
|
SERVER_ADMIN: 'SERVER_ADMIN_EMAIL'
|
||||||
|
HTTP_SERVER_NAME: 'HTTP_DOMAIN_NAME'
|
||||||
|
HTTPS_SERVER_NAME: 'HTTPS_DOMAIN_NAME'
|
||||||
|
LOG_LEVEL: 'info'
|
||||||
|
PHP_MEMORY_LIMIT: '256M'
|
||||||
|
UPLOAD_MAX_FILESIZE: '8M'
|
||||||
|
volumes:
|
||||||
|
- 'linkstack_data:/htdocs'
|
||||||
|
#- '/app/linkstack/:/htdocs'
|
||||||
|
ports:
|
||||||
|
- '8190:443'
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
linkstack_data:
|
||||||
|
```
|
||||||
49
content/projects/mkdocs/index.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
---
|
||||||
|
title: mkdocs
|
||||||
|
description: mkdocs
|
||||||
|
author: wompmacho
|
||||||
|
date: 2025-02-04
|
||||||
|
lastmod: 2025-02-04
|
||||||
|
showHero: false # needed to hide "hero banner"
|
||||||
|
---
|
||||||
|
|
||||||
|
## What is mkdocs?
|
||||||
|
|
||||||
|
MkDocs is a fast, simple, and extensible static site generator geared specifically toward building project documentation. It relies heavily on Markdown files, which makes it incredibly accessible for developers who want to write docs as easily as they write code.
|
||||||
|
|
||||||
|
## Docker Compose Example
|
||||||
|
|
||||||
|
Running mkdocs with [material](https://squidfunk.github.io/mkdocs-material/)
|
||||||
|
theme and plugins built in.
|
||||||
|
|
||||||
|
> [!INFO]
|
||||||
|
> There is some setup of folders and things that are not automatic so
|
||||||
|
wont work straight out of the box.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# mkdocs -- https://squidfunk.github.io/mkdocs-material/
|
||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
mkdocs:
|
||||||
|
container_name: 'mkdocs'
|
||||||
|
restart: unless-stopped
|
||||||
|
image: squidfunk/mkdocs-material
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
volumes:
|
||||||
|
#- /mnt/store/app/mkdocs/:/docs
|
||||||
|
- docs_nfs:/docs
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
ports:
|
||||||
|
- "9896:8000"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
docs_nfs:
|
||||||
|
name: docs_nfs
|
||||||
|
driver_opts:
|
||||||
|
type: nfs
|
||||||
|
o: addr=truenas,nolock,soft,ro
|
||||||
|
device: :/mnt/store/vault/app/mkdocs
|
||||||
|
```
|
||||||
BIN
content/projects/nginx-proxy-manager/cloudflare_ssl_example.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 17 KiB |
161
content/projects/nginx-proxy-manager/index.md
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
---
|
||||||
|
title: nginx-proxy-manager
|
||||||
|
description: nginx-proxy-manager
|
||||||
|
author: wompmacho
|
||||||
|
date: 2025-02-04
|
||||||
|
lastmod: 2025-02-04
|
||||||
|
showHero: false # needed to hide "hero banner"
|
||||||
|
---
|
||||||
|
|
||||||
|
## What is nginx-proxy-manager?
|
||||||
|
|
||||||
|
Nginx-proxy-manager is a simplified GUI for handling an nginx server
|
||||||
|
configuration. Nginx is a reverse proxy server.
|
||||||
|
|
||||||
|
A reverse proxy server is a type of proxy server that typically sits behind the
|
||||||
|
firewall in a private network and directs client requests to the appropriate
|
||||||
|
backend server. Nginx is a very common go-to. Nginx-proxy-manager is a nice gui
|
||||||
|
version that has some built in tools, like handling SSL Certificates with Let's
|
||||||
|
Encrypt. Nginix can provide load balancing, Web acceleration, Security and
|
||||||
|
anonymity for servers.
|
||||||
|
|
||||||
|
Personally I use nginx to proxy all my traffic to my dedicated servers so that I
|
||||||
|
do not have to expose local hosts via port forwarding. This also allows me to do
|
||||||
|
some extra encryption along the way and add additional security via access lists
|
||||||
|
where I see fit. I can also reuse ports, which saves a lot of time for
|
||||||
|
configurations.
|
||||||
|
|
||||||
|
## Docker Compose Example
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# nginx-proxy-manager - docker compose
|
||||||
|
---
|
||||||
|
version: "3.8"
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
container_name: nginx-proxy-manager
|
||||||
|
image: "jc21/nginx-proxy-manager:latest"
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
- "81:81"
|
||||||
|
- "443:443"
|
||||||
|
volumes:
|
||||||
|
- ./data:/data
|
||||||
|
- ./letsencrypt:/etc/letsencrypt
|
||||||
|
```
|
||||||
|
|
||||||
|
Nginx gives you that great routing to your internal networked servers. Also
|
||||||
|
helps you set up your DNS both inside and outside the network. Can be a little
|
||||||
|
confusing at first.
|
||||||
|
|
||||||
|
First you will need a route you want to point to. In this case I will use this
|
||||||
|
site.
|
||||||
|
|
||||||
|
I want to be able to type in **wiki.wompmacho.com** and arrive here rather than
|
||||||
|
remembering my IP address and having to set up multiple complicated port
|
||||||
|
forwards in my router. Instead nginx allows me to open one port - 80 and direct
|
||||||
|
all traffic to nginx. Then based on some rules I have set up I can point that
|
||||||
|
traffic to my internal server. For this I will need a domain name server (DNS)
|
||||||
|
to point to my external IP.
|
||||||
|
|
||||||
|
### DNS
|
||||||
|
|
||||||
|
A domain name server (DNS) allows me to make a name that can be easily looked up
|
||||||
|
and point traffic to an Internet Protocol (IP) address that a server can easily
|
||||||
|
understand.
|
||||||
|
|
||||||
|
I can type in a name to a browser - my browser will ask my computers dns cache
|
||||||
|
_where is google.com_ and when not found - it will ask my router _where is
|
||||||
|
google.com_ which will then ask the dns server it is pointed to (typically your
|
||||||
|
ISP) who then asks the dns server the ISP is pointing to... and so on until
|
||||||
|
eventually one of the DNS servers contain the information about _google.com_.
|
||||||
|
Then it can retrieve the IP address of that server and send that information
|
||||||
|
back down the line - adding it to its own cache as it goes so that it does not
|
||||||
|
have to keep looking up this information. This will allow the browser to make
|
||||||
|
requests to that server directly.
|
||||||
|
|
||||||
|
In order to make my dns name known so that people can find it on the internet
|
||||||
|
easily, We will have to purchase the name from a host of an Authoritative DNS
|
||||||
|
server. An Authoritative DNS server will not cache the info, but instead act as
|
||||||
|
a primary resource of the configuration for a dns name so other dns servers can
|
||||||
|
ask for that resource.
|
||||||
|
|
||||||
|
In this example I have purchased **wompmacho.com** from cloudflare who operate
|
||||||
|
as a registrar and facilitates purchasing that name from a higher authoritative
|
||||||
|
registry. Allowing me to point my external IP address to this address.
|
||||||
|
|
||||||
|
Once I have a DNS name I can use my registar (cloudflare) to point that name to
|
||||||
|
my external IP address (my router's IP address).
|
||||||
|
|
||||||
|
> [!INFO] wompmacho.com <> 175.222.222.222
|
||||||
|
|
||||||
|
### Port forwarding
|
||||||
|
|
||||||
|
This traffic will then be requested from my router which _should_ be typically
|
||||||
|
set up to block incoming requests. In order to allow a request to my server
|
||||||
|
hosting my site I will need to open a port (80) and allow traffic through my
|
||||||
|
router's firewall to my docker container that is hosting nginx-proxy-manager.
|
||||||
|
Nginx will then redirect this again to my docker container for my site.
|
||||||
|
|
||||||
|
### A records
|
||||||
|
|
||||||
|
For my scenario my dns name is **wompmacho.com** but if I want to have multiple
|
||||||
|
sites at my IP address I will need to be able to differentiate them. To do this
|
||||||
|
I will use an A record. This allows me to split up my domain with multiple sub
|
||||||
|
domains.
|
||||||
|
|
||||||
|
- wiki.wompmacho.com
|
||||||
|
- **subdomain**.wompmacho.com
|
||||||
|
|
||||||
|
### Setting up a proxy
|
||||||
|
|
||||||
|
This will point traffic to the same domain (wompmacho.com) but based on the
|
||||||
|
sub-domain nginx will be able to direct and load balance traffic to my internal
|
||||||
|
server hosting the wiki - in this case also my docker container. The wiki is
|
||||||
|
hosted on a different port. We can point this proxy to that port.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Cloudflare DNS Proxy
|
||||||
|
|
||||||
|
An example of a dns service is Cloudflare. I switched over to cloudflare when
|
||||||
|
google sold their awesome DNS. I have been loving it since the switch, there is
|
||||||
|
a lot of info out there on services they offer and how to set things up. The
|
||||||
|
biggest reason I switched over to cloudflare is their dns proxy. This allows my
|
||||||
|
home IP to be proxied behind cloudflare services - and helps hide my servers
|
||||||
|
location. This also allows me to utilize their services to block things like
|
||||||
|
botnet attacks.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### SSL encryption
|
||||||
|
|
||||||
|
Secure Sockets Layer (SSL) is a security protocol that provides privacy,
|
||||||
|
authentication, and integrity to Internet communications. SSL eventually evolved
|
||||||
|
into Transport Layer Security (TLS). Using Nginx-proxy-manager we can connect
|
||||||
|
our cloudflare DNS to our nginx server using SSL encryption. This is what that
|
||||||
|
lock and **https** indicates on your browser - you are using a secured and
|
||||||
|
verified connection to the server. This helps stop man in the middle attacks
|
||||||
|
preventing people from spoofing the connection and spying on you.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
We do this by adding a cloudflare certificate to nginx proxy manager and then
|
||||||
|
setting up our proxy host to use this certificate on the SSL tab.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
> [!INFO] Note this is is only for a secure connection between **nginx <-> cloudflare**
|
||||||
|
|
||||||
|
The details page is referring to your internal setup - or where nginx should
|
||||||
|
point the dns to.
|
||||||
|
|
||||||
|
> [!INFO] **origin server <-> nginx**
|
||||||
|
|
||||||
|
Use https here only if you have ssl setup on your origin server and your server
|
||||||
|
is set up to accept https, otherwise you make get bad gateway 502 errors.
|
||||||
|
|
||||||
|

|
||||||
BIN
content/projects/nginx-proxy-manager/nginx_proxy_host_setup.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 18 KiB |
BIN
content/projects/nginx-proxy-manager/nginx_ssl_setup_example.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
content/projects/nginx-proxy-manager/ssl_connection_lock.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
45
content/projects/portainer/index.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
---
|
||||||
|
title: portainer
|
||||||
|
description: Quick overview of portainer and setup
|
||||||
|
date: 2025-02-04
|
||||||
|
lastmod: 2025-02-04
|
||||||
|
author: wompmacho
|
||||||
|
showHero: false # needed to hide "hero banner"
|
||||||
|
---
|
||||||
|
|
||||||
|
## What is Portainer?
|
||||||
|
|
||||||
|
`Portainer` is a lightweight, powerful container management platform that provides a graphical user interface (GUI) to manage your Docker, Docker Swarm, and Kubernetes environments. It essentially sits on top of your container runtime, allowing you to control complex infrastructure without needing to master the command line.
|
||||||
|
|
||||||
|
Portainer provides a very easy to understand user interface for deploying other docker containers. The great thing is, Portainer
|
||||||
|
is a container itself, so it should run automatically following setup and allow
|
||||||
|
you a nice gui interface for your docker environment via a web browser. I
|
||||||
|
particularly love its dashboard because you get a great snapshot of your running
|
||||||
|
containers, can easily restart and monitor your containers, but most importantly
|
||||||
|
edit and deploy docker-compose files via the "stacks" page.
|
||||||
|
|
||||||
|
- Portainer CE is free version
|
||||||
|
- [Install
|
||||||
|
Guide](https://docs.portainer.io/start/install-ce/server/docker/linux)
|
||||||
|
- Create the volume that Portainer Server will use to store its database
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
docker volume create portainer_data
|
||||||
|
```
|
||||||
|
|
||||||
|
- Download and install the Portainer Server container
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
docker run -d -p 8000:8000 -p 9443:9443 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
- Verify the container is running with docker ps
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
root@server:~# docker ps
|
||||||
|
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||||
|
de5b28eb2fa9 portainer/portainer-ce:latest "/portainer" 2 weeks ago Up 9 days 0.0.0.0:8000->8000/tcp, :::8000->8000/tcp, 0.0.0.0:9443->9443/tcp, :::9443->9443/tcp portainer
|
||||||
|
```
|
||||||
|
|
||||||
|
- Navigate to https://HOST_IP_ADDRESS:9443 and create a user so you can log in
|
||||||
|
to the Portainer web interface.
|
||||||
73
content/projects/qbittorent/index.md
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
---
|
||||||
|
title: qBittorrent
|
||||||
|
description: Quick overview of qBittorrent and setup
|
||||||
|
date: 2025-02-04
|
||||||
|
lastmod: 2025-02-04
|
||||||
|
author: wompmacho
|
||||||
|
showHero: false # needed to hide "hero banner"
|
||||||
|
---
|
||||||
|
|
||||||
|
## What is qBittorrent?
|
||||||
|
|
||||||
|
`qBittorrent` is a awesome, simple app that allows you to use classic QBittorrent in a docker
|
||||||
|
container. I use QBittorrent because I can set it up with a VPN that only
|
||||||
|
connects via the container directly. Effectively separating it from the rest of
|
||||||
|
my network and allowing me to continue as normal while it is downloading. It
|
||||||
|
will automatically stop the network if the VPN is not functioning correctly.
|
||||||
|
|
||||||
|
## Docker Compose Example
|
||||||
|
|
||||||
|
``` yaml
|
||||||
|
# qbittorrentvpn - docker compose
|
||||||
|
# https://hub.docker.com/r/dyonr/qbittorrentvpn
|
||||||
|
---
|
||||||
|
version: "2"
|
||||||
|
services :
|
||||||
|
qbittorrentvpn:
|
||||||
|
container_name: qbittorrentvpn
|
||||||
|
privileged: true
|
||||||
|
image: dyonr/qbittorrentvpn
|
||||||
|
environment :
|
||||||
|
- VPN_ENABLED=true
|
||||||
|
- VPN_USERNAME=VPN_USERNAME
|
||||||
|
- VPN_PASSWORD=VPN_PASSWORD
|
||||||
|
- LAN_NETWORK=10.0.0.0/24
|
||||||
|
- WEBUI_PORT_ENV=8080
|
||||||
|
- INCOMING_PORT_ENV=8999
|
||||||
|
ports :
|
||||||
|
- 8080:8080
|
||||||
|
- 8999:8999
|
||||||
|
- 8999:8999/udp
|
||||||
|
volumes :
|
||||||
|
- /app/QBittorrent/config:/config
|
||||||
|
- /mnt/store/MediaServer/torrent:/downloads
|
||||||
|
restart: unless-stopped
|
||||||
|
```
|
||||||
|
To set up the VPN you will need to have an existing account with a VPN service.
|
||||||
|
Username & Password for the vpn will be provided as a key by your vpn service.
|
||||||
|
In my case I use Surfshark and have to go log into my account, navigate to the
|
||||||
|
linux setup page and grab my generated Username key and Password key there.
|
||||||
|
|
||||||
|
A credentials file on my docker host was generated by QBittorrent when running
|
||||||
|
the first time.
|
||||||
|
```
|
||||||
|
# download all availble server conf
|
||||||
|
sudo wget https://my.surfshark.com/vpn/api/v1/server/configurations
|
||||||
|
|
||||||
|
# cp the server you want to use into config folder
|
||||||
|
/app/QBittorrent/config/openvpn
|
||||||
|
```
|
||||||
|
|
||||||
|
Once you restart your qbittorrentvpn docker container you can test your vpn
|
||||||
|
service with a torrent leak test. Use the + add torrent link button to Download
|
||||||
|
the torrent and test that your VPN service is connected and working.
|
||||||
|
|
||||||
|
- [torrent-leak-test](https://bash.ws/torrent-leak-test)
|
||||||
|
|
||||||
|
### Magnet links
|
||||||
|
|
||||||
|
Use magnet link and item hash to avoid logins
|
||||||
|
|
||||||
|
```
|
||||||
|
magnet:?xt=urn:btih:${HASH}
|
||||||
|
```
|
||||||
33
content/projects/uptime-kuma/index.md
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
---
|
||||||
|
title: uptime-kuma
|
||||||
|
description: Quick overview of uptime-kuma and setup
|
||||||
|
date: 2025-02-04
|
||||||
|
lastmod: 2025-02-04
|
||||||
|
author: wompmacho
|
||||||
|
showHero: false # needed to hide "hero banner"
|
||||||
|
---
|
||||||
|
|
||||||
|
## What is uptime-kuma?
|
||||||
|
|
||||||
|
`uptime-kuma` is a neat little web monitoring application. Lotta dope things right
|
||||||
|
out of the box, very gui / user friendly. Pretty much just add the stack, update
|
||||||
|
the dir for config - and it works. Integrates with discord webhooks, great easy
|
||||||
|
status page and dashboard.
|
||||||
|
|
||||||
|
## Docker Compose Example
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# uptime-kuma - docker compose
|
||||||
|
---
|
||||||
|
# https://github.com/louislam/uptime-kuma/wiki/%F0%9F%94%A7-How-to-Install
|
||||||
|
version: '3.3'
|
||||||
|
services:
|
||||||
|
uptime-kuma:
|
||||||
|
container_name: uptime-kuma
|
||||||
|
image: louislam/uptime-kuma:1
|
||||||
|
volumes:
|
||||||
|
- /app/uptime-kuma/data:/app/data
|
||||||
|
ports:
|
||||||
|
- 3001:3001 # <Host Port>:<Container Port>
|
||||||
|
restart: always
|
||||||
|
```
|
||||||
42
content/projects/vaultwarden/index.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
description: vaultwarden
|
||||||
|
author: wompmacho
|
||||||
|
date: 2025-02-23
|
||||||
|
lastmod: 2025-02-23
|
||||||
|
showHero: false # needed to hide "hero banner"
|
||||||
|
---
|
||||||
|
|
||||||
|
## What is vaultwarden?
|
||||||
|
|
||||||
|
`vaultwarden` ia a alternative server implementation of the Bitwarden Client API, written in
|
||||||
|
Rust and compatible with official Bitwarden clients, perfect for self-hosted
|
||||||
|
deployment where running the official resource-heavy service might not be ideal.
|
||||||
|
|
||||||
|
## Docker Compose Example
|
||||||
|
|
||||||
|
```
|
||||||
|
# vaultwarden -- https://github.com/dani-garcia/vaultwarden
|
||||||
|
---
|
||||||
|
services:
|
||||||
|
vaultwarden:
|
||||||
|
image: vaultwarden/server:latest
|
||||||
|
container_name: vaultwarden
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
DOMAIN: "https://vaultwarden.wompmacho.com"
|
||||||
|
ROCKET_PORT: 80
|
||||||
|
ROCKET_ENV: production
|
||||||
|
volumes:
|
||||||
|
- vaultwarden-mount:/data/
|
||||||
|
ports:
|
||||||
|
- '9998:80'
|
||||||
|
- '9999:443'
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
vaultwarden-mount:
|
||||||
|
name: vaultwarden-mount
|
||||||
|
driver_opts:
|
||||||
|
type: nfs
|
||||||
|
o: addr=truenas,nolock,soft,rw
|
||||||
|
device: :/mnt/store/vault/app/vaultwarden
|
||||||
|
```
|
||||||
35
content/projects/webtop/index.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
title: webtop
|
||||||
|
description: Quick overview of webtop and setup
|
||||||
|
date: 2025-02-04
|
||||||
|
lastmod: 2025-02-04
|
||||||
|
author: wompmacho
|
||||||
|
showHero: false # needed to hide "hero banner"
|
||||||
|
---
|
||||||
|
|
||||||
|
## What is webtop?
|
||||||
|
|
||||||
|
`webtop` is a awesome mini linux env I can use as a secure remote web-client for my home
|
||||||
|
network.
|
||||||
|
|
||||||
|
## Docker Compose Example
|
||||||
|
|
||||||
|
``` yaml
|
||||||
|
# webtop - https://docs.linuxserver.io/images/docker-webtop/#lossless-mode
|
||||||
|
---
|
||||||
|
services:
|
||||||
|
webtop:
|
||||||
|
image: lscr.io/linuxserver/webtop:latest
|
||||||
|
container_name: webtop
|
||||||
|
environment:
|
||||||
|
- PUID=1000
|
||||||
|
- PGID=1000
|
||||||
|
- TZ=America/New_York
|
||||||
|
- TITLE=Webtop #optional
|
||||||
|
volumes:
|
||||||
|
- /app/webtop/data:/config
|
||||||
|
ports:
|
||||||
|
- 7978:3000
|
||||||
|
- 7979:3001
|
||||||
|
restart: unless-stopped
|
||||||
|
```
|
||||||
45
content/projects/wikijs/index.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
---
|
||||||
|
title: wikijs
|
||||||
|
description: Quick overview of wikijs and setup
|
||||||
|
date: 2025-02-04
|
||||||
|
lastmod: 2025-02-04
|
||||||
|
author: wompmacho
|
||||||
|
showHero: false # needed to hide "hero banner"
|
||||||
|
---
|
||||||
|
|
||||||
|
## What is wikijs?
|
||||||
|
|
||||||
|
Wiki.js is a powerful, modern, and open-source wiki application built on Node.js. It is designed to be the central knowledge base for your home lab or professional projects, replacing traditional, clunky wiki platforms with a sleek, intuitive interface.
|
||||||
|
|
||||||
|
I like it because of the useful Markdown editor that lets you nicely organize
|
||||||
|
links, code etc. I can also backup the database to my NAS in nice MD files, so
|
||||||
|
nothing gets lost if something is corrupted.
|
||||||
|
|
||||||
|
## Docker Compose Example
|
||||||
|
|
||||||
|
``` yaml
|
||||||
|
# wikijs - docker compose
|
||||||
|
# https://github.com/linuxserver/docker-wikijs
|
||||||
|
---
|
||||||
|
version: "3.8"
|
||||||
|
services:
|
||||||
|
wikijs:
|
||||||
|
image: lscr.io/linuxserver/wikijs:latest
|
||||||
|
container_name: wikijs
|
||||||
|
environment:
|
||||||
|
- PUID=0
|
||||||
|
- PGID=0
|
||||||
|
- TZ=Etc/UTC
|
||||||
|
- DB_TYPE=sqlite #optional
|
||||||
|
- DB_HOST= #optional
|
||||||
|
- DB_PORT= #optional
|
||||||
|
- DB_NAME= #optional
|
||||||
|
- DB_USER= #optional
|
||||||
|
- DB_PASS= #optional
|
||||||
|
volumes:
|
||||||
|
- /app/wiki/config:/config
|
||||||
|
- /app/wiki/data:/data
|
||||||
|
ports:
|
||||||
|
- 3000:3000
|
||||||
|
restart: unless-stopped
|
||||||
|
```
|
||||||
172
content/resume.md
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
---
|
||||||
|
title: Resume
|
||||||
|
description: job history & resume
|
||||||
|
author: wompmacho
|
||||||
|
date: 2024-07-12T02:40:50.769Z
|
||||||
|
lastmod: 2025-02-08
|
||||||
|
tags:
|
||||||
|
---
|
||||||
|
|
||||||
|
{{< rawhtml >}}
|
||||||
|
<table id="resume-table">
|
||||||
|
<!----------------------------------Experience--------------------------------->
|
||||||
|
<tr>
|
||||||
|
<td colspan="3" class="header header-google-red">Experience</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr class="section-header">
|
||||||
|
<td colspan="2" class="role">Special Projects Lead (DT L3) - <span class="google-blue">G</span><span class="google-red">o</span><span class="google-yellow">o</span><span class="google-blue">g</span><span class="google-green">l</span><span class="google-red">e</span></td>
|
||||||
|
<td class="dates">October 2024 - <span class="google-green">Current</span></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="3">
|
||||||
|
<p>
|
||||||
|
HwOps Special Projects Lead; Resolve technical incidents and escalations by performing analysis utilizing existing data models or leveraging custom built data infrastructure to formulate and interpret data to reach specific conclusions and next steps. Develop detailed reports and intuitive dashboards, communicating key insights for data driven analysis. File bugs against products, documentation, and procedures by documenting desired behavior or steps to reproduce, and driving bugs to resolution. Suggest code-level resolutions for complex issues by leveraging tools, tool development and effective communication with stakeholders. Identify opportunities to build or enable solutions that improve, support or empower OMs, Site Leads & DTs to solve issues by using self-service tools and documentation. Fostered team growth through mentorship, training course facilitation, collaboration with internal training teams, and technical writing development.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr class="section-header">
|
||||||
|
<td colspan="2" class="role">Data Center Technician (DT L2) - <span class="google-blue">G</span><span class="google-red">o</span><span class="google-yellow">o</span><span class="google-blue">g</span><span class="google-green">l</span><span class="google-red">e</span></td>
|
||||||
|
<td class="dates">July 2023 - October 2024</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="3">
|
||||||
|
<p>
|
||||||
|
Site Operations hardware maintenance and networking, resolving critical issues and collaborating cross-functionally to address SLO deviations. Built and led an internal escalation team for weekend/holiday support, creating resources and onboarding leaders. Developed and documented new processes, championed project documentation, and contributed to technician hiring, onboarding, and training. Mentored Googlers and facilitated training programs. Transitioned into Leader Role as a Maintenance Lead / Escalation Point of Contact.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<!---------------------------------Consulting---------------------------------->
|
||||||
|
<tr>
|
||||||
|
<td colspan="3" class="header header-google-yellow">Consulting / Freelance / Helping out Family</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr class="section-header">
|
||||||
|
<td colspan="2" class="role">Porch Light Properties LLC</td>
|
||||||
|
<td class="dates">Jun 2020 - Jul 2023</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="3">
|
||||||
|
<p>
|
||||||
|
Developed and implemented long-term systems, development, and planning strategies, including rebranding initiatives. Served as Hiring Manager, overseeing onboarding, system administration, and policy management. Managed social media, website development/design, SEO, and marketing campaigns (including Facebook Ads). Utilized Google Analytics and oversaw technology/security initiatives and traditional marketing.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr class="section-header">
|
||||||
|
<td colspan="2" class="role">Videography, Photography, Film Media, Drone Services</td>
|
||||||
|
<td class="dates">Jun 2020 - Jan 2023</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="3">
|
||||||
|
<div class="iframe-wrapper" style="display: flex; flex-direction: row; align-items: center; justify-content: center;">
|
||||||
|
<iframe width="560" height="315" src="https://www.youtube.com/embed/-DN8mhOxeKQ?si=jdVsuQoCPdWjUjBp" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr class="section-header">
|
||||||
|
<td colspan="2" class="role">Tabora Farm and Winery</td>
|
||||||
|
<td class="dates">Dec 2019 - May 2020</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="3">
|
||||||
|
<p>
|
||||||
|
Managed social media, website development/design, and SEO. Oversaw tax and licensing compliance for interstate wine shipments.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<!---------------------------------Freelance----------------------------------->
|
||||||
|
<!-- <tr>
|
||||||
|
<td colspan="3" class="header header-google-green">Freelance</td>
|
||||||
|
</tr> -->
|
||||||
|
|
||||||
|
<!---------------------------------JOBs---------------------------------------->
|
||||||
|
<tr>
|
||||||
|
<td colspan="3" class="header header-google-blue">Work Experience Continued</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr class="section-header">
|
||||||
|
<td colspan="2" class="role">Operations Engineer - <span class="twitter-blue">Twitter</span> Inc.</td>
|
||||||
|
<td class="dates">Apr 2015 - Sep 2017</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="3">
|
||||||
|
<p>
|
||||||
|
Led site operations teams and provided on-call support for multiple data centers/POPs, consistently exceeding SLOs goals. Managed, hired, onboarded, and trained operations engineers and staff. Served as a Twitter liaison and brand ambassador, reporting on emerging technologies. Provided on-site support to engineering teams, proactively monitored services, and managed projects including repairs, decommissioning, upgrades, installations, networking, and maintenance.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr class="section-header">
|
||||||
|
<td colspan="2" class="role">Operations Technician (OTA/OA) - <span class="google-blue">G</span><span class="google-red">o</span><span class="google-yellow">o</span><span class="google-blue">g</span><span class="google-green">l</span><span class="google-red">e</span></td>
|
||||||
|
<td class="dates">Nov 2012 - Mar 2015</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="3">
|
||||||
|
<p>
|
||||||
|
Supported multiple sites on critical infrastructure projects, including server repairs, hardware qualifications, QA, NPI, HAT, disk sanitization, project management, decommissioning, upgrades, and backup library maintenance. Collaborated effectively to maintain Google's infrastructure and ensure operational excellence.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<!---------------------------------Education----------------------------------->
|
||||||
|
<tr>
|
||||||
|
<td colspan="3" class="header header-google-red">Education</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr class="section-header">
|
||||||
|
<td colspan="2" class="">Pennridge High School</td>
|
||||||
|
<td class="dates">2004 - 2009</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr class="section-header">
|
||||||
|
<td colspan="2" class="">Georgia State University - Computer Science</td>
|
||||||
|
<td class="dates">2012 - 2017</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<!---------------------------------Skills & CERTS------------------------------>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="header header-google-yellow">Skills</td>
|
||||||
|
<td colspan="1" class="header header-google-green">Certificates</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<ul>
|
||||||
|
<li>Leadership, Mentoring</li>
|
||||||
|
<li>UNIX / Linux / OS</li>
|
||||||
|
<li>Networking, TCP/IP, DNS, DHCP</li>
|
||||||
|
<li>Technical Writing & Documentation</li>
|
||||||
|
<li>SQL, HTML, CSS, JS</li>
|
||||||
|
<li>Java, Golang, Shell Scripting</li>
|
||||||
|
<li>Docker, VMs, Baremetal</li>
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<ul>
|
||||||
|
<li>LPIC-1 - Linux Professional Institute</li>
|
||||||
|
<li>SUSE Certified Linux Administrator (SUSE CLA)</li>
|
||||||
|
<li>Small Unmanned Aircraft System (Part 107)</li>
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="center">
|
||||||
|
{{< button href="me/2025_Resume_Michael_Braicu.pdf" target="_self" >}}
|
||||||
|
PDF Download
|
||||||
|
{{< /button >}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{< /rawhtml >}}
|
||||||
8
content/search.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
title: "Search"
|
||||||
|
layout: "search"
|
||||||
|
summary: "search"
|
||||||
|
placeholder: "Search the wiki..."
|
||||||
|
|
||||||
|
---
|
||||||
4
content/stream/_index.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
draft: true
|
||||||
|
title: 'Stream'
|
||||||
|
---
|
||||||
71
content/stream/stream_gear.md
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
---
|
||||||
|
description: stream stuffs
|
||||||
|
date: 2024-07-12T03:11:33.093Z
|
||||||
|
lastmod: 2025-02-02
|
||||||
|
#tags: kick, livestream, twitch, youtube
|
||||||
|
author: wompmacho
|
||||||
|
title: Gear
|
||||||
|
---
|
||||||
|
|
||||||
|
this has changed a bit... I will update later...
|
||||||
|
|
||||||
|
## Camera Gear
|
||||||
|
|
||||||
|
- [Sony Alpha a6400](https://amzn.to/38FDUjk)
|
||||||
|
- [Sigma 16mm f/1.4](https://amzn.to/3LzPKdj)
|
||||||
|
- [Gonine AC-PW20 NP-FW50 Dummy Battery](https://amzn.to/3vAaz30)
|
||||||
|
- [Elgato Cam Link 4K](https://amzn.to/3FdqOWU)
|
||||||
|
- [Pixel Desk Camera Mount Stand](https://amzn.to/3F5XG3G)
|
||||||
|
- [Quick Release Plate Camera Tripod Mount](https://amzn.to/3KGBSwU)
|
||||||
|
- [Micro HDMI to HDMI Adapter Cable](https://amzn.to/38DV2pG)
|
||||||
|
|
||||||
|
## Lighting
|
||||||
|
|
||||||
|
- [GVM 1000D RGB Led Video Light](https://amzn.to/3vHKCP7)
|
||||||
|
|
||||||
|
## Audio
|
||||||
|
|
||||||
|
- [Shure SM7b - Vocal Dynamic Microphone](https://amzn.to/3CCxdd0)
|
||||||
|
- [Shure SM57-LCE Cardioid Dynamic Mic](https://amzn.to/3F60duI)
|
||||||
|
- [Shure A2WS-BLK - pop filter](https://amzn.to/3krKGMm)
|
||||||
|
- [PreSonus Revelator io24](https://amzn.to/3P17eBI)
|
||||||
|
- [Mic Arm Desk Mount](https://amzn.to/3wTBSFN)
|
||||||
|
- [Brainwavz XL Micro Suede Memory Foam Earpads](https://amzn.to/3kvMDqY)
|
||||||
|
|
||||||
|
## IRL Setup
|
||||||
|
|
||||||
|
- [Alex Tech 10ft - 1/2 inch Cord Protector](https://amzn.to/3LFExYP)
|
||||||
|
- [SIM Card Adapter Nano Micro](https://amzn.to/3F3i2um)
|
||||||
|
- [NDI HDMI Encoder, TBS2603SE NDI](https://amzn.to/3KsQlwc)
|
||||||
|
- [USB to DC Convert Cable](https://amzn.to/373VBsm)
|
||||||
|
- [Cudy N300 WiFi Unlocked 4G LTE](https://amzn.to/38DHUAT)
|
||||||
|
- [Sony FDR-X1000V/W 4K Action Cam](https://amzn.to/3KAVDpg)
|
||||||
|
- [Backpack Shoulder Chest Strap Clip Mount](https://amzn.to/3F44HBY)
|
||||||
|
- [Bicycle & Motorcycle Phone Mount](https://amzn.to/38G679C)
|
||||||
|
- [Portable Charger Power Bank](https://amzn.to/3Kx4M2f)
|
||||||
|
|
||||||
|
## Game PC
|
||||||
|
|
||||||
|
- [WD_BLACK 2TB SN850 NVMe](https://amzn.to/3vwPvKu)
|
||||||
|
- [Corsair Vengeance LPX 32GB - Mem](https://amzn.to/3LCr6ZA)
|
||||||
|
- [Corsair Carbide Series Air 740](https://amzn.to/3F4dzHT)
|
||||||
|
- [CORSAIR Hydro Series H115i](https://amzn.to/3kveuaR)
|
||||||
|
- [ASUS ROG STRIX GeForce GTX 1080](https://amzn.to/3LCsFGW)
|
||||||
|
- [Intel Core i7-7700K](https://amzn.to/3N0NY5B)
|
||||||
|
- [Asus Z170-A - MOBO](https://amzn.to/3LHCVhq)
|
||||||
|
|
||||||
|
## Peripheral
|
||||||
|
|
||||||
|
- [Logitech G Pro Wireless Gaming Mouse](https://amzn.to/3vuC3H3)
|
||||||
|
- [Acer Predator XB272 bmiprz 27"](https://amzn.to/3LD5uMJ)
|
||||||
|
- [CORSAIR K70 RGB MK.2 RAPIDFIRE](https://amzn.to/38J3Zhi)
|
||||||
|
- [Speakers - PreSonus Eris](https://amzn.to/3KAwAD1)
|
||||||
|
- [Single Monitor Desk Mount - Adjustable Gas Spring](https://amzn.to/3LBhluO)
|
||||||
|
|
||||||
|
## Stream PC
|
||||||
|
|
||||||
|
- [G.SKILL TridentZ Series 16GB](https://amzn.to/3kvPMHp)
|
||||||
|
- [Elgato Stream Deck](https://amzn.to/3Fd4WuD)
|
||||||
|
- [AMD YD180XBCAEWOF Ryzen 7 1800X](https://amzn.to/3F468Am)
|
||||||
|
- [Nvidia GeForce GTX 1080](https://amzn.to/3F468Am)
|
||||||
|
- [ASUS Prime X370-Pro - MOBO](https://amzn.to/3s65gG7)
|
||||||
77
content/stream/stream_settings.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
---
|
||||||
|
title: OBS Settings
|
||||||
|
description: OBS & Live Stream Settings
|
||||||
|
author: wompmacho
|
||||||
|
date: 2024-01-25T22:28:32.943Z
|
||||||
|
lastmod: 2025-02-04
|
||||||
|
#tags: twitch
|
||||||
|
---
|
||||||
|
|
||||||
|
## OBS
|
||||||
|
|
||||||
|
### Stream Settings
|
||||||
|
|
||||||
|
```
|
||||||
|
Ignore stream service settings
|
||||||
|
Video Encoder
|
||||||
|
x264
|
||||||
|
1920x1080
|
||||||
|
Rate control
|
||||||
|
CBR
|
||||||
|
Bitrate
|
||||||
|
8000 Kbps
|
||||||
|
Keyframe Interval
|
||||||
|
1s
|
||||||
|
CPU Usage
|
||||||
|
Medium
|
||||||
|
Profile
|
||||||
|
None
|
||||||
|
Tune
|
||||||
|
None
|
||||||
|
x264 Options
|
||||||
|
keyinit=90
|
||||||
|
```
|
||||||
|
### Recording Settings
|
||||||
|
|
||||||
|
```
|
||||||
|
Recoding Format
|
||||||
|
.mkv
|
||||||
|
Audio Track
|
||||||
|
All
|
||||||
|
Automatic File Spliting
|
||||||
|
Split Time
|
||||||
|
240 min
|
||||||
|
```
|
||||||
|
### Video
|
||||||
|
|
||||||
|
```
|
||||||
|
Common FPS Values
|
||||||
|
60
|
||||||
|
```
|
||||||
|
### Advanced
|
||||||
|
|
||||||
|
```
|
||||||
|
Recording Filename Formatting
|
||||||
|
%MM-%DD-%CCYY_%A_%hh-%mm-%p_%FPS
|
||||||
|
Automatically Remux to MP4
|
||||||
|
```
|
||||||
|
## Camera
|
||||||
|
|
||||||
|
```
|
||||||
|
Sony A6400
|
||||||
|
|
||||||
|
Resolution for stream
|
||||||
|
3840x2160
|
||||||
|
|
||||||
|
Specs
|
||||||
|
Max Resolution
|
||||||
|
6000 x 4000
|
||||||
|
Image Ratio
|
||||||
|
1:1, 3:2, 16:9
|
||||||
|
Sensor
|
||||||
|
25 megapixels
|
||||||
|
CMOS
|
||||||
|
APS-C (23.5 x 15.6 mm)
|
||||||
|
ISO
|
||||||
|
Auto, 100-32000 (expands to 102800)
|
||||||
|
```
|
||||||
138
layouts/_default/list.html
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
{{ .Scratch.Set "scope" "list" }}
|
||||||
|
{{ $enableToc := .Params.showTableOfContents | default (site.Params.list.showTableOfContents | default false) }}
|
||||||
|
{{ $showToc := and $enableToc (in .TableOfContents "<ul") }}
|
||||||
|
|
||||||
|
{{/* Hero */}}
|
||||||
|
{{ if site.Params.list.showHero | default false }}
|
||||||
|
{{ $heroStyle := print "hero/" site.Params.list.heroStyle ".html" }}
|
||||||
|
{{ if templates.Exists ( printf "partials/%s" $heroStyle ) }}
|
||||||
|
{{ partial $heroStyle . }}
|
||||||
|
{{ else }}
|
||||||
|
{{ partial "hero/basic.html" . }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{/* Header */}}
|
||||||
|
<header>
|
||||||
|
{{ if .Params.showBreadcrumbs | default (site.Params.list.showBreadcrumbs | default false) }}
|
||||||
|
{{ partial "breadcrumbs.html" . }}
|
||||||
|
{{ end }}
|
||||||
|
<h1 class="mt-5 text-4xl font-extrabold text-neutral-900 dark:text-neutral">{{ .Title }}</h1>
|
||||||
|
<div class="mt-1 mb-2 text-base text-neutral-500 dark:text-neutral-400 print:hidden">
|
||||||
|
{{ partial "article-meta/list.html" (dict "context" . "scope" "single") }}
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{{/* Description (markdown content) */}}
|
||||||
|
{{ $tocMargin := cond $showToc "mt-12" "mt-0" }}
|
||||||
|
{{ $topClass := cond (hasPrefix site.Params.header.layout "fixed") "lg:top-[140px]" "lg:top-10" }}
|
||||||
|
<section class="{{ $tocMargin }} prose flex max-w-full flex-col dark:prose-invert lg:flex-row">
|
||||||
|
{{ if $showToc }}
|
||||||
|
<div class="order-first px-0 lg:order-last lg:max-w-xs lg:ps-8">
|
||||||
|
<div class="toc ps-5 lg:sticky {{ $topClass }}">
|
||||||
|
{{ if $showToc }}
|
||||||
|
{{ partial "toc.html" . }}
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
<div class="min-w-0 min-h-0 max-w-prose w-full">
|
||||||
|
{{ .Content }}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{{/* Article Grid */}}
|
||||||
|
{{ if gt .Pages 0 }}
|
||||||
|
{{ $cardView := .Params.cardView | default (site.Params.list.cardView | default false) }}
|
||||||
|
{{ $cardViewScreenWidth := .Params.cardViewScreenWidth | default (site.Params.list.cardViewScreenWidth | default false) }}
|
||||||
|
{{ $groupByYear := .Params.groupByYear | default (site.Params.list.groupByYear | default false) }}
|
||||||
|
{{ $orderByWeight := .Params.orderByWeight | default (site.Params.list.orderByWeight | default false) }}
|
||||||
|
{{ $groupByYear := and (not $orderByWeight) $groupByYear }}
|
||||||
|
|
||||||
|
{{ if not $cardView }}
|
||||||
|
<section class="space-y-10 w-full">
|
||||||
|
{{ if not $orderByWeight }}
|
||||||
|
{{ range (.Pages.GroupByDate "2006") }}
|
||||||
|
{{ if $groupByYear }}
|
||||||
|
<h2 class="mt-12 text-2xl font-bold text-neutral-700 first:mt-8 dark:text-neutral-300">
|
||||||
|
{{ .Key }}
|
||||||
|
</h2>
|
||||||
|
{{ end }}
|
||||||
|
{{ range .Pages }}
|
||||||
|
{{ partial "article-link/simple.html" . }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ else }}
|
||||||
|
{{ range .Pages.ByWeight }}
|
||||||
|
{{ partial "article-link/simple.html" . }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
</section>
|
||||||
|
{{ else }}
|
||||||
|
{{ if $groupByYear }}
|
||||||
|
{{ range (.Pages.GroupByDate "2006") }}
|
||||||
|
{{ if $cardViewScreenWidth }}
|
||||||
|
<div class="relative w-screen max-w-[1600px] px-[30px] start-[calc(max(-50vw,-800px)+50%)]">
|
||||||
|
<h2 class="mt-12 mb-3 text-2xl font-bold text-neutral-700 first:mt-8 dark:text-neutral-300">
|
||||||
|
{{ .Key }}
|
||||||
|
</h2>
|
||||||
|
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5">
|
||||||
|
{{ range .Pages }}
|
||||||
|
{{ partial "article-link/card.html" . }}
|
||||||
|
{{ end }}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
{{ else }}
|
||||||
|
<h2 class="mt-12 mb-3 text-2xl font-bold text-neutral-700 first:mt-8 dark:text-neutral-300">
|
||||||
|
{{ .Key }}
|
||||||
|
</h2>
|
||||||
|
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3">
|
||||||
|
{{ range .Pages }}
|
||||||
|
{{ partial "article-link/card.html" . }}
|
||||||
|
{{ end }}
|
||||||
|
</section>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ else }}
|
||||||
|
{{ if $cardViewScreenWidth }}
|
||||||
|
<div class="relative w-screen max-w-[1600px] px-[30px] start-[calc(max(-50vw,-800px)+50%)]">
|
||||||
|
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5">
|
||||||
|
{{ if not $orderByWeight }}
|
||||||
|
{{ range (.Pages.GroupByDate "2006") }}
|
||||||
|
{{ range .Pages }}
|
||||||
|
{{ partial "article-link/card.html" . }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ else }}
|
||||||
|
{{ range .Pages.ByWeight }}
|
||||||
|
{{ partial "article-link/card.html" . }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
{{ else }}
|
||||||
|
<section class="w-full grid gap-4 sm:grid-cols-2 md:grid-cols-3">
|
||||||
|
{{ if not $orderByWeight }}
|
||||||
|
{{ range (.Pages.GroupByDate "2006") }}
|
||||||
|
{{ range .Pages }}
|
||||||
|
{{ partial "article-link/card.html" . }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ else }}
|
||||||
|
{{ range .Pages.ByWeight }}
|
||||||
|
{{ partial "article-link/card.html" . }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
</section>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ else }}
|
||||||
|
<section class="mt-10 prose dark:prose-invert">
|
||||||
|
<p class="py-8 border-t">
|
||||||
|
<em>{{ i18n "list.no_articles" | emojify }}</em>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
82
layouts/_default/single.html
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
{{ .Scratch.Set "scope" "single" }}
|
||||||
|
|
||||||
|
<article class="w-full py-8">
|
||||||
|
{{ if .Params.showHero | default (site.Params.article.showHero | default false) }}
|
||||||
|
{{ partial (printf "hero/%s.html" (.Params.heroStyle | default site.Params.article.heroStyle)) . }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<header id="single_header" class="mt-5 max-w-prose mx-auto px-4">
|
||||||
|
<h1 class="mt-0 text-4xl font-extrabold text-neutral-900 dark:text-neutral">{{ .Title | emojify }}</h1>
|
||||||
|
<div class="mt-1 mb-6 text-base text-neutral-500 dark:text-neutral-400 print:hidden">
|
||||||
|
{{ partial "article-meta/basic.html" (dict "context" . "scope" "single") }}
|
||||||
|
</div>
|
||||||
|
{{ if not (.Params.showAuthorBottom | default (site.Params.article.showAuthorBottom | default false)) }}
|
||||||
|
{{ template "SingleAuthor" . }}
|
||||||
|
{{ end }}
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{{/* The Relative Wrapper: Acts as an anchor for the absolute TOC */}}
|
||||||
|
<div class="single-post-wrapper relative mt-8 w-full max-w-7xl mx-auto px-4">
|
||||||
|
|
||||||
|
{{/* Sidebar TOC: Mobile flow (Top), Desktop absolute (Right) */}}
|
||||||
|
{{ if .Params.showTableOfContents | default site.Params.article.showTableOfContents }}
|
||||||
|
<aside id="sidebar-toc" class="w-full mb-8 xl:mb-0">
|
||||||
|
<div class="toc sticky top-24">
|
||||||
|
{{ partial "toc.html" . }}
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{/* Main Content: Dead center, uninfluenced by anything */}}
|
||||||
|
<section id="main-content" class="w-full">
|
||||||
|
<div class="article-content prose dark:prose-invert mx-auto">
|
||||||
|
{{ partial "series/series.html" . }}
|
||||||
|
{{ .Content }}
|
||||||
|
|
||||||
|
{{ if .Params.replyByEmail | default site.Params.replyByEmail }}
|
||||||
|
<strong class="block mt-8">
|
||||||
|
<a target="_blank" href="mailto:{{ site.Params.Author.email }}">Reply by Email</a>
|
||||||
|
</strong>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<div class="mt-10">
|
||||||
|
{{ if (.Params.showAuthorBottom | default (site.Params.article.showAuthorBottom | default false)) }}
|
||||||
|
{{ template "SingleAuthor" . }}
|
||||||
|
{{ end }}
|
||||||
|
{{ partial "series/series-closed.html" . }}
|
||||||
|
{{ partial "sharing-links.html" . }}
|
||||||
|
{{ partial "related.html" . }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer class="pt-8 mt-12 border-t border-neutral-200 dark:border-neutral-800 max-w-prose mx-auto px-4">
|
||||||
|
{{ partial "article-pagination.html" . }}
|
||||||
|
</footer>
|
||||||
|
</article>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "SingleAuthor" }}
|
||||||
|
<div class="max-w-prose">
|
||||||
|
{{ $authorsData := site.Data.authors }}
|
||||||
|
{{ $baseURL := site.BaseURL }}
|
||||||
|
{{ $isAuthorBottom := (.Params.showAuthorBottom | default ( site.Params.article.showAuthorBottom | default false)) }}
|
||||||
|
{{ if not (strings.HasSuffix $baseURL "/") }}{{ $baseURL = delimit (slice $baseURL "/") "" }}{{ end }}
|
||||||
|
|
||||||
|
{{ if .Params.showAuthor | default (site.Params.article.showAuthor | default true) }}
|
||||||
|
{{ partial "author.html" . }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ range $author := .Page.Params.authors }}
|
||||||
|
{{ $authorData := index $authorsData $author }}
|
||||||
|
{{ if $authorData }}
|
||||||
|
{{ $link := printf "%sauthors/%s/" $baseURL $author }}
|
||||||
|
{{ partial "author-extra.html" (dict "context" . "data" $authorData "link" $link) }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
<div class="{{ cond $isAuthorBottom "mb-10" "mb-5" }}"></div>
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
8
layouts/index.html
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{{ define "main" }}
|
||||||
|
{{ $partial := print "home/" .Site.Params.homepage.layout ".html" }}
|
||||||
|
{{ if templates.Exists ( printf "partials/%s" $partial ) }}
|
||||||
|
{{ partial $partial . }}
|
||||||
|
{{ else }}
|
||||||
|
{{ partial "home/profile.html" . }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
102
layouts/partials/article-link/card.html
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
{{/* Used by
|
||||||
|
1. list.html and term.html (when the cardView option is enabled)
|
||||||
|
2. Recent articles template (when the cardView option is enabled)
|
||||||
|
3. Shortcode list.html
|
||||||
|
*/}}
|
||||||
|
{{ $disableImageOptimization := site.Store.Get "disableImageOptimization" }}
|
||||||
|
|
||||||
|
{{ $page := .Page }}
|
||||||
|
{{ $featured := "" }}
|
||||||
|
{{ $featuredURL := "" }}
|
||||||
|
{{ if not .Params.hideFeatureImage }}
|
||||||
|
{{/* frontmatter and resources logic remains the same */}}
|
||||||
|
{{ with $page }}
|
||||||
|
{{ with .Params.featureimage }}
|
||||||
|
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
|
||||||
|
{{ if site.Params.hotlinkFeatureImage }}
|
||||||
|
{{ $featuredURL = . }}
|
||||||
|
{{ else }}
|
||||||
|
{{ $featured = resources.GetRemote . }}
|
||||||
|
{{ end }}
|
||||||
|
{{ else }}
|
||||||
|
{{ $featured = resources.Get . }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ if not (or $featured $featuredURL) }}
|
||||||
|
{{ $images := .Resources.ByType "image" }}
|
||||||
|
{{ range slice "*feature*" "*cover*" "*thumbnail*" }}
|
||||||
|
{{ if not $featured }}{{ $featured = $images.GetMatch . }}{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ if not (or $featured $featuredURL) }}
|
||||||
|
{{ $default := site.Store.Get "defaultFeaturedImage" }}
|
||||||
|
{{ if $default.url }}
|
||||||
|
{{ $featuredURL = $default.url }}
|
||||||
|
{{ else if $default.obj }}
|
||||||
|
{{ $featured = $default.obj }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ if not $featuredURL }}
|
||||||
|
{{ with $featured }}
|
||||||
|
{{ $featuredURL = .RelPermalink }}
|
||||||
|
{{ if not (or $disableImageOptimization (eq .MediaType.SubType "svg")) }}
|
||||||
|
{{ $featuredURL = (.Resize "600x").RelPermalink }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<article
|
||||||
|
class="relative flex flex-col h-full overflow-hidden rounded-lg border border-neutral-300 dark:border-neutral-600">
|
||||||
|
{{ with $featuredURL }}
|
||||||
|
<div class="flex-none relative overflow-hidden thumbnail_card h-40">
|
||||||
|
<img
|
||||||
|
src="{{ . }}"
|
||||||
|
role="presentation"
|
||||||
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
class="not-prose absolute inset-0 w-full h-full object-cover">
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
{{ if and .Draft .Site.Params.article.showDraftLabel }}
|
||||||
|
<span class="absolute top-0 right-0 m-2">
|
||||||
|
{{ partial "badge.html" (i18n "article.draft" | emojify) }}
|
||||||
|
</span>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{/* Main Content Wrapper - Added flex-grow to push footer down */}}
|
||||||
|
<div class="p-4 flex flex-col flex-grow">
|
||||||
|
<header>
|
||||||
|
<a
|
||||||
|
{{ with $page.Params.externalUrl }}
|
||||||
|
href="{{ . }}" target="_blank" rel="external"
|
||||||
|
{{ else }}
|
||||||
|
href="{{ $page.RelPermalink }}"
|
||||||
|
{{ end }}
|
||||||
|
class="not-prose before:absolute before:inset-0 decoration-primary-500 dark:text-neutral text-xl font-bold text-neutral-800 hover:underline hover:underline-offset-2">
|
||||||
|
<h2>
|
||||||
|
{{ .Title | emojify }}
|
||||||
|
{{ if .Params.externalUrl }}
|
||||||
|
<span class="cursor-default align-top text-xs text-neutral-400 dark:text-neutral-500">
|
||||||
|
<span class="rtl:hidden">↗</span>
|
||||||
|
<span class="ltr:hidden">↖</span>
|
||||||
|
</span>
|
||||||
|
{{ end }}
|
||||||
|
</h2>
|
||||||
|
</a>
|
||||||
|
</header>
|
||||||
|
<div class="text-sm text-neutral-500 dark:text-neutral-400 mb-2">
|
||||||
|
{{ partial "article-meta/basic.html" . }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{ if .Params.showSummary | default (.Site.Params.list.showSummary | default false) }}
|
||||||
|
{{/* Added line-clamp-3 and flex-grow here */}}
|
||||||
|
<div class="prose dark:prose-invert py-1 line-clamp-3 flex-grow text-sm">
|
||||||
|
{{ .Summary | plainify }}
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="px-6 pt-4 pb-2"></div>
|
||||||
|
</article>
|
||||||
2
layouts/partials/favicons.html
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<link rel="icon" type="image/svg+xml" href="{{ "/icons/icon.svg" | relURL }}">
|
||||||
|
<link rel="apple-touch-icon" href="{{ "/icons/icon.svg" | relURL }}">
|
||||||
237
layouts/partials/head.html
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
{{ with .Site.Language.Params.htmlCode | default .Site.LanguageCode }}
|
||||||
|
<meta http-equiv="content-language" content="{{ . }}">
|
||||||
|
{{ end }}
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<meta name="theme-color">
|
||||||
|
|
||||||
|
{{/* Title */}}
|
||||||
|
{{ if .IsHome }}
|
||||||
|
<title>{{ .Site.Title | emojify }}</title>
|
||||||
|
<meta name="title" content="{{ .Site.Title | emojify }}">
|
||||||
|
{{ else }}
|
||||||
|
<title>{{ .Title | emojify }} · {{ .Site.Title | emojify }}</title>
|
||||||
|
<meta name="title" content="{{ .Title | emojify }} · {{ .Site.Title | emojify }}">
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{/* Metadata */}}
|
||||||
|
{{ with (.Params.Summary | default .Params.Description) | default .Site.Params.description }}
|
||||||
|
<meta name="description" content="{{ . }}">
|
||||||
|
{{ end }}
|
||||||
|
{{ with .Params.Tags | default .Site.Params.keywords }}
|
||||||
|
<meta name="keywords" content="{{ range . }}{{ . }},{{ end }}">
|
||||||
|
{{ end }}
|
||||||
|
{{ with .Site.Params.robots }}
|
||||||
|
<meta name="robots" content="{{ . }}">
|
||||||
|
{{ end }}
|
||||||
|
{{ with .Params.robots }}
|
||||||
|
<meta name="robots" content="{{ . }}">
|
||||||
|
{{ end }}
|
||||||
|
<link rel="canonical" href="{{ .Permalink }}">
|
||||||
|
{{ range .AlternativeOutputFormats }}
|
||||||
|
{{ printf `
|
||||||
|
<link rel="%s" type="%s" href="%s" title="%s" />` .Rel .MediaType.Type .RelPermalink ($.Site.Title | emojify) |
|
||||||
|
safeHTML
|
||||||
|
}}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{/* Me */}}
|
||||||
|
{{ with .Site.Params.Author.name }}
|
||||||
|
<meta name="author" content="{{ . }}">
|
||||||
|
{{ end }}
|
||||||
|
{{ with .Site.Params.Author.links }}
|
||||||
|
{{ range $links := . }}
|
||||||
|
{{ range $name, $url := $links }}
|
||||||
|
{{ if not (strings.HasPrefix $url "mailto:") }}
|
||||||
|
<link href="{{ $url }}" rel="me">
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{/* Social */}}
|
||||||
|
{{ template "_internal/opengraph.html" . }}
|
||||||
|
{{ template "_internal/twitter_cards.html" . }}
|
||||||
|
{{/* Use defaultSocialImage if feature image does not exist */}}
|
||||||
|
{{ $featureImage := "" }}
|
||||||
|
{{ $pageImages := .Resources.ByType "image" }}
|
||||||
|
{{ range slice "*featured*" "*cover*" "*thumbnail*" }}
|
||||||
|
{{ if not $featureImage }}
|
||||||
|
{{ $featureImage = $pageImages.GetMatch . }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ if not $featureImage }}
|
||||||
|
{{ with .Site.Params.defaultSocialImage }}
|
||||||
|
{{ $socialImage := "" }}
|
||||||
|
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
|
||||||
|
{{ $socialImage = resources.GetRemote . }}
|
||||||
|
{{ else }}
|
||||||
|
{{ $socialImage = resources.Get . }}
|
||||||
|
{{ end }}
|
||||||
|
{{ with $socialImage }}
|
||||||
|
<meta name="twitter:image" content="{{ .RelPermalink | absURL }}">
|
||||||
|
<meta property="og:image" content="{{ .RelPermalink | absURL }}">
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{/* Site Verification */}}
|
||||||
|
{{ with .Site.Params.verification.google }}
|
||||||
|
<meta name="google-site-verification" content="{{ . }}">
|
||||||
|
{{ end }}
|
||||||
|
{{ with .Site.Params.verification.bing }}
|
||||||
|
<meta name="msvalidate.01" content="{{ . }}">
|
||||||
|
{{ end }}
|
||||||
|
{{ with .Site.Params.verification.pinterest }}
|
||||||
|
<meta name="p:domain_verify" content="{{ . }}">
|
||||||
|
{{ end }}
|
||||||
|
{{ with .Site.Params.verification.yandex }}
|
||||||
|
<meta name="yandex-verification" content="{{ . }}">
|
||||||
|
{{ end }}
|
||||||
|
{{ with .Site.Params.verification.fediverse }}
|
||||||
|
<meta name="fediverse:creator" content="{{ . }}">
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ $alg := .Site.Params.fingerprintAlgorithm | default "sha512" }}
|
||||||
|
|
||||||
|
{{/* CSS */}}
|
||||||
|
{{ $cssResources := slice }}
|
||||||
|
{{ $schemeName := .Site.Params.colorScheme | default "blowfish" }}
|
||||||
|
{{ $cssScheme := resources.Get (printf "css/schemes/%s.css" (lower $schemeName)) | default (resources.Get "css/schemes/blowfish.css") }}
|
||||||
|
{{ $cssResources = $cssResources | append $cssScheme }}
|
||||||
|
{{ $cssResources = $cssResources | append (resources.Get "css/compiled/main.css") }}
|
||||||
|
{{ with resources.Get "css/custom.css" }}
|
||||||
|
{{ $cssResources = $cssResources | append . }}
|
||||||
|
{{ end }}
|
||||||
|
{{ if not .Site.Params.disableImageZoom | default true }}
|
||||||
|
{{ $cssResources = $cssResources | append (resources.Get "lib/zoom/style.css") }}
|
||||||
|
{{ end }}
|
||||||
|
{{ $bundleCSS := $cssResources | resources.Concat "css/main.bundle.css" | resources.Minify | resources.Fingerprint $alg }}
|
||||||
|
<link
|
||||||
|
type="text/css"
|
||||||
|
rel="stylesheet"
|
||||||
|
href="{{ $bundleCSS.RelPermalink }}"
|
||||||
|
integrity="{{ $bundleCSS.Data.Integrity }}">
|
||||||
|
|
||||||
|
{{/* JS loaded immediately */}}
|
||||||
|
{{ $jsAppearance := resources.Get "js/appearance.js" | resources.ExecuteAsTemplate "js/appearance.js" . | resources.Minify | resources.Fingerprint $alg }}
|
||||||
|
<script
|
||||||
|
type="text/javascript"
|
||||||
|
src="{{ $jsAppearance.RelPermalink }}"
|
||||||
|
integrity="{{ $jsAppearance.Data.Integrity }}"></script>
|
||||||
|
{{ $enableA11y := .Site.Params.enableA11y | default false }}
|
||||||
|
{{ if $enableA11y }}
|
||||||
|
{{ $jsA11y := resources.Get "js/a11y.js" | resources.Minify | resources.Fingerprint $alg }}
|
||||||
|
<script src="{{ $jsA11y.RelPermalink }}" integrity="{{ $jsA11y.Data.Integrity }}"></script>
|
||||||
|
{{ end }}
|
||||||
|
{{ $showZenMode := .Params.showZenMode | default (.Site.Params.article.showZenMode | default false) }}
|
||||||
|
{{ $shouldIncludeZenMode := or $enableA11y $showZenMode }}
|
||||||
|
{{ if and .IsPage $shouldIncludeZenMode }}
|
||||||
|
{{ $jsZenMode := resources.Get "js/zen-mode.js" | resources.Minify | resources.Fingerprint $alg }}
|
||||||
|
<script
|
||||||
|
type="text/javascript"
|
||||||
|
src="{{ $jsZenMode.RelPermalink }}"
|
||||||
|
integrity="{{ $jsZenMode.Data.Integrity }}"></script>
|
||||||
|
{{ end }}
|
||||||
|
{{ if not .Site.Params.disableImageZoom | default true }}
|
||||||
|
{{ $zoomJS := resources.Get "lib/zoom/zoom.min.umd.js" | resources.Fingerprint $alg }}
|
||||||
|
<script src="{{ $zoomJS.RelPermalink }}" integrity="{{ $zoomJS.Data.Integrity }}"></script>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{/* JS deferred */}}
|
||||||
|
{{ $jsResources := slice }}
|
||||||
|
{{ if site.Params.footer.showScrollToTop | default true }}
|
||||||
|
{{ $jsResources = $jsResources | append (resources.Get "js/scroll-to-top.js") }}
|
||||||
|
{{ end }}
|
||||||
|
{{ if .Site.Params.enableSearch | default false }}
|
||||||
|
{{ $jsResources = $jsResources | append (resources.Get "lib/fuse/fuse.min.js") | append (resources.Get "js/search.js") }}
|
||||||
|
{{ end }}
|
||||||
|
{{ if .Site.Params.enableCodeCopy | default false }}
|
||||||
|
{{ $jsResources = $jsResources | append (resources.Get "js/code.js") }}
|
||||||
|
{{ end }}
|
||||||
|
{{ if .Site.Params.rtl | default false }}
|
||||||
|
{{ $jsResources = $jsResources | append (resources.Get "js/rtl.js") }}
|
||||||
|
{{ end }}
|
||||||
|
{{ $jsResources = $jsResources | append (resources.Get "js/button-likes.js") }}
|
||||||
|
{{ $jsResources = $jsResources | append (resources.Get "js/katex-render.js") }}
|
||||||
|
{{ $jsResources = $jsResources | append (resources.Get "js/print-support.js") }}
|
||||||
|
{{ if $jsResources }}
|
||||||
|
{{ $bundleJS := $jsResources | resources.Concat "js/main.bundle.js" | resources.Minify | resources.Fingerprint $alg }}
|
||||||
|
<script
|
||||||
|
defer
|
||||||
|
type="text/javascript"
|
||||||
|
id="script-bundle"
|
||||||
|
src="{{ $bundleJS.RelPermalink }}"
|
||||||
|
integrity="{{ $bundleJS.Data.Integrity }}"
|
||||||
|
data-copy="{{ i18n "code.copy" }}"
|
||||||
|
data-copied="{{ i18n "code.copied" }}"></script>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{/* Conditional loaded resources */}}
|
||||||
|
{{ partial "vendor.html" . }}
|
||||||
|
|
||||||
|
{{/* Icons */}}
|
||||||
|
{{ if templates.Exists "partials/favicons.html" }}
|
||||||
|
{{ partialCached "favicons.html" .Site }}
|
||||||
|
{{ else }}
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="{{ "apple-touch-icon.png" | relURL }}">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="{{ "favicon-32x32.png" | relURL }}">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="{{ "favicon-16x16.png" | relURL }}">
|
||||||
|
<link rel="manifest" href="{{ "site.webmanifest" | relURL }}">
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{/* Schema */}}
|
||||||
|
{{ partial "schema.html" . }}
|
||||||
|
|
||||||
|
{{/* Analytics */}}
|
||||||
|
{{ if hugo.IsProduction }}
|
||||||
|
{{ partial "analytics/main.html" . }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{/* Extend head - eg. for custom analytics scripts, etc. */}}
|
||||||
|
{{ if templates.Exists "partials/extend-head.html" }}
|
||||||
|
{{ partialCached "extend-head.html" .Site }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{/* Uncached extend head - Example: https://gohugo.io/methods/page/hasshortcode/ */}}
|
||||||
|
{{ if templates.Exists "partials/extend-head-uncached.html" }}
|
||||||
|
{{ partial "extend-head-uncached.html" . }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{/* Firebase */}}
|
||||||
|
{{ with $.Site.Params.firebase }}
|
||||||
|
{{ if isset $.Site.Params "firebase" }}
|
||||||
|
<script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js"></script>
|
||||||
|
<script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-firestore.js"></script>
|
||||||
|
<script src="https://www.gstatic.com/firebasejs/8.10.0/firebase-auth.js"></script>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
const firebaseConfig = {
|
||||||
|
apiKey: {{ $.Site.Params.firebase.apiKey }},
|
||||||
|
authDomain: {{ $.Site.Params.firebase.authDomain }},
|
||||||
|
projectId: {{ $.Site.Params.firebase.projectId }},
|
||||||
|
storageBucket: {{ $.Site.Params.firebase.storageBucket }},
|
||||||
|
messagingSenderId: {{ $.Site.Params.firebase.messagingSenderId }},
|
||||||
|
appId: {{ $.Site.Params.firebase.appId }},
|
||||||
|
measurementId: {{ $.Site.Params.firebase.measurementId }}
|
||||||
|
};
|
||||||
|
|
||||||
|
var app = firebase.initializeApp(firebaseConfig);
|
||||||
|
var db = firebase.firestore();
|
||||||
|
var auth = firebase.auth();
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{/* Advertisement */}}
|
||||||
|
{{ with .Site.Params.advertisement.adsense }}
|
||||||
|
<meta name="google-adsense-account" content="{{ . }}">
|
||||||
|
<script
|
||||||
|
async
|
||||||
|
src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client={{ . }}"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
{{ end }}
|
||||||
|
</head>
|
||||||
126
layouts/partials/home/background.html
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
{{ $disableImageOptimization := .Site.Params.disableImageOptimization | default false }}
|
||||||
|
<article class="prose dark:prose-invert max-w-full">
|
||||||
|
<div class="relative">
|
||||||
|
<div class="absolute inset-x-0 bottom-0 h-1/2"></div>
|
||||||
|
<div class="mx-auto max-w-7xl p-0">
|
||||||
|
<div class="relative sm:overflow-hidden">
|
||||||
|
<div class="fixed inset-x-0 top-0 -z-10">
|
||||||
|
{{ $useDefault := true }}
|
||||||
|
{{ $homepageImage := "" }}
|
||||||
|
{{ with .Site.Params.defaultBackgroundImage }}
|
||||||
|
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
|
||||||
|
{{ $homepageImage = resources.GetRemote . }}
|
||||||
|
{{ else }}
|
||||||
|
{{ $homepageImage = resources.Get . }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ with .Site.Params.homepage.homepageImage }}
|
||||||
|
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
|
||||||
|
{{ $homepageImage = resources.GetRemote . }}
|
||||||
|
{{ $useDefault = false }}
|
||||||
|
{{ else }}
|
||||||
|
{{ $homepageImage = resources.Get . }}
|
||||||
|
{{ $useDefault = false }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
{{ if $homepageImage }}
|
||||||
|
{{ $style := "" }}
|
||||||
|
{{ $defaultPosition := cond $useDefault site.Params.imagePosition false }}
|
||||||
|
{{ with $.Params.imagePosition | default $defaultPosition }}
|
||||||
|
{{ $style = printf "object-position: %s;" . }}
|
||||||
|
{{ end }}
|
||||||
|
<img
|
||||||
|
id="background-image"
|
||||||
|
class="nozoom mt-0 mr-0 mb-0 ml-0 h-[1000px] w-full object-cover"
|
||||||
|
src="{{ $homepageImage.RelPermalink }}"
|
||||||
|
role="presentation"
|
||||||
|
{{ if $style }}style="{{ $style | safeCSS }}"{{ end }}>
|
||||||
|
<div
|
||||||
|
class="from-neutral absolute inset-0 h-[1000px] bg-gradient-to-t to-transparent mix-blend-normal dark:from-neutral-800"></div>
|
||||||
|
<div
|
||||||
|
class="from-neutral absolute inset-0 h-[1000px] bg-gradient-to-t to-neutral-100 opacity-60 mix-blend-normal dark:from-neutral-800 dark:to-neutral-800"></div>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="relative flex flex-col items-center justify-center px-1 py-1 text-center">
|
||||||
|
{{ with .Site.Params.Author.image }}
|
||||||
|
{{ $authorImage := "" }}
|
||||||
|
{{ if or (strings.HasPrefix . "http:") (strings.HasPrefix . "https:") }}
|
||||||
|
{{ $authorImage = resources.GetRemote . }}
|
||||||
|
{{ else }}
|
||||||
|
{{ $authorImage = resources.Get . }}
|
||||||
|
{{ end }}
|
||||||
|
{{ if $authorImage }}
|
||||||
|
{{ $final := $authorImage }}
|
||||||
|
{{ $squareImage := $authorImage }}
|
||||||
|
{{ if not (or $disableImageOptimization (eq $authorImage.MediaType.SubType "svg")) }}
|
||||||
|
{{ $final = $authorImage.Fill (print "288x288 q" ( $.Site.Params.Author.imagequality | default "96" )) }}
|
||||||
|
{{ $shortSide := int (math.Min $authorImage.Width $authorImage.Height) }}
|
||||||
|
{{ $squareImage = $authorImage.Crop (printf "%dx%d" $shortSide $shortSide ) }}
|
||||||
|
{{ end }}
|
||||||
|
<img
|
||||||
|
class="mb-2 h-36 w-36 rounded-full"
|
||||||
|
width="144"
|
||||||
|
height="144"
|
||||||
|
alt="{{ $.Site.Params.Author.name | default `Author` }}"
|
||||||
|
src="{{ $final.RelPermalink }}"
|
||||||
|
data-zoom-src="{{ $squareImage.RelPermalink }}">
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
<h1 class="mb-2 text-4xl font-extrabold text-neutral-800 dark:text-neutral-200">
|
||||||
|
{{ .Site.Params.Author.name | default .Site.Title }}
|
||||||
|
</h1>
|
||||||
|
{{ with .Site.Params.Author.headline }}
|
||||||
|
<h2 class="mt-0 mb-0 text-xl text-neutral-800 dark:text-neutral-300">
|
||||||
|
{{ . | markdownify }}
|
||||||
|
</h2>
|
||||||
|
{{ end }}
|
||||||
|
<div class="mt-3 mb-10 text-2xl">
|
||||||
|
{{ with .Site.Params.Author.links }}
|
||||||
|
<div class="flex flex-wrap">
|
||||||
|
{{ range $links := . }}
|
||||||
|
{{ range $name, $url := $links }}
|
||||||
|
<a
|
||||||
|
class="hover:text-primary-400 text-primary-800 dark:text-primary-200 px-1"
|
||||||
|
href="{{ $url }}"
|
||||||
|
target="_blank"
|
||||||
|
aria-label="{{ $name | title }}"
|
||||||
|
title="{{ $name | title }}"
|
||||||
|
rel="me noopener noreferrer">
|
||||||
|
{{ partial "icon.html" $name }}
|
||||||
|
</a>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="relative flex flex-col w-full px-1 py-1 text-left">
|
||||||
|
<section class="prose dark:prose-invert mx-auto w-full">
|
||||||
|
{{ .Content }}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
{{ partial "recent-articles/main.html" . }}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{{ if .Site.Params.homepage.layoutBackgroundBlur | default false }}
|
||||||
|
<div
|
||||||
|
id="background-blur"
|
||||||
|
class="fixed opacity-0 inset-x-0 top-0 h-full single_hero_background nozoom backdrop-blur-xl bg-neutral-100/75 dark:bg-neutral-800/60"></div>
|
||||||
|
{{ $backgroundBlur := resources.Get "js/background-blur.js" }}
|
||||||
|
{{ $backgroundBlur = $backgroundBlur | resources.Minify | resources.Fingerprint (.Site.Params.fingerprintAlgorithm | default "sha512") }}
|
||||||
|
<script
|
||||||
|
type="text/javascript"
|
||||||
|
src="{{ $backgroundBlur.RelPermalink }}"
|
||||||
|
integrity="{{ $backgroundBlur.Data.Integrity }}"
|
||||||
|
data-blur-id="background-blur"
|
||||||
|
data-image-id="background-image"
|
||||||
|
{{ if $homepageImage }}data-image-url="{{ $homepageImage.RelPermalink }}"{{ end }}></script>
|
||||||
|
{{ end }}
|
||||||
143
layouts/partials/toc.html
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
<details
|
||||||
|
open
|
||||||
|
id="TOCView"
|
||||||
|
class="toc-right mt-0 overflow-y-auto overscroll-contain bf-scrollbar rounded-lg -ms-5 ps-5 pe-2 block lg:block">
|
||||||
|
<summary
|
||||||
|
class="block py-1 text-lg font-semibold cursor-pointer bg-neutral-100 text-neutral-800 -ms-5 ps-5 dark:bg-neutral-700 dark:text-neutral-100 lg:hidden">
|
||||||
|
{{ i18n "article.table_of_contents" }}
|
||||||
|
</summary>
|
||||||
|
<div
|
||||||
|
id="TableOfContents"
|
||||||
|
class="min-w-[220px] py-2 border-dotted border-s-1 -ms-5 ps-5 dark:border-neutral-600">
|
||||||
|
{{/* We use Hugo's native TOC generator so it never disappears */}}
|
||||||
|
{{ .TableOfContents | safeHTML }}
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details class="toc-inside mt-0 overflow-hidden rounded-lg -ms-5 ps-5 lg:hidden">
|
||||||
|
<summary
|
||||||
|
class="py-1 text-lg font-semibold cursor-pointer bg-neutral-100 text-neutral-800 -ms-5 ps-5 dark:bg-neutral-700 dark:text-neutral-100 lg:hidden">
|
||||||
|
{{ i18n "article.table_of_contents" }}
|
||||||
|
</summary>
|
||||||
|
<div
|
||||||
|
class="py-2 border-dotted border-neutral-300 border-s-1 -ms-5 ps-5 dark:border-neutral-600">
|
||||||
|
{{ .TableOfContents | safeHTML }}
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
{{ if .Site.Params.smartTOC }}
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
'use strict'
|
||||||
|
const SCROLL_OFFSET_RATIO = 0.33
|
||||||
|
const TOC_SELECTOR = '#TableOfContents'
|
||||||
|
const ANCHOR_SELECTOR = '.anchor'
|
||||||
|
const TOC_LINK_SELECTOR = 'a[href^="#"]'
|
||||||
|
const NESTED_LIST_SELECTOR = 'li ul'
|
||||||
|
const ACTIVE_CLASS = 'active'
|
||||||
|
let isJumpingToAnchor = false
|
||||||
|
|
||||||
|
// --- NEW LOGIC: Prune .no-toc items before initializing SmartTOC ---
|
||||||
|
function pruneHiddenTOCItems() {
|
||||||
|
// Find all headers on the page with the 'no-toc' class
|
||||||
|
const ignoredHeaders = document.querySelectorAll('h1.no-toc, h2.no-toc, h3.no-toc, h4.no-toc, h5.no-toc, h6.no-toc');
|
||||||
|
|
||||||
|
ignoredHeaders.forEach(header => {
|
||||||
|
if (!header.id) return;
|
||||||
|
|
||||||
|
// Find matching links in both desktop and mobile TOCs
|
||||||
|
const linksToRemove = document.querySelectorAll(`.toc-right a[href="#${header.id}"], .toc-inside a[href="#${header.id}"]`);
|
||||||
|
|
||||||
|
linksToRemove.forEach(link => {
|
||||||
|
const li = link.closest('li');
|
||||||
|
if (li) {
|
||||||
|
const parentUl = li.parentElement;
|
||||||
|
li.remove(); // Remove the item from the TOC
|
||||||
|
|
||||||
|
// If removing this item leaves an empty list, remove the ul too
|
||||||
|
if (parentUl && parentUl.children.length === 0) {
|
||||||
|
parentUl.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getActiveAnchorId(anchors, offsetRatio) {
|
||||||
|
const threshold = window.scrollY + window.innerHeight * offsetRatio
|
||||||
|
const tocLinks = [...document.querySelectorAll('#TableOfContents a[href^="#"]')]
|
||||||
|
const tocIds = new Set(tocLinks.map(link => link.getAttribute('href').substring(1)))
|
||||||
|
|
||||||
|
if (isJumpingToAnchor) {
|
||||||
|
for (let i = 0; i < anchors.length; i++) {
|
||||||
|
const anchor = anchors[i]
|
||||||
|
if (!tocIds.has(anchor.id)) continue
|
||||||
|
const top = anchor.getBoundingClientRect().top + window.scrollY
|
||||||
|
if (Math.abs(window.scrollY - top) < 100) {
|
||||||
|
return anchor.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = anchors.length - 1; i >= 0; i--) {
|
||||||
|
const top = anchors[i].getBoundingClientRect().top + window.scrollY
|
||||||
|
if (top <= threshold && tocIds.has(anchors[i].id)) {
|
||||||
|
return anchors[i].id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return anchors.find(anchor => tocIds.has(anchor.id))?.id || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTOC({ toc, anchors, links, scrollOffset, collapseInactive }) {
|
||||||
|
const activeId = getActiveAnchorId(anchors, scrollOffset)
|
||||||
|
if (!activeId) return
|
||||||
|
|
||||||
|
links.forEach(link => {
|
||||||
|
const isActive = link.getAttribute('href') === `#${activeId}`
|
||||||
|
link.classList.toggle(ACTIVE_CLASS, isActive)
|
||||||
|
if (collapseInactive) {
|
||||||
|
const ul = link.closest('li')?.querySelector('ul')
|
||||||
|
if (ul) ul.style.display = isActive ? '' : 'none'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (collapseInactive) {
|
||||||
|
const activeLink = toc.querySelector(`a[href="#${CSS.escape(activeId)}"]`)
|
||||||
|
let el = activeLink
|
||||||
|
while (el && el !== toc) {
|
||||||
|
if (el.tagName === 'UL') el.style.display = ''
|
||||||
|
if (el.tagName === 'LI') el.querySelector('ul')?.style.setProperty('display', '')
|
||||||
|
el = el.parentElement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initTOC() {
|
||||||
|
// 1. Remove the hidden items first
|
||||||
|
pruneHiddenTOCItems();
|
||||||
|
|
||||||
|
const toc = document.querySelector(TOC_SELECTOR)
|
||||||
|
if (!toc) return
|
||||||
|
const collapseInactive = {{ if site.Params.smartTOCHideUnfocusedChildren }}true{{ else }}false{{ end }}
|
||||||
|
|
||||||
|
// 2. Query the anchors, explicitly ignoring .no-toc headers so scroll-spy ignores them
|
||||||
|
const anchors = [...document.querySelectorAll('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]')].filter(a => !a.classList.contains('no-toc'))
|
||||||
|
const links = [...toc.querySelectorAll(TOC_LINK_SELECTOR)]
|
||||||
|
|
||||||
|
if (collapseInactive) {
|
||||||
|
toc.querySelectorAll(NESTED_LIST_SELECTOR).forEach(ul => ul.style.display = 'none')
|
||||||
|
}
|
||||||
|
|
||||||
|
links.forEach(link => {
|
||||||
|
link.addEventListener('click', () => { isJumpingToAnchor = true })
|
||||||
|
})
|
||||||
|
|
||||||
|
const config = { toc, anchors, links, scrollOffset: SCROLL_OFFSET_RATIO, collapseInactive }
|
||||||
|
window.addEventListener('scroll', () => updateTOC(config), { passive: true })
|
||||||
|
window.addEventListener('hashchange', () => updateTOC(config), { passive: true })
|
||||||
|
updateTOC(config)
|
||||||
|
}
|
||||||
|
document.readyState === 'loading' ? document.addEventListener('DOMContentLoaded', initTOC) : initTOC()
|
||||||
|
})()
|
||||||
|
</script>
|
||||||
|
{{ end }}
|
||||||
1
layouts/shortcodes/rawhtml.html
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{{ .Inner }}
|
||||||
4
layouts/shortcodes/video.html
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<video width="100%" controls preload="metadata">
|
||||||
|
<source src="{{ .Get "src" }}" type="video/mp4">
|
||||||
|
Your browser does not support the video tag.
|
||||||
|
</video>
|
||||||