Merge pull request 'New Membership Page and other changes' (#4) from membership-page into main
Reviewed-on: #4
|
@ -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).
|
|
@ -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 {
|
||||
|
|
1
assets/dist/icons/article-ny-times.svg
vendored
Normal 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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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 |
1
assets/dist/icons/magnifying-glass.svg
vendored
Normal 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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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": {}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
@ -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
|
@ -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)
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
</ul><a class="btn btn-primary shadow navbar-btn" role="button" href="/membership.html">JOIN & 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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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™</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 %}
|
||||
|
|
|
@ -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 ‐ Verein zur Förderung von Privatsphäre und digitaler
|
||||
Souveränitä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 ‐ Verein zur Förderung von Privatsphäre und digitaler
|
||||
Souveränitä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">
|
||||