Merge pull request 'New Membership Page and other changes' (#4) from membership-page into main

Reviewed-on: #4
This commit is contained in:
Kumi 2024-05-30 10:15:09 +00:00 committed by git.private.coffee
commit 99a5f96157
Signed by: git.private.coffee
GPG key ID: 60CFD1C72200C214
33 changed files with 860 additions and 421 deletions

View file

@ -24,3 +24,8 @@ The website will be available at `http://localhost:9810`.
This project is licensed under the MIT License - see the [LICENSE](LICENSE)
file for details.
## Attribution
This website is built using the [Bootstrap](https://getbootstrap.com) framework
and [Phosphor Icons](https://phosphoricons.com).

View file

@ -85,6 +85,48 @@ h5 {
margin-top: 10px;
}
.currency-col {
width: 200px;
white-space: nowrap;
text-align: right;
}
.table-transparency td:not(:first-child) {
text-align: right;
}
.section {
padding: 20px 0;
border-bottom: 1px solid #e0e0e0;
}
.alert-warning {
background-color: #fff3cd;
border-color: #ffeeba;
color: #856404;
padding: 15px;
margin-bottom: 20px;
border-radius: 4px;
}
.alert-warning .alert-link {
color: #856404;
font-weight: bold;
text-decoration: underline;
}
.alert-warning .alert-link:hover {
color: #604c2e;
}
.bs-icon.bs-icon-primary svg {
fill: var(--bs-primary-bg-subtle);
}
.bs-icon.bs-icon-lg svg {
fill: var(--bs-primary)
}
/* Responsive Styles */
@media (max-width: 768px) {
.navbar .container {

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M128,96H232a8,8,0,0,1,0,16H128a8,8,0,0,1,0-16Zm104,32H128a8,8,0,0,0,0,16H232a8,8,0,0,0,0-16Zm0,32H80a8,8,0,0,0,0,16H232a8,8,0,0,0,0-16Zm0,32H80a8,8,0,0,0,0,16H232a8,8,0,0,0,0-16ZM96,144a8,8,0,0,0,0-16H88V64h32v8a8,8,0,0,0,16,0V56a8,8,0,0,0-8-8H32a8,8,0,0,0-8,8V72a8,8,0,0,0,16,0V64H72v64H64a8,8,0,0,0,0,16Z"></path></svg>

After

Width:  |  Height:  |  Size: 430 B

1
assets/dist/icons/calendar.svg vendored Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M208,32H184V24a8,8,0,0,0-16,0v8H88V24a8,8,0,0,0-16,0v8H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM72,48v8a8,8,0,0,0,16,0V48h80v8a8,8,0,0,0,16,0V48h24V80H48V48ZM208,208H48V96H208V208Zm-96-88v64a8,8,0,0,1-16,0V132.94l-4.42,2.22a8,8,0,0,1-7.16-14.32l16-8A8,8,0,0,1,112,120Zm59.16,30.45L152,176h16a8,8,0,0,1,0,16H136a8,8,0,0,1-6.4-12.8l28.78-38.37A8,8,0,1,0,145.07,132a8,8,0,1,1-13.85-8A24,24,0,0,1,176,136,23.76,23.76,0,0,1,171.16,150.45Z"></path></svg>

After

Width:  |  Height:  |  Size: 604 B

1
assets/dist/icons/chats.svg vendored Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M216,80H184V48a16,16,0,0,0-16-16H40A16,16,0,0,0,24,48V176a8,8,0,0,0,13,6.22L72,154V184a16,16,0,0,0,16,16h93.59L219,230.22a8,8,0,0,0,5,1.78,8,8,0,0,0,8-8V96A16,16,0,0,0,216,80ZM66.55,137.78,40,159.25V48H168v88H71.58A8,8,0,0,0,66.55,137.78ZM216,207.25l-26.55-21.47a8,8,0,0,0-5-1.78H88V152h80a16,16,0,0,0,16-16V96h32Z"></path></svg>

After

Width:  |  Height:  |  Size: 438 B

1
assets/dist/icons/coffee.svg vendored Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M80,56V24a8,8,0,0,1,16,0V56a8,8,0,0,1-16,0Zm40,8a8,8,0,0,0,8-8V24a8,8,0,0,0-16,0V56A8,8,0,0,0,120,64Zm32,0a8,8,0,0,0,8-8V24a8,8,0,0,0-16,0V56A8,8,0,0,0,152,64Zm96,56v8a40,40,0,0,1-37.51,39.91,96.59,96.59,0,0,1-27,40.09H208a8,8,0,0,1,0,16H32a8,8,0,0,1,0-16H56.54A96.3,96.3,0,0,1,24,136V88a8,8,0,0,1,8-8H208A40,40,0,0,1,248,120ZM200,96H40v40a80.27,80.27,0,0,0,45.12,72h69.76A80.27,80.27,0,0,0,200,136Zm32,24a24,24,0,0,0-16-22.62V136a95.78,95.78,0,0,1-1.2,15A24,24,0,0,0,232,128Z"></path></svg>

After

Width:  |  Height:  |  Size: 600 B

1
assets/dist/icons/database.svg vendored Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M128,24C74.17,24,32,48.6,32,80v96c0,31.4,42.17,56,96,56s96-24.6,96-56V80C224,48.6,181.83,24,128,24Zm80,104c0,9.62-7.88,19.43-21.61,26.92C170.93,163.35,150.19,168,128,168s-42.93-4.65-58.39-13.08C55.88,147.43,48,137.62,48,128V111.36c17.06,15,46.23,24.64,80,24.64s62.94-9.68,80-24.64ZM69.61,53.08C85.07,44.65,105.81,40,128,40s42.93,4.65,58.39,13.08C200.12,60.57,208,70.38,208,80s-7.88,19.43-21.61,26.92C170.93,115.35,150.19,120,128,120s-42.93-4.65-58.39-13.08C55.88,99.43,48,89.62,48,80S55.88,60.57,69.61,53.08ZM186.39,202.92C170.93,211.35,150.19,216,128,216s-42.93-4.65-58.39-13.08C55.88,195.43,48,185.62,48,176V159.36c17.06,15,46.23,24.64,80,24.64s62.94-9.68,80-24.64V176C208,185.62,200.12,195.43,186.39,202.92Z"></path></svg>

After

Width:  |  Height:  |  Size: 834 B

1
assets/dist/icons/envelope.svg vendored Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M224,48H32a8,8,0,0,0-8,8V192a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A8,8,0,0,0,224,48Zm-96,85.15L52.57,64H203.43ZM98.71,128,40,181.81V74.19Zm11.84,10.85,12,11.05a8,8,0,0,0,10.82,0l12-11.05,58,53.15H52.57ZM157.29,128,216,74.18V181.82Z"></path></svg>

After

Width:  |  Height:  |  Size: 360 B

1
assets/dist/icons/git-branch.svg vendored Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M232,64a32,32,0,1,0-40,31v17a8,8,0,0,1-8,8H96a23.84,23.84,0,0,0-8,1.38V95a32,32,0,1,0-16,0v66a32,32,0,1,0,16,0V144a8,8,0,0,1,8-8h88a24,24,0,0,0,24-24V95A32.06,32.06,0,0,0,232,64ZM64,64A16,16,0,1,1,80,80,16,16,0,0,1,64,64ZM96,192a16,16,0,1,1-16-16A16,16,0,0,1,96,192ZM200,80a16,16,0,1,1,16-16A16,16,0,0,1,200,80Z"></path></svg>

After

Width:  |  Height:  |  Size: 435 B

1
assets/dist/icons/images.svg vendored Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M216,40H72A16,16,0,0,0,56,56V72H40A16,16,0,0,0,24,88V200a16,16,0,0,0,16,16H184a16,16,0,0,0,16-16V184h16a16,16,0,0,0,16-16V56A16,16,0,0,0,216,40ZM72,56H216v62.75l-10.07-10.06a16,16,0,0,0-22.63,0l-20,20-44-44a16,16,0,0,0-22.62,0L72,109.37ZM184,200H40V88H56v80a16,16,0,0,0,16,16H184Zm32-32H72V132l36-36,49.66,49.66a8,8,0,0,0,11.31,0L194.63,120,216,141.38V168ZM160,84a12,12,0,1,1,12,12A12,12,0,0,1,160,84Z"></path></svg>

After

Width:  |  Height:  |  Size: 525 B

1
assets/dist/icons/leaf.svg vendored Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M223.45,40.07a8,8,0,0,0-7.52-7.52C139.8,28.08,78.82,51,52.82,94a87.09,87.09,0,0,0-12.76,49c.57,15.92,5.21,32,13.79,47.85l-19.51,19.5a8,8,0,0,0,11.32,11.32l19.5-19.51C81,210.73,97.09,215.37,113,215.94q1.67.06,3.33.06A86.93,86.93,0,0,0,162,203.18C205,177.18,227.93,116.21,223.45,40.07ZM153.75,189.5c-22.75,13.78-49.68,14-76.71.77l88.63-88.62a8,8,0,0,0-11.32-11.32L65.73,179c-13.19-27-13-54,.77-76.71,22.09-36.47,74.6-56.44,141.31-54.06C210.2,114.89,190.22,167.41,153.75,189.5Z"></path></svg>

After

Width:  |  Height:  |  Size: 598 B

1
assets/dist/icons/lightbulb.svg vendored Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M176,232a8,8,0,0,1-8,8H88a8,8,0,0,1,0-16h80A8,8,0,0,1,176,232Zm40-128a87.55,87.55,0,0,1-33.64,69.21A16.24,16.24,0,0,0,176,186v6a16,16,0,0,1-16,16H96a16,16,0,0,1-16-16v-6a16,16,0,0,0-6.23-12.66A87.59,87.59,0,0,1,40,104.49C39.74,56.83,78.26,17.14,125.88,16A88,88,0,0,1,216,104Zm-16,0a72,72,0,0,0-73.74-72c-39,.92-70.47,33.39-70.26,72.39a71.65,71.65,0,0,0,27.64,56.3A32,32,0,0,1,96,186v6h64v-6a32.15,32.15,0,0,1,12.47-25.35A71.65,71.65,0,0,0,200,104Zm-16.11-9.34a57.6,57.6,0,0,0-46.56-46.55,8,8,0,0,0-2.66,15.78c16.57,2.79,30.63,16.85,33.44,33.45A8,8,0,0,0,176,104a9,9,0,0,0,1.35-.11A8,8,0,0,0,183.89,94.66Z"></path></svg>

After

Width:  |  Height:  |  Size: 728 B

1
assets/dist/icons/link.svg vendored Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M240,88.23a54.43,54.43,0,0,1-16,37L189.25,160a54.27,54.27,0,0,1-38.63,16h-.05A54.63,54.63,0,0,1,96,119.84a8,8,0,0,1,16,.45A38.62,38.62,0,0,0,150.58,160h0a38.39,38.39,0,0,0,27.31-11.31l34.75-34.75a38.63,38.63,0,0,0-54.63-54.63l-11,11A8,8,0,0,1,135.7,59l11-11A54.65,54.65,0,0,1,224,48,54.86,54.86,0,0,1,240,88.23ZM109,185.66l-11,11A38.41,38.41,0,0,1,70.6,208h0a38.63,38.63,0,0,1-27.29-65.94L78,107.31A38.63,38.63,0,0,1,144,135.71a8,8,0,0,0,16,.45A54.86,54.86,0,0,0,144,96a54.65,54.65,0,0,0-77.27,0L32,130.75A54.62,54.62,0,0,0,70.56,224h0a54.28,54.28,0,0,0,38.64-16l11-11A8,8,0,0,0,109,185.66Z"></path></svg>

After

Width:  |  Height:  |  Size: 714 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M229.66,218.34l-50.07-50.06a88.11,88.11,0,1,0-11.31,11.31l50.06,50.07a8,8,0,0,0,11.32-11.32ZM40,112a72,72,0,1,1,72,72A72.08,72.08,0,0,1,40,112Z"></path></svg>

After

Width:  |  Height:  |  Size: 267 B

1
assets/dist/icons/markdown-logo.svg vendored Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M232,48H24A16,16,0,0,0,8,64V192a16,16,0,0,0,16,16H232a16,16,0,0,0,16-16V64A16,16,0,0,0,232,48Zm0,144H24V64H232V192ZM128,104v48a8,8,0,0,1-16,0V123.31L93.66,141.66a8,8,0,0,1-11.32,0L64,123.31V152a8,8,0,0,1-16,0V104a8,8,0,0,1,13.66-5.66L88,124.69l26.34-26.35A8,8,0,0,1,128,104Zm77.66,18.34a8,8,0,0,1,0,11.32l-24,24a8,8,0,0,1-11.32,0l-24-24a8,8,0,0,1,11.32-11.32L168,132.69V104a8,8,0,0,1,16,0v28.69l10.34-10.35A8,8,0,0,1,205.66,122.34Z"></path></svg>

After

Width:  |  Height:  |  Size: 555 B

1
assets/dist/icons/mastodon-logo.svg vendored Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M184,32H72A40,40,0,0,0,32,72V192a40,40,0,0,0,40,40h88a8,8,0,0,0,0-16H72a24,24,0,0,1-24-24v-8H184a40,40,0,0,0,40-40V72A40,40,0,0,0,184,32Zm24,112a24,24,0,0,1-24,24H48V72A24,24,0,0,1,72,48H184a24,24,0,0,1,24,24Zm-24-40v32a8,8,0,0,1-16,0V104a16,16,0,0,0-32,0v32a8,8,0,0,1-16,0V104a16,16,0,0,0-32,0v32a8,8,0,0,1-16,0V104a32,32,0,0,1,56-21.13A32,32,0,0,1,184,104Z"></path></svg>

After

Width:  |  Height:  |  Size: 482 B

1
assets/dist/icons/matrix-logo.svg vendored Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M72,216a8,8,0,0,1-8,8H40a8,8,0,0,1-8-8V40a8,8,0,0,1,8-8H64a8,8,0,0,1,0,16H48V208H64A8,8,0,0,1,72,216ZM216,32H192a8,8,0,0,0,0,16h16V208H192a8,8,0,0,0,0,16h24a8,8,0,0,0,8-8V40A8,8,0,0,0,216,32Zm-32,88a32,32,0,0,0-56-21.13,31.93,31.93,0,0,0-40.71-6.15A8,8,0,0,0,72,96v64a8,8,0,0,0,16,0V120a16,16,0,0,1,32,0v40a8,8,0,0,0,16,0V120a16,16,0,0,1,32,0v40a8,8,0,0,0,16,0Z"></path></svg>

After

Width:  |  Height:  |  Size: 485 B

1
assets/dist/icons/network.svg vendored Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M232,112H136V88h8a16,16,0,0,0,16-16V40a16,16,0,0,0-16-16H112A16,16,0,0,0,96,40V72a16,16,0,0,0,16,16h8v24H24a8,8,0,0,0,0,16H56v32H48a16,16,0,0,0-16,16v32a16,16,0,0,0,16,16H80a16,16,0,0,0,16-16V176a16,16,0,0,0-16-16H72V128H184v32h-8a16,16,0,0,0-16,16v32a16,16,0,0,0,16,16h32a16,16,0,0,0,16-16V176a16,16,0,0,0-16-16h-8V128h32a8,8,0,0,0,0-16ZM112,40h32V72H112ZM80,208H48V176H80Zm128,0H176V176h32Z"></path></svg>

After

Width:  |  Height:  |  Size: 516 B

1
assets/dist/icons/pencil-ruler.svg vendored Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M208,32H160a16,16,0,0,0-16,16V208a16,16,0,0,0,16,16h48a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32Zm0,176H160V176h24a8,8,0,0,0,0-16H160V136h24a8,8,0,0,0,0-16H160V96h24a8,8,0,0,0,0-16H160V48h48V208ZM77.66,26.34a8,8,0,0,0-11.32,0l-32,32A8,8,0,0,0,32,64V208a16,16,0,0,0,16,16H96a16,16,0,0,0,16-16V64a8,8,0,0,0-2.34-5.66ZM48,176V80H64v96ZM80,80H96v96H80ZM72,43.31,92.69,64H51.31ZM48,208V192H96v16Z"></path></svg>

After

Width:  |  Height:  |  Size: 513 B

1
assets/dist/icons/video.svg vendored Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M164.44,105.34l-48-32A8,8,0,0,0,104,80v64a8,8,0,0,0,12.44,6.66l48-32a8,8,0,0,0,0-13.32ZM120,129.05V95l25.58,17ZM216,40H40A16,16,0,0,0,24,56V168a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A16,16,0,0,0,216,40Zm0,128H40V56H216V168Zm16,40a8,8,0,0,1-8,8H32a8,8,0,0,1,0-16H224A8,8,0,0,1,232,208Z"></path></svg>

After

Width:  |  Height:  |  Size: 412 B

1
assets/dist/icons/webcam.svg vendored Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M168,104a40,40,0,1,0-40,40A40,40,0,0,0,168,104Zm-64,0a24,24,0,1,1,24,24A24,24,0,0,1,104,104Zm120,96H136V183.6a80,80,0,1,0-16,0V200H32a8,8,0,0,0,0,16H224a8,8,0,0,0,0-16ZM64,104a64,64,0,1,1,64,64A64.07,64.07,0,0,1,64,104Z"></path></svg>

After

Width:  |  Height:  |  Size: 343 B

33
data/finances.json Normal file
View file

@ -0,0 +1,33 @@
{
"2024": {
"4": {
"Membership Fees": {
"EUR": 365
},
"Donations": {},
"Server Costs": {
"EUR": -216.57
},
"Domain Names": {},
"Operating Expenses": {
"EUR": -36.10
},
"Conversions": {}
},
"5": {
"Membership Fees": {
"EUR": 390
},
"Donations": {
"BTC": 0.000434,
"XMR": 0.447661805527
},
"Server Costs": {
"EUR": -430.04
},
"Domain Names": {},
"Operating Expenses": {},
"Conversions": {}
}
}
}

View file

@ -12,6 +12,7 @@
"url": "https://element.private.coffee"
}
],
"icon": "matrix-logo",
"exclude_from_index": false,
"exclude_from_simple": true
},
@ -27,6 +28,7 @@
"url": "https://cryptpad.private.coffee"
}
],
"icon": "article-ny-times",
"exclude_from_index": false,
"exclude_from_simple": false
},
@ -42,6 +44,7 @@
"url": "https://piped.private.coffee"
}
],
"icon": "video",
"exclude_from_index": false,
"exclude_from_simple": false
},
@ -57,6 +60,7 @@
"url": "https://myip.coffee"
}
],
"icon": "network",
"exclude_from_index": false,
"exclude_from_simple": false
},
@ -72,6 +76,7 @@
"url": "https://invidious.private.coffee"
}
],
"icon": "video",
"exclude_from_index": false,
"exclude_from_simple": false
},
@ -91,6 +96,7 @@
"url": "https://skrt.social"
}
],
"icon": "mastodon-logo",
"exclude_from_index": false,
"exclude_from_simple": false
},
@ -106,6 +112,7 @@
"url": "https://git.private.coffee"
}
],
"icon": "git-branch",
"exclude_from_index": false,
"exclude_from_simple": false
},
@ -121,6 +128,7 @@
"url": "https://bbb.private.coffee"
}
],
"icon": "webcam",
"exclude_from_index": false,
"exclude_from_simple": false
},
@ -136,6 +144,7 @@
"url": "https://hedgedoc.private.coffee"
}
],
"icon": "markdown-logo",
"exclude_from_index": false,
"exclude_from_simple": false
},
@ -151,6 +160,7 @@
"url": "https://gothub.private.coffee"
}
],
"icon": "git-branch",
"exclude_from_index": false,
"exclude_from_simple": false
},
@ -166,6 +176,7 @@
"url": "https://redlib.private.coffee"
}
],
"icon": "chats",
"exclude_from_index": false,
"exclude_from_simple": false
},
@ -181,6 +192,7 @@
"url": "https://alltube.private.coffee"
}
],
"icon": "video",
"exclude_from_index": false,
"exclude_from_simple": false
},
@ -196,6 +208,7 @@
"url": "https://structables.private.coffee"
}
],
"icon": "lightbulb",
"exclude_from_index": false,
"exclude_from_simple": false
},
@ -211,6 +224,7 @@
"url": "https://nocodb.private.coffee"
}
],
"icon": "database",
"exclude_from_index": true,
"exclude_from_simple": true
},
@ -226,6 +240,7 @@
"url": "https://penpot.private.coffee"
}
],
"icon": "pencil-ruler",
"exclude_from_index": false,
"exclude_from_simple": false
},
@ -241,6 +256,7 @@
"url": "https://pcof.fi"
}
],
"icon": "link",
"exclude_from_index": false,
"exclude_from_simple": false
},
@ -256,6 +272,7 @@
"url": "https://rallly.private.coffee"
}
],
"icon": "calendar",
"exclude_from_index": true,
"exclude_from_simple": true
},
@ -271,6 +288,7 @@
"url": "https://librey.private.coffee"
}
],
"icon": "magnifying-glass",
"exclude_from_index": false,
"exclude_from_simple": false
},
@ -286,6 +304,7 @@
"url": "https://overleaf.private.coffee"
}
],
"icon": "leaf",
"exclude_from_index": false,
"exclude_from_simple": false
},
@ -301,44 +320,9 @@
"url": "https://binternet.private.coffee"
}
],
"icon": "images",
"exclude_from_index": false,
"exclude_from_simple": false
},
{
"name": "Nitter",
"url": "https://nitter.private.coffee",
"short_description": "Nitter is a privacy-friendly alternative front-end to Twitter.",
"long_description": "Nitter is a privacy-friendly alternative front-end to Twitter. It allows you to browse Twitter without being tracked.",
"status": "NOK",
"links": [
{
"name": "Go to Nitter",
"url": "https://nitter.private.coffee",
"alternatives": [
{
"name": "Tor (.onion)",
"url": "http://nitter.coffee2m3bjsrrqqycx6ghkxrnejl2q6nl7pjw2j4clchjj6uk5zozad.onion/"
}
]
}
],
"exclude_from_index": true,
"exclude_from_simple": true
},
{
"name": "Proxigram",
"url": "https://proxigram.private.coffee",
"short_description": "Proxigram is a privacy-friendly alternative front-end to Instagram.",
"long_description": "Proxigram is a privacy-friendly alternative front-end to Instagram. It allows you to browse Instagram without being tracked.",
"status": "NOK",
"links": [
{
"name": "Go to Proxigram",
"url": "https://proxigram.private.coffee"
}
],
"exclude_from_index": true,
"exclude_from_simple": true
}
]
}

201
helpers/finances.py Normal file
View file

@ -0,0 +1,201 @@
from decimal import Decimal
def get_latest_month(data):
years = sorted(data.keys())
latest_year = years[-1]
months = sorted(data[latest_year].keys())
latest_month = months[-1]
return int(latest_month), int(latest_year)
def get_transparency_data(data, year=None, month=None):
if year is None:
year = max(data.keys())
if month is None:
month = max(data[year].keys())
assert year in data, f"Year {year} not found in data"
assert month in data[year], f"Month {month}-{year} not found in data"
# Initialize balances
balances = {}
incomes = {}
expenses = {}
start_balance = {}
end_balance = {}
for y in sorted(data.keys()):
if int(y) > int(year):
break
for m in sorted(data[y].keys()):
if int(y) == int(year) and int(m) > int(month):
break
# If the month is the one we are interested in, capture the start balance
if int(y) == int(year) and int(m) == int(month):
start_balance = {k: Decimal(v) for k, v in balances.items()}
for category in data[y][m]:
for currency, amount in data[y][m][category].items():
if currency not in balances:
balances[currency] = Decimal(0)
balances[currency] += Decimal(str(amount))
# Track incomes and expenses
if int(y) == int(year) and int(m) == int(month):
if Decimal(str(amount)) > 0:
if category not in incomes:
incomes[category] = {}
if currency not in incomes[category]:
incomes[category][currency] = Decimal(0)
incomes[category][currency] += Decimal(str(amount))
else:
if category not in expenses:
expenses[category] = {}
if currency not in expenses[category]:
expenses[category][currency] = Decimal(0)
expenses[category][currency] += Decimal(str(amount))
# If the month is the one we are interested in, capture the end balance
if int(y) == int(year) and int(m) == int(month):
end_balance = {k: Decimal(v) for k, v in balances.items()}
# Calculate accumulated sums of incomes and expenses
accumulated_incomes = {
currency: sum(incomes[cat].get(currency, Decimal(0)) for cat in incomes)
for currency in balances
}
accumulated_expenses = {
currency: sum(expenses[cat].get(currency, Decimal(0)) for cat in expenses)
for currency in balances
}
return {
"start_balance": start_balance,
"end_balance": end_balance,
"incomes": incomes,
"expenses": expenses,
"accumulated_incomes": accumulated_incomes,
"accumulated_expenses": accumulated_expenses,
}
def generate_transparency_table(result, currencies=None):
def extract_currencies(data):
return ["EUR"] + (
list(
set(
list(data["start_balance"].keys())
+ list(data["end_balance"].keys())
+ list(data["accumulated_incomes"].keys())
+ list(data["accumulated_expenses"].keys())
)
- {"EUR"}
)
)
def format_currency(value, currency):
if currency == "EUR":
return f"{value:,.2f}"
elif currency in ["BTC", "ETH", "XMR"]:
return f"{value:,.9f} {currency}"
else:
return f"{value} {currency}"
def format_value(value, currency):
if value == 0:
return f"{format_currency(value, currency)}"
elif value > 0:
return f"+ {format_currency(value, currency)}"
else:
return f"- {format_currency(abs(value), currency)}"
html = """
<table class="table table-bordered table-transparency">
<thead class="table-light">
<tr>
<th scope="col">Category</th>
"""
if currencies is None:
currencies = extract_currencies(result)
# Add currency headers
for currency in currencies:
if currency == "EUR":
html += '<th class="currency-col" scope="col">Euros (€)</th>'
elif currency == "BTC":
html += '<th class="currency-col" scope="col">Bitcoin (BTC)</th>'
elif currency == "ETH":
html += '<th class="currency-col" scope="col">Ethereum (ETH)</th>'
elif currency == "XMR":
html += '<th class="currency-col" scope="col">Monero (XMR)</th>'
else:
html += f'<th class="currency-col" scope="col">{currency}</th>'
html += """
</tr>
</thead>
<tbody>
"""
# Add start balance row
html += "<tr><td>Account Balance (start of month)</td>"
for currency in currencies:
value = result["start_balance"].get(currency, Decimal(0))
html += f"<td>{format_value(value, currency)}</td>"
html += "</tr>"
# Add income rows
for category, transactions in result["incomes"].items():
html += f"<tr><td>{category}</td>"
for currency in currencies:
value = transactions.get(currency, "")
if value != "":
html += f"<td>{format_value(value, currency)}</td>"
else:
html += "<td></td>"
html += "</tr>"
# Add expense rows
for category, transactions in result["expenses"].items():
html += f"<tr><td>{category}</td>"
for currency in currencies:
value = transactions.get(currency, "")
if value != "":
html += f"<td>{format_value(value, currency)}</td>"
else:
html += "<td></td>"
html += "</tr>"
# Add total income row
html += '<tr class="table-secondary"><td><b>Total Income</b></td>'
for currency in currencies:
value = result["accumulated_incomes"].get(currency, Decimal(0))
html += f"<td><b>{format_value(value, currency)}</b></td>"
html += "</tr>"
# Add total expenses row
html += '<tr class="table-secondary"><td><b>Total Expenses</b></td>'
for currency in currencies:
value = result["accumulated_expenses"].get(currency, Decimal(0))
html += f"<td><b>{format_value(value, currency)}</b></td>"
html += "</tr>"
# Add end balance row
html += '<tr class="table-secondary"><td><b>Account Balance (end of month)</b></td>'
for currency in currencies:
value = result["end_balance"].get(currency, Decimal(0))
html += f"<td><b>{format_value(value, currency)}</b></td>"
html += "</tr>"
html += """
</tbody>
</table>
"""
return html

77
main.py
View file

@ -3,9 +3,17 @@ from jinja2 import TemplateNotFound
import json
import pathlib
import os
import datetime
from argparse import ArgumentParser
from helpers.finances import (
generate_transparency_table,
get_transparency_data,
get_latest_month,
)
app = Flask(__name__)
@ -18,18 +26,79 @@ def send_assets(path):
@app.route("/<path:path>.html")
def catch_all(path):
try:
services = json.loads(
(pathlib.Path(__file__).parent / "services.json").read_text()
)
return render_template(f"{path}.html", services=services)
kwargs = {}
if app.development_mode:
kwargs.update(
{
"warning": render_template("prod-warning.html"),
}
)
if path in (
"index",
"simple",
):
services = json.loads(
(pathlib.Path(__file__).parent / "data" / "services.json").read_text()
)
kwargs.update(
{
"services": services,
}
)
if path == "membership":
finances = json.loads(
(pathlib.Path(__file__).parent / "data" / "finances.json").read_text()
)
finances_month, finances_year = get_latest_month(finances)
finances_period = datetime.date(finances_year, finances_month, 1)
finances_period_str = finances_period.strftime("%B %Y")
finances_table = generate_transparency_table(
get_transparency_data(finances)
)
kwargs.update(
{
"finances": finances_table,
"finances_period": finances_period_str,
}
)
return render_template(f"{path}.html", **kwargs)
except TemplateNotFound:
return "404 Not Found", 404
app.development_mode = False
if os.environ.get("PRIVATECOFFEE_DEV"):
app.development_mode = True
def icon(icon_name):
file = send_from_directory("assets", f"dist/icons/{icon_name}.svg")
try:
file_content = file.response.file.read().decode("utf-8")
except AttributeError:
file_content = file.response.read().decode("utf-8")
return file_content
app.add_template_filter(icon)
if __name__ == "__main__":
parser = ArgumentParser(description="Run the private.coffee web server.")
parser.add_argument("--port", type=int, default=9810)
parser.add_argument("--debug", action="store_true")
parser.add_argument("--dev", action="store_true")
args = parser.parse_args()
app.development_mode = args.dev or app.development_mode
app.run(port=args.port, debug=args.debug)

View file

@ -51,6 +51,7 @@
</ul><a class="btn btn-primary shadow navbar-btn" role="button" href="/membership.html">JOIN &amp; REBEL</a>
</div>
</nav>
{% if warning %}{{ warning|safe }}{% endif %}
{% block content %}{% endblock %}
<footer class="bg-primary-gradient">
<div class="container py-4 py-lg-5">
@ -74,26 +75,12 @@
<div class="fw-bold d-flex align-items-center mb-2">
<span
class="bs-icon-sm bs-icon-circle bs-icon-primary d-flex justify-content-center align-items-center bs-icon me-2"
><svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
fill="currentColor"
viewBox="0 0 16 16"
class="bi bi-bezier"
>
<path
fill-rule="evenodd"
d="M0 10.5A1.5 1.5 0 0 1 1.5 9h1A1.5 1.5 0 0 1 4 10.5v1A1.5 1.5 0 0 1 2.5 13h-1A1.5 1.5 0 0 1 0 11.5zm1.5-.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5zm10.5.5A1.5 1.5 0 0 1 13.5 9h1a1.5 1.5 0 0 1 1.5 1.5v1a1.5 1.5 0 0 1-1.5 1.5h-1a1.5 1.5 0 0 1-1.5-1.5zm1.5-.5a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5zM6 4.5A1.5 1.5 0 0 1 7.5 3h1A1.5 1.5 0 0 1 10 4.5v1A1.5 1.5 0 0 1 8.5 7h-1A1.5 1.5 0 0 1 6 5.5zM7.5 4a.5.5 0 0 0-.5.5v1a.5.5 0 0 0 .5.5h1a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5z"
></path>
<path
d="M6 4.5H1.866a1 1 0 1 0 0 1h2.668A6.517 6.517 0 0 0 1.814 9H2.5c.123 0 .244.015.358.043a5.517 5.517 0 0 1 3.185-3.185A1.503 1.503 0 0 1 6 5.5zm3.957 1.358A1.5 1.5 0 0 0 10 5.5v-1h4.134a1 1 0 1 1 0 1h-2.668a6.517 6.517 0 0 1 2.72 3.5H13.5c-.123 0-.243.015-.358.043a5.517 5.517 0 0 0-3.185-3.185z"
></path></svg></span
>{{ "coffee" | icon | safe }}</span
><span>Private.coffee</span>
</div>
<p class="text-muted">
Private.coffee is a collection of services that respect your
privacy.
Private.coffee is a privacy-focused non-profit association, dedicated
to supporting privacy and digital sovereignty.
</p>
</div>
</div>

View file

@ -29,10 +29,9 @@
<div class="col mb-5">
<div class="card shadow-sm">
<div class="card-body px-4 py-5 px-md-5">
<div class="bs-icon-lg d-flex justify-content-center align-items-center mb-3 bs-icon" style="top: 1rem;right: 1rem;position: absolute;"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" class="bi bi-chat-text text-primary">
<path d="M2.678 11.894a1 1 0 0 1 .287.801 10.97 10.97 0 0 1-.398 2c1.395-.323 2.247-.697 2.634-.893a1 1 0 0 1 .71-.074A8.06 8.06 0 0 0 8 14c3.996 0 7-2.807 7-6 0-3.192-3.004-6-7-6S1 4.808 1 8c0 1.468.617 2.83 1.678 3.894m-.493 3.905a21.682 21.682 0 0 1-.713.129c-.2.032-.352-.176-.273-.362a9.68 9.68 0 0 0 .244-.637l.003-.01c.248-.72.45-1.548.524-2.319C.743 11.37 0 9.76 0 8c0-3.866 3.582-7 8-7s8 3.134 8 7-3.582 7-8 7a9.06 9.06 0 0 1-2.347-.306c-.52.263-1.639.742-3.468 1.105z"></path>
<path d="M4 5.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5M4 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 4 8m0 2.5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5"></path>
</svg></div>
<div class="bs-icon-lg d-flex justify-content-center align-items-center mb-3 bs-icon" style="top: 1rem;right: 1rem;position: absolute;">
{% if service.icon %}{{ service.icon | icon | safe }}{% endif %}
</div>
<h5 class="fw-bold card-title">{{ service.name }}</h5>
<p class="text-muted card-text mb-4">{{ service.long_description }}</p>
{% for link in service.links %}
@ -92,9 +91,7 @@
<div class="col-md-4 col-xl-4 d-flex justify-content-center justify-content-xl-start">
<div class="d-flex flex-wrap flex-md-column justify-content-md-start align-items-md-start h-100">
<div class="d-flex align-items-center p-3">
<div class="bs-icon-md bs-icon-circle bs-icon-primary shadow d-flex flex-shrink-0 justify-content-center align-items-center d-inline-block bs-icon bs-icon-md"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" class="bi bi-envelope">
<path d="M0 4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm2-1a1 1 0 0 0-1 1v.217l7 4.2 7-4.2V4a1 1 0 0 0-1-1zm13 2.383-4.708 2.825L15 11.105zm-.034 6.876-5.64-3.471L8 9.583l-1.326-.795-5.64 3.47A1 1 0 0 0 2 13h12a1 1 0 0 0 .966-.741M1 11.105l4.708-2.897L1 5.383z"></path>
</svg></div>
<div class="bs-icon-md bs-icon-circle bs-icon-primary shadow d-flex flex-shrink-0 justify-content-center align-items-center d-inline-block bs-icon bs-icon-md">{{ "envelope" | icon | safe }}</div>
<div class="px-2">
<h6 class="fw-bold mb-0">Email</h6>
<p class="text-muted mb-0">support@private.coffee</p>
@ -107,9 +104,7 @@
<div class="col-md-4 col-xl-4 d-flex justify-content-center justify-content-xl-start">
<div class="d-flex flex-wrap flex-md-column justify-content-md-start align-items-md-start h-100">
<div class="d-flex align-items-center p-3">
<div class="bs-icon-md bs-icon-circle bs-icon-primary shadow d-flex flex-shrink-0 justify-content-center align-items-center d-inline-block bs-icon bs-icon-md"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" class="bi bi-envelope">
<path d="M0 4a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm2-1a1 1 0 0 0-1 1v.217l7 4.2 7-4.2V4a1 1 0 0 0-1-1zm13 2.383-4.708 2.825L15 11.105zm-.034 6.876-5.64-3.471L8 9.583l-1.326-.795-5.64 3.47A1 1 0 0 0 2 13h12a1 1 0 0 0 .966-.741M1 11.105l4.708-2.897L1 5.383z"></path>
</svg></div>
<div class="bs-icon-md bs-icon-circle bs-icon-primary shadow d-flex flex-shrink-0 justify-content-center align-items-center d-inline-block bs-icon bs-icon-md">{{ "matrix-logo" | icon | safe }}</div>
<div class="px-2">
<h6 class="fw-bold mb-0">Matrix</h6>
<p class="text-muted mb-0"><a href="https://matrix.pcof.fi/#/#private.coffee:private.coffee">#private.coffee:private.coffee</a></p>

View file

@ -1,7 +1,7 @@
{% extends "base.html" %}
{% block title %}Legal Notice{% endblock %}
{% block content %}
<section class="bg-primary-gradient">
<section>
<div class="container py-5">
<div class="row align-items-center">
<div class="col">
@ -20,7 +20,6 @@
</div>
<div class="container">
<h1>Legal Notice</h1>
<p>This is a legal notice for the website private.coffee</p>
<p>The website private.coffee is run by the following entity:</p>
<address>

View file

@ -1,56 +1,116 @@
{% extends "base.html" %}
{% block title %}Membership / Donations{% endblock %}
{% extends "base.html" %} {% block title %}Membership / Donations{% endblock %}
{% block content %}
<div class="container">
<h1>Membership/Donations</h1>
<p>
private.coffee is a non-profit organization dedicated to supporting privacy
and digital sovereignty. We fund our server infrastructure through donations
and membership fees.
</p>
<p>
Membership fees help cover the costs of server infrastructure, domain names,
and other operating expenses. Likewise, all donations contribute towards
these ongoing costs.
</p>
<p>
Supporting the private.coffee project is possible by joining our association
or by making a direct donation.
</p>
<div class="donation-details">
<p><b>Donations:</b></p>
<p>
Bank private.coffee - Verein zur Förderung von Privatsphäre und digitaler
Souveränität
</p>
<p>IBAN: AT35 2081 5000 4554 0812</p>
<p>BIC: STSPAT2GXXX</p>
<p>
For donation listing preferences, please specify in the note to payee.
<div class="container my-5">
<div class="text-center mb-5">
<h1 class="special-header fancy-text-primary">Membership/Donations</h1>
<p class="lead">
Private.coffee is a non-profit organization dedicated to supporting
privacy and digital sovereignty. We fund our activities and running
infrastructure costs through membership fees. Donations allow us to expand
our services and reach more people.
</p>
</div>
<div class="zvr-number">
<p>Central Register of Associations (ZVR) Number: 1758485319</p>
<p>
Our statutes can be found in our Git
<a href="https://git.private.coffee/PrivateCoffee/statuten"
>in German (legally binding)</a
>
and
<a
href="https://git.private.coffee/PrivateCoffee/Statuten/src/branch/english"
>in English</a
>.
</p>
<p class="contact-info">
Interested in joining the association? Reach out via
<a href="mailto:support@private.coffee">email</a> or
<a href="https://matrix.private.cf/#/#private.coffee:private.coffee"
>Matrix</a
>
for more information.
</p>
<div class="row">
<div class="col-md-4 mb-4">
<div class="card shadow-sm">
<div class="card-body">
<h5 class="card-title">Membership</h5>
<p class="card-text">
Membership fees help cover the costs of our day-to-day activities,
server infrastructure, domain names, and other running operating
expenses. This allows us to provide services to our members and the
public.
</p>
<p class="card-text">
If you're interested in joining our association, get in touch with
us for more information.
</p>
</div>
</div>
</div>
<div class="col-md-4 mb-4">
<div class="card shadow-sm">
<div class="card-body">
<h5 class="card-title">Bank Donations</h5>
<p class="card-text">
Even if you're not a member, you can still support us by making a
direct donation to our bank account. Your donation will be used to
fund our activities and expand our services.
</p>
<p class="card-text"><b>Account holder:</b> Private.coffee</p>
<p class="card-text"><b>IBAN:</b> AT35 2081 5000 4554 0812</p>
<p class="card-text"><b>BIC:</b> STSPAT2GXXX</p>
</div>
</div>
</div>
<div class="col-md-4 mb-4">
<div class="card shadow-sm">
<div class="card-body">
<h5 class="card-title">Crypto Donations</h5>
<p class="card-text">
While our expenses are generally in fiat currency, we accept
donations in cryptocurrencies, too.
</p>
<p class="card-text">
<b>Bitcoin (BTC):</b>
<code>bc1qnu7r5sed4afacfpgx5za8hsyhaj4rs45dpm26k</code>
</p>
<p class="card-text">
<b>Monero (XMR):</b>
<code
>487Ny4iBk2pKGJwjyYrumFD8xFmrS6jCSXNA8e5EvVJ49GyS54CRDVz514MBnXgNT1EioKYiagHs33sLzUAFj8i3Pwg3AMS</code
>
</p>
<p class="card-text">
<b>Ethereum (ETH):</b> <code>Coming soon&trade;</code>
</p>
<p class="card-text">
If your client supports OpenAlias, you can just send your donation
to <code>private.coffee</code>.
</p>
</div>
</div>
</div>
</div>
<div class="card shadow-sm mt-4">
<div class="card-body">
<h5 class="card-title">Transparency Report for {{ finances_period }}</h5>
<p class="card-text">
We believe in transparency and accountability. Below is a summary of our
income and expenses for the last month, so you can see how your
donations are being used.
</p>
<div class="table-responsive">{{ finances|safe }}</div>
</div>
</div>
<div class="card shadow-sm mt-4">
<div class="card-body">
<h5>Central Register of Associations (ZVR) Number: 1758485319</h5>
<p>
Our statutes can be found in our Git
<a href="https://git.private.coffee/PrivateCoffee/statuten"
>in German (legally binding)</a
>
and
<a
href="https://git.private.coffee/PrivateCoffee/Statuten/src/branch/english"
>in English</a
>.
</p>
<p class="contact-info" id="contact-info">
Interested in joining the association? Reach out via
<a href="mailto:support@private.coffee">email</a> or
<a href="https://matrix.pcof.fi/#/#private.coffee:private.coffee"
>Matrix</a
>
for more information.
</p>
</div>
</div>
</div>
{% endblock %}

View file

@ -1,167 +1,187 @@
{% extends "base.html" %}
{% block title %}Privacy Policy{% endblock %}
{% extends "base.html" %} {% block title %}Privacy Policy{% endblock %}
{% block content %}
<div class="container">
<h1>Privacy Policy</h1>
<p>
This is the privacy policy for the website private.coffee. It describes how
we collect and use your personal data.
</p>
<p>
This privacy policy is effective as of 2024-04-26. It may be updated from
time to time, so please check back regularly.
</p>
<p>
If you have any questions about this privacy policy, please contact us at
<a href="mailto:support@private.coffee">support@private.coffee</a>.
</p>
<h2>Who are we?</h2>
<p>The data controller for private.coffee is:</p>
<address>
Private.coffee &dash; Verein zur Förderung von Privatsphäre und digitaler
Souver&auml;nit&auml;t<br />
c/o Klaus-Uwe Mitterer<br />
Gartengasse 22/7/3<br />
8010 Graz<br />
Austria<br />
</address>
<p>Central Register of Associations (ZVR) Number: 1758485319</p>
<p>
Email:
<a href="mailto:support@private.coffee">support@private.coffee</a>
</p>
<h2>What data do we collect?</h2>
<p>We collect the following data from you:</p>
<ul>
<li>
Your IP address, which is automatically collected by our web server for
some of our services.
</li>
<li>
Any data you provide to us, such as your email address, when you create an
account on one of our services.
</li>
<li>
Usage data that is collected automatically by our services, such as the
time you last logged in, or the number of times you have logged in, as
well as any content you create using our services.
</li>
<li>
Any information, including personal data and payment information, that you
provide to us when you make a donation or join our association.
</li>
</ul>
<h2>How do we use your data?</h2>
<p>
We use your data to provide you with the services you requested. This
includes:
</p>
<ul>
<li>Communication between our web server and your browser</li>
<li>Storing your preferences or your created content</li>
<li>Detection of abuse or resource exhaustion on our servers</li>
<li>Providing support to you</li>
<li>Processing donations and memberships</li>
</ul>
<p>All of those are what is called "legitimate interests" in legal speak.</p>
<p>
We do not use your data for any other purposes, including marketing,
advertising, or tracking, we do not share your data with third parties
(unless required by law), we do not use your data to create profiles or make
automated decisions, and we do not cross-reference your data between
different services or any other sources.
</p>
<h2>Where do we store your data?</h2>
<p>
We store your data on our web servers, which are located in Austria. We also
use servers in Germany, but those are not public-facing and do not store or
process any personal data.
</p>
<h2>How long do we store your data?</h2>
<ul>
<li>
IP addresses collected by our web server are stored for up to 48 hours.
After that, the log files that contain them are deleted automatically.
</li>
<li>
Data associated with an account you have on one of our services is stored
for as long as you have an account on that service. If you delete your
account, your data will be deleted automatically. The service will have a
dedicated privacy policy, please consult that if you have any specific
questions.
</li>
<li>
If you send a support request to us, we will store your email address and
any other data you provide to us for up to 30 days in our support system.
After that, the data will be deleted automatically.
</li>
<li>
Data associated with a donation is stored for as long as required by law,
which is currently 7 years in Austria. After that, the data will be
deleted.
</li>
<li>
Data associated with a membership is stored for as long as you are a
member of our association. If you leave the association, your data will be
deleted automatically. This does not apply to data that is required by law
to be stored for a longer period of time, such as financial records, which
are stored for 7 years regardless of your membership status.
</li>
</ul>
<h2>What are your data protection rights?</h2>
<p>You have the following data protection rights:</p>
<ul>
<li>You have the right to request access to your personal data.</li>
<li>You have the right to request correction of your personal data.</li>
<li>You have the right to request erasure of your personal data.</li>
<li>
You have the right to request restriction of processing of your personal
data.
</li>
<li>You have the right to object to processing of your personal data.</li>
<li>You have the right to request transfer of your personal data.</li>
</ul>
<p>If you make a request, we have one month to respond to you.</p>
<p>
If you would like to exercise any of these rights, please contact us at
<a href="mailto:support@private.coffee">support@private.coffee</a>.
</p>
<p>
Your privacy is important to us - after all, that's why we're all here - and
we will do our best to respond to your request as soon as possible. Please
note that we may need to verify your identity before we can respond to your
request - we will not share your data with anyone else.
</p>
<p>
If you are not satisfied with our response, or believe that we are
processing your data in a way that is not compliant with the law, you have
the right to lodge a complaint with the supervisory authority in your
country. In Austria, this is the Austrian Data Protection Authority (<a
href="https://www.dsb.gv.at/"
>https://www.dsb.gv.at/</a
>). However, we like to think of ourselves as nice people and will try to
help you out directly, and are always open to suggestions for improvement,
so please do get in touch with us if you have any questions or concerns.
</p>
<h2>What are cookies?</h2>
<p>
Cookies are small text files that are placed on your computer or mobile
device when you visit a website. They are widely used to make websites work,
or work more efficiently, as well as to provide information to the owners of
the site. Cookies are stored on your device, and can be accessed by the
website that created them. They are not harmful to your device, and do not
directly contain any personal data, but are used by our services to identify
you between multiple visits.
</p>
<p>
Cookies are used on some of our services to provide you with a better
experience. For example, we use cookies to remember your preferences and
settings - for example, to allow you to login to our services - and to
provide you with a better experience when browsing our services. We do not
use cookies to track your activity, either on our services or on other
websites. In legal/technical terms, we exclusively use "strictly necessary"
first-party cookies, which is also why you don't see any cookie banners on
our website.
</p>
<div class="text-center mb-5">
<h1 class="special-header fancy-text-primary">Privacy Policy</h1>
<p class="lead">
This is the privacy policy for the website private.coffee. It describes
how we collect and use your personal data.
</p>
</div>
<div class="section">
<p>
This privacy policy is effective as of 2024-04-26. It may be updated from
time to time, so please check back regularly.
</p>
<p>
If you have any questions about this privacy policy, please contact us at
<a href="mailto:support@private.coffee">support@private.coffee</a>.
</p>
</div>
<div class="section">
<h2>Who are we?</h2>
<p>The data controller for private.coffee is:</p>
<address>
Private.coffee &dash; Verein zur Förderung von Privatsphäre und digitaler
Souver&auml;nit&auml;t<br />
c/o Klaus-Uwe Mitterer<br />
Gartengasse 22/7/3<br />
8010 Graz<br />
Austria<br />
</address>
<p>Central Register of Associations (ZVR) Number: 1758485319</p>
<p>
Email:
<a href="mailto:support@private.coffee">support@private.coffee</a>
</p>
</div>
<div class="section">
<h2>What data do we collect?</h2>
<p>We collect the following data from you:</p>
<ul>
<li>
Your IP address, which is automatically collected by our web server for
some of our services.
</li>
<li>
Any data you provide to us, such as your email address, when you create
an account on one of our services.
</li>
<li>
Usage data that is collected automatically by our services, such as the
time you last logged in, or the number of times you have logged in, as
well as any content you create using our services.
</li>
<li>
Any information, including personal data and payment information, that
you provide to us when you make a donation or join our association.
</li>
</ul>
</div>
<div class="section">
<h2>How do we use your data?</h2>
<p>
We use your data to provide you with the services you requested. This
includes:
</p>
<ul>
<li>Communication between our web server and your browser</li>
<li>Storing your preferences or your created content</li>
<li>Detection of abuse or resource exhaustion on our servers</li>
<li>Providing support to you</li>
<li>Processing donations and memberships</li>
</ul>
<p>
All of those are what is called "legitimate interests" in legal speak.
</p>
<p>
We do not use your data for any other purposes, including marketing,
advertising, or tracking, we do not share your data with third parties
(unless required by law), we do not use your data to create profiles or
make automated decisions, and we do not cross-reference your data between
different services or any other sources.
</p>
</div>
<div class="section">
<h2>Where do we store your data?</h2>
<p>
We store your data on our web servers, which are located in Austria. We
also use servers in Germany, but those are not public-facing and do not
store or process any personal data.
</p>
</div>
<div class="section">
<h2>How long do we store your data?</h2>
<ul>
<li>
IP addresses collected by our web server are stored for up to 48 hours.
After that, the log files that contain them are deleted automatically.
</li>
<li>
Data associated with an account you have on one of our services is
stored for as long as you have an account on that service. If you delete
your account, your data will be deleted automatically. The service will
have a dedicated privacy policy, please consult that if you have any
specific questions.
</li>
<li>
If you send a support request to us, we will store your email address
and any other data you provide to us for up to 30 days in our support
system. After that, the data will be deleted automatically.
</li>
<li>
Data associated with a donation is stored for as long as required by
law, which is currently 7 years in Austria. After that, the data will be
deleted.
</li>
<li>
Data associated with a membership is stored for as long as you are a
member of our association. If you leave the association, your data will
be deleted automatically. This does not apply to data that is required
by law to be stored for a longer period of time, such as financial
records, which are stored for 7 years regardless of your membership
status.
</li>
</ul>
</div>
<div class="section">
<h2>What are your data protection rights?</h2>
<p>You have the following data protection rights:</p>
<ul>
<li>You have the right to request access to your personal data.</li>
<li>You have the right to request correction of your personal data.</li>
<li>You have the right to request erasure of your personal data.</li>
<li>
You have the right to request restriction of processing of your personal
data.
</li>
<li>You have the right to object to processing of your personal data.</li>
<li>You have the right to request transfer of your personal data.</li>
</ul>
<p>If you make a request, we have one month to respond to you.</p>
<p>
If you would like to exercise any of these rights, please contact us at
<a href="mailto:support@private.coffee">support@private.coffee</a>.
</p>
<p>
Your privacy is important to us - after all, that's why we're all here -
and we will do our best to respond to your request as soon as possible.
Please note that we may need to verify your identity before we can respond
to your request - we will not share your data with anyone else.
</p>
<p>
If you are not satisfied with our response, or believe that we are
processing your data in a way that is not compliant with the law, you have
the right to lodge a complaint with the supervisory authority in your
country. In Austria, this is the Austrian Data Protection Authority (<a
href="https://www.dsb.gv.at/"
>https://www.dsb.gv.at/</a
>). However, we like to think of ourselves as nice people and will try to
help you out directly, and are always open to suggestions for improvement,
so please do get in touch with us if you have any questions or concerns.
</p>
</div>
<div class="section">