Merge pull request #212 from Lissy93/REWRITE

Total Rewrite
This commit is contained in:
Alicia Sykes 2024-02-25 15:39:59 +00:00 committed by GitHub
commit 212e38ed4b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
88 changed files with 17699 additions and 4715 deletions

64
.github/ABOUT.md vendored
View File

@ -1,64 +0,0 @@
# Repository Admin
## Mirror
---
## Credits
The credits section has now been moved to **[CREDITS.md](https://github.com/Lissy93/awesome-privacy/blob/main/.github/CREDITS.md)**
---
## Website
A web-version of Awesome-Privacy is available at: **[awesome-privacy.xyz](https://awesome-privacy.xyz)**
The source for the website is managed in the [`gh-pages`](https://github.com/Lissy93/awesome-privacy/tree/gh-pages) branch.
The content is in markdown format, and pulled from the main branch using [this workflow](https://github.com/Lissy93/awesome-privacy/blob/main/.github/workflows/sync-docs-branch.yml) running on GitHub actions. The site is built with [Docsify](https://docsify.js.org), and deployed automatically onto [Netlify](https://netlify.com).
A mirror is also deployed to GH Pages, at: [lissy93.github.io/awesome-privacy](https://lissy93.github.io/awesome-privacy/)
### Making Changes to the Website
#### Making changes to content
To add, remove or amend any piece of software on the list, please follow the [Contributing Guide](https://github.com/Lissy93/awesome-privacy/blob/main/.github/CONTRIBUTING.md), and submit changes only to the main branch.
#### Making changes to the website
To make changes to the layout, style or logic of the web app, excluding any markdown files, changes should be submitted directly to the gh-pages branch.
### Website Privacy
Although no analytics or tracking is included in the website, the third-party services involved in deploying, hosting and DNS all potentially have the ability to see user traffic, and may collect, store and process data on visitors. You should refer to the following privacy policies for more information:
- [CloudFlare Privacy Policy](https://cloudflare.com/privacypolicy/) _(used for DNS)_
- [Netlify Privacy Policy](https://www.netlify.com/privacy/) _(used for website hosting)_
- [GitHub Privacy Policy](https://docs.github.com/en/site-policy/privacy-policies/github-privacy-statement) _(used for code hosting)_
---
## Transparency
---
## Disclaimer
---
## Author Pledge
---
## License
Awesome-Privacy is licensed under [Creative Commons, CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).
In short, this means:
- You are free to use and distribute this repository, and any or it's content for personal or commerical use.
- But you must include the copyright, give credit to the original author, and state what changes you've made.
- And you cannot hold the author liable for any damages, and there is no warranty for any of the content.

View File

@ -4,36 +4,51 @@
### How to make an Addition
You can add entries to this list by opening a pull request.
You can add, edit or remove entries by opening a pull request.
Get started by [editing](https://github.com/Lissy93/awesome-privacy/edit/main/README.md) the readme, committing changes to your fork, and opening a pull request upstream to this repo's main branch.
If you are new to GitHub, [this tutorial](https://www.freecodecamp.org/news/how-to-make-your-first-pull-request-on-github/) may help you.
All data is stored in [`awesome-privacy.yml`](https://github.com/Lissy93/awesome-privacy/blob/main/awesome-privacy.yml).
If you're adding, editing or removing a listing - **this is the only file you need to edit**.
Get started by forking the repo, [editing](https://github.com/Lissy93/awesome-privacy/edit/main/awesome-privacy.yml) the data, committing changes to your fork, and opening a pull request upstream to this repo's main branch. If you're new to open source, you can find some resources to get you started at [git-in.to](https://git-in.to), but feel free to reach out if you need any help 😊
Your request will be reviewed, then either merged, or have changes requested, or if the [Guidelines](#guidelines) are not met, it may be closed with a comment explaining why.
To make layout or stylistic amendments to the website ([awesome-privacy.xyz](https://awesome-privacy.xyz)), pull requests should be made to the `gh-pages` branch. For more information about the website, see [About --> Website](https://github.com/Lissy93/awesome-privacy/blob/main/.github/ABOUT.md#website)
To make layout or stylistic amendments to the website ([awesome-privacy.xyz](https://awesome-privacy.xyz)), see [Website docs](https://github.com/Lissy93/awesome-privacy#the-website) in the readme for build and running instructions.
---
### Requirements
Software additions to this list, will usually need to meet the following requirements.
For software to be included in this list, it must meet the following requirements:
- **Privacy Respecting**
- The project must respect users privacy, not collect more data than necessary, and store info securely
- The project must respect users privacy, not collect more data than necessary, and store info securely
- For hosted services, the project must have a clear privacy policy
- The user must remain in full control of their data, and be able to delete it at any time
- **Secure**
- The software must be secure by default, without requiring additional configuration
- There should be no current, critical security issues
- The handling of past issues must have been prompt, transparent and effective
- **Open Source**
- The full source code should be released under an open source license
- The full source code should be released under an open source license
- Ideally it should be possible for the user to build and run/deploy the software themselves from source
- **Actively Maintained**
- The developers should address dependency updates and security patches in a timely manner
- **Transparent**
- It should be clear who is behind the project, what their motives are, and what (if any) the funding model is
- **Ethical**
- Must not suppress free speech, discriminate or disregard any human rights
- **Relevant**
- The software must be relevant, and fit into one of the existing categories
- **Functional**
- Must be fully functional, and not just a concept or idea
- A stable (non-alpha/beta) release is required at a minimum
- Must be accessible to the general public, and not just a select group of people
- If technical knowledge is required to run it, the software must be well documented
_There may be some exceptions, but these would need to be fully justified, reviewed by the community, and drawbacks / anti-features must be listed along-side the software when merged. Usually these entries go within the "Notable Mentions" section instead._
_There may be some exceptions, but these would need to be fully justified, reviewed
by the community, and the drawbacks / anti-features must be clearly listed along-side the software.
Usually these entries go within the "Notable Mentions" section instead._
---
@ -41,6 +56,7 @@ _There may be some exceptions, but these would need to be fully justified, revie
Your pull request must follow these requirements. Failure to do so, might result in it being closed.
- Do not edit the README directly when adding / editing a listing
- Ensure your PR is not a duplicate, search for existing / previous submissions first
- You must respond to any comments or requests for changes in a timely manner, 48-hours maximum
- Write short but descriptive git commit messages, under 50 characters. This must be in the format of `Adds [software-name] to [section-name]`. Your PR will be rejected if you name it `Updates README.md`
@ -59,4 +75,4 @@ Your pull request must follow these requirements. Failure to do so, might result
- Your changes must be correctly formatted, in valid markdown
- The addition title must be a link the project, and in bold
- The addition description must be no less than 50, and no more than 250 characters, keep it clear and to the point
- The structure of additions is as follows: `[Project Name](https://project-link/) | Brief description`

992
.github/CREDITS.md vendored
View File

@ -1,992 +0,0 @@
# Credits
### Sponsors
<!-- readme: sponsors -start -->
<table>
<tr>
<td align="center">
<a href="https://github.com/koconder">
<img src="https://avatars.githubusercontent.com/u/25068?u=582657b23622aaa3dfe68bd028a780f272f456fa&v=4" width="80;" alt="koconder"/>
<br />
<sub><b>Vincent Koc</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/peng1can">
<img src="https://avatars.githubusercontent.com/u/225854?v=4" width="80;" alt="peng1can"/>
<br />
<sub><b>Peng1can</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/alydemah">
<img src="https://avatars.githubusercontent.com/u/652035?u=ac2c04e474da37bfeafcfa25076cc1800997aedb&v=4" width="80;" alt="alydemah"/>
<br />
<sub><b>Aly Mohamed</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/tbjers">
<img src="https://avatars.githubusercontent.com/u/1117052?u=539d96d5e581b3139c75713ce35b89a36626404c&v=4" width="80;" alt="tbjers"/>
<br />
<sub><b>Torgny Bjers</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/emlazzarin">
<img src="https://avatars.githubusercontent.com/u/1141361?u=714e3487a3f2e0df721b01a0133945f075d3ff68&v=4" width="80;" alt="emlazzarin"/>
<br />
<sub><b>Eddy Lazzarin</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/AnandChowdhary">
<img src="https://avatars.githubusercontent.com/u/2841780?u=747e554b3a7f12eb20b7910e1c87d817844f714f&v=4" width="80;" alt="AnandChowdhary"/>
<br />
<sub><b>Anand Chowdhary</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/shrippen">
<img src="https://avatars.githubusercontent.com/u/2873570?v=4" width="80;" alt="shrippen"/>
<br />
<sub><b>Shrippen</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/bile0026">
<img src="https://avatars.githubusercontent.com/u/5022496?u=aec96ad173c0ea9baaba93807efa8a848af6595c&v=4" width="80;" alt="bile0026"/>
<br />
<sub><b>Zach Biles</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/UlisesGascon">
<img src="https://avatars.githubusercontent.com/u/5110813?u=3c41facd8aa26154b9451de237c34b0f78d672a5&v=4" width="80;" alt="UlisesGascon"/>
<br />
<sub><b>Ulises Gascón</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/digitalarche">
<img src="https://avatars.githubusercontent.com/u/6546135?u=d033c9c16e8367987aec3f9ff5922bc67dd1eedf&v=4" width="80;" alt="digitalarche"/>
<br />
<sub><b>Digital Archeology</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/bmcgonag">
<img src="https://avatars.githubusercontent.com/u/7346620?u=2a0f9284f3e12ac1cc15288c254d1ec68a5081e8&v=4" width="80;" alt="bmcgonag"/>
<br />
<sub><b>Brian McGonagill</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/vlad-timofeev">
<img src="https://avatars.githubusercontent.com/u/11474041?u=eee43705b54d2ec9f51fc4fcce5ad18dd17c87e4&v=4" width="80;" alt="vlad-timofeev"/>
<br />
<sub><b>Vlad Timofeev</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/helixzz">
<img src="https://avatars.githubusercontent.com/u/12218889?u=d06d0c103dfbdb99450623064f7da3c5a3675fb6&v=4" width="80;" alt="helixzz"/>
<br />
<sub><b>HeliXZz</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/undefined">
<img src="" width="80;" alt="undefined"/>
<br />
<sub><b>Undefined</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Bastii717">
<img src="https://avatars.githubusercontent.com/u/53431819?u=604977bed6ad6875ada890d0d3765a4cacc2fa14&v=4" width="80;" alt="Bastii717"/>
<br />
<sub><b>Bastii717</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/frankdez93">
<img src="https://avatars.githubusercontent.com/u/87549420?v=4" width="80;" alt="frankdez93"/>
<br />
<sub><b>frankdez93</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/ratty222">
<img src="https://avatars.githubusercontent.com/u/92832598?u=137b65530cbd5f5af9c24cde51baa6cc77cc934b&v=4" width="80;" alt="ratty222"/>
<br />
<sub><b>Brent</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/hernanpopper">
<img src="https://avatars.githubusercontent.com/u/104868017?v=4" width="80;" alt="hernanpopper"/>
<br />
<sub><b>hernanpopper</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/NixyJuppie">
<img src="https://avatars.githubusercontent.com/u/138570196?u=b102c222487905728b858704962d32759df29ebe&v=4" width="80;" alt="NixyJuppie"/>
<br />
<sub><b>Nixy</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/nrvo">
<img src="https://avatars.githubusercontent.com/u/151435968?u=e1dcb307fd0efdc45cddbe9490a7b956e4da6835&v=4" width="80;" alt="nrvo"/>
<br />
<sub><b>Nrvo</b></sub>
</a>
</td></tr>
</table>
<!-- readme: sponsors -end -->
---
### Contributors
<!-- readme: contributors -start -->
<table>
<tr>
<td align="center">
<a href="https://github.com/liss-bot">
<img src="https://avatars.githubusercontent.com/u/87835202?v=4" width="80;" alt="liss-bot"/>
<br />
<sub><b>Alicia Bot</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Lissy93">
<img src="https://avatars.githubusercontent.com/u/1862727?v=4" width="80;" alt="Lissy93"/>
<br />
<sub><b>Alicia Sykes</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Ki-er">
<img src="https://avatars.githubusercontent.com/u/32241933?v=4" width="80;" alt="Ki-er"/>
<br />
<sub><b>Kieran</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/lilithium-hydride">
<img src="https://avatars.githubusercontent.com/u/78992082?v=4" width="80;" alt="lilithium-hydride"/>
<br />
<sub><b>Lilith</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/ltguillaume">
<img src="https://avatars.githubusercontent.com/u/4777345?v=4" width="80;" alt="ltguillaume"/>
<br />
<sub><b>Guillaume</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/A-childs-encyclopedia">
<img src="https://avatars.githubusercontent.com/u/84061672?v=4" width="80;" alt="A-childs-encyclopedia"/>
<br />
<sub><b>A-childs-encyclopedia</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/amilich">
<img src="https://avatars.githubusercontent.com/u/1927690?v=4" width="80;" alt="amilich"/>
<br />
<sub><b>Andrew Milich</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/kerbless">
<img src="https://avatars.githubusercontent.com/u/32358946?v=4" width="80;" alt="kerbless"/>
<br />
<sub><b>Kerbless</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/GhoulBoii">
<img src="https://avatars.githubusercontent.com/u/78494833?v=4" width="80;" alt="GhoulBoii"/>
<br />
<sub><b>GhoulBoi69</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/slade991">
<img src="https://avatars.githubusercontent.com/u/10143703?v=4" width="80;" alt="slade991"/>
<br />
<sub><b>Slade991</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/samsapti">
<img src="https://avatars.githubusercontent.com/u/26819407?v=4" width="80;" alt="samsapti"/>
<br />
<sub><b>Sam A.</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/gabrielvicenteYT">
<img src="https://avatars.githubusercontent.com/u/68158102?v=4" width="80;" alt="gabrielvicenteYT"/>
<br />
<sub><b>Coccocoa's Helper</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/AlexOgden">
<img src="https://avatars.githubusercontent.com/u/1379601?v=4" width="80;" alt="AlexOgden"/>
<br />
<sub><b>Alex Ogden</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/spignelon">
<img src="https://avatars.githubusercontent.com/u/92091338?v=4" width="80;" alt="spignelon"/>
<br />
<sub><b>Ujjawal Saini</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Upstream8022">
<img src="https://avatars.githubusercontent.com/u/105393306?v=4" width="80;" alt="Upstream8022"/>
<br />
<sub><b>Upstream8022</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/WardPearce">
<img src="https://avatars.githubusercontent.com/u/27844174?v=4" width="80;" alt="WardPearce"/>
<br />
<sub><b>Ward</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Wesley-Ryan">
<img src="https://avatars.githubusercontent.com/u/69822796?v=4" width="80;" alt="Wesley-Ryan"/>
<br />
<sub><b>Wesley-Ryan</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/ZhymabekRoman">
<img src="https://avatars.githubusercontent.com/u/61125068?v=4" width="80;" alt="ZhymabekRoman"/>
<br />
<sub><b>Zhymabek Roman</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/baddate">
<img src="https://avatars.githubusercontent.com/u/37013819?v=4" width="80;" alt="baddate"/>
<br />
<sub><b>Sanmoji</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/colenh">
<img src="https://avatars.githubusercontent.com/u/40342475?v=4" width="80;" alt="colenh"/>
<br />
<sub><b>Cole</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/jxhn">
<img src="https://avatars.githubusercontent.com/u/1396009?v=4" width="80;" alt="jxhn"/>
<br />
<sub><b>Jxhn</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/kolaente">
<img src="https://avatars.githubusercontent.com/u/13721712?v=4" width="80;" alt="kolaente"/>
<br />
<sub><b>Kolaente</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/magical-heyrovsky">
<img src="https://avatars.githubusercontent.com/u/101060148?v=4" width="80;" alt="magical-heyrovsky"/>
<br />
<sub><b>Magical-heyrovsky</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/mrpavan">
<img src="https://avatars.githubusercontent.com/u/20220426?v=4" width="80;" alt="mrpavan"/>
<br />
<sub><b>Pavan</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/pnodet">
<img src="https://avatars.githubusercontent.com/u/5941125?v=4" width="80;" alt="pnodet"/>
<br />
<sub><b>Paul Nodet</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/titanism">
<img src="https://avatars.githubusercontent.com/u/101466223?v=4" width="80;" alt="titanism"/>
<br />
<sub><b>Titanism</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/tschlotfeldt">
<img src="https://avatars.githubusercontent.com/u/149240?v=4" width="80;" alt="tschlotfeldt"/>
<br />
<sub><b>Tim Schlotfeldt</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/edent">
<img src="https://avatars.githubusercontent.com/u/837136?v=4" width="80;" alt="edent"/>
<br />
<sub><b>Terence Eden</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Sean12697">
<img src="https://avatars.githubusercontent.com/u/22508776?v=4" width="80;" alt="Sean12697"/>
<br />
<sub><b>Sean O'Mahoney</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/NylaTheWolf">
<img src="https://avatars.githubusercontent.com/u/41797151?v=4" width="80;" alt="NylaTheWolf"/>
<br />
<sub><b>NylaTheWolf</b></sub>
</a>
</td></tr>
</table>
<!-- readme: contributors -end -->
---
### Original Contributors
_Awesome-Privacy originated from [Personal-Security-Checklist](https://github.com/Lissy93/personal-security-checklist), in which the following users contributed to._
<table>
<tr>
<td align="center">
<a href="https://github.com/Lissy93">
<img src="https://avatars.githubusercontent.com/u/1862727?v=4" width="80;" alt="Lissy93"/>
<br />
<sub><b>Alicia Sykes</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/liss-bot">
<img src="https://avatars.githubusercontent.com/u/87835202?v=4" width="80;" alt="liss-bot"/>
<br />
<sub><b>Alicia Bot</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/matkoniecz">
<img src="https://avatars.githubusercontent.com/u/899988?v=4" width="80;" alt="matkoniecz"/>
<br />
<sub><b>Mateusz Konieczny</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/lucadidomenico">
<img src="https://avatars.githubusercontent.com/u/56132403?v=4" width="80;" alt="lucadidomenico"/>
<br />
<sub><b>Luca Di Domenico</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/gitetsu">
<img src="https://avatars.githubusercontent.com/u/44036?v=4" width="80;" alt="gitetsu"/>
<br />
<sub><b>Gitetsu</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/aarontorres0">
<img src="https://avatars.githubusercontent.com/u/51248787?v=4" width="80;" alt="aarontorres0"/>
<br />
<sub><b>Aaron </b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/0xnbk">
<img src="https://avatars.githubusercontent.com/u/355844?v=4" width="80;" alt="0xnbk"/>
<br />
<sub><b>Nbk</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/alxndrv">
<img src="https://avatars.githubusercontent.com/u/44431221?v=4" width="80;" alt="alxndrv"/>
<br />
<sub><b>Andrey Aleksandrov</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/ilesinge">
<img src="https://avatars.githubusercontent.com/u/501674?v=4" width="80;" alt="ilesinge"/>
<br />
<sub><b>Alexandre G.-Raymond</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/ba32107">
<img src="https://avatars.githubusercontent.com/u/26036493?v=4" width="80;" alt="ba32107"/>
<br />
<sub><b>Balazs Gyurak</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/dmbaturin">
<img src="https://avatars.githubusercontent.com/u/482212?v=4" width="80;" alt="dmbaturin"/>
<br />
<sub><b>Daniil Baturin</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/pndyjack">
<img src="https://avatars.githubusercontent.com/u/20967911?v=4" width="80;" alt="pndyjack"/>
<br />
<sub><b>Pndyjack</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/kdenhartog">
<img src="https://avatars.githubusercontent.com/u/23125059?v=4" width="80;" alt="kdenhartog"/>
<br />
<sub><b>Kyle Den Hartog</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/mwleeds">
<img src="https://avatars.githubusercontent.com/u/7833263?v=4" width="80;" alt="mwleeds"/>
<br />
<sub><b>Phaedrus Leeds</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/sirodoht">
<img src="https://avatars.githubusercontent.com/u/553444?v=4" width="80;" alt="sirodoht"/>
<br />
<sub><b>Theodore Keloglou</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/zacharyraber">
<img src="https://avatars.githubusercontent.com/u/105998568?v=4" width="80;" alt="zacharyraber"/>
<br />
<sub><b>Zachary Raber</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/ansuz">
<img src="https://avatars.githubusercontent.com/u/1264398?v=4" width="80;" alt="ansuz"/>
<br />
<sub><b>Ansuz</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/hypogram">
<img src="https://avatars.githubusercontent.com/u/122231081?v=4" width="80;" alt="hypogram"/>
<br />
<sub><b>Hypogram</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/marjamis">
<img src="https://avatars.githubusercontent.com/u/10202014?v=4" width="80;" alt="marjamis"/>
<br />
<sub><b>Marjamis</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/rusty-snake">
<img src="https://avatars.githubusercontent.com/u/41237666?v=4" width="80;" alt="rusty-snake"/>
<br />
<sub><b>Rusty-snake</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/b3pio">
<img src="https://avatars.githubusercontent.com/u/59568034?v=4" width="80;" alt="b3pio"/>
<br />
<sub><b>₿ӠⱣłØ</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/101lols">
<img src="https://avatars.githubusercontent.com/u/29000894?v=4" width="80;" alt="101lols"/>
<br />
<sub><b>101lols</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/boushley">
<img src="https://avatars.githubusercontent.com/u/101239?v=4" width="80;" alt="boushley"/>
<br />
<sub><b>Aaron Boushley</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Adv4n6">
<img src="https://avatars.githubusercontent.com/u/34752023?v=4" width="80;" alt="Adv4n6"/>
<br />
<sub><b>Adv4n6</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/andydonzelli">
<img src="https://avatars.githubusercontent.com/u/6990701?v=4" width="80;" alt="andydonzelli"/>
<br />
<sub><b>Andy Donzelli</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/austinhuang0131">
<img src="https://avatars.githubusercontent.com/u/16656689?v=4" width="80;" alt="austinhuang0131"/>
<br />
<sub><b>Austin Huang</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/ben-thul">
<img src="https://avatars.githubusercontent.com/u/13566569?v=4" width="80;" alt="ben-thul"/>
<br />
<sub><b>Ben Thul</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/BBlackwo">
<img src="https://avatars.githubusercontent.com/u/7598058?v=4" width="80;" alt="BBlackwo"/>
<br />
<sub><b>Benjamin B</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/BrunoBernardino">
<img src="https://avatars.githubusercontent.com/u/1239616?v=4" width="80;" alt="BrunoBernardino"/>
<br />
<sub><b>Bruno Bernardino</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/chemmi">
<img src="https://avatars.githubusercontent.com/u/15739060?v=4" width="80;" alt="chemmi"/>
<br />
<sub><b>Christian Hemminghaus</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/DaShoe">
<img src="https://avatars.githubusercontent.com/u/89014564?v=4" width="80;" alt="DaShoe"/>
<br />
<sub><b>DaShoe</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/danielgtaylor">
<img src="https://avatars.githubusercontent.com/u/106826?v=4" width="80;" alt="danielgtaylor"/>
<br />
<sub><b>Daniel G. Taylor</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/dim1119">
<img src="https://avatars.githubusercontent.com/u/10008788?v=4" width="80;" alt="dim1119"/>
<br />
<sub><b>Dimitris Lazarakis</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/VoDmAl">
<img src="https://avatars.githubusercontent.com/u/248879?v=4" width="80;" alt="VoDmAl"/>
<br />
<sub><b>Dmitry Vorobyev</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/federicoviceconti">
<img src="https://avatars.githubusercontent.com/u/25590766?v=4" width="80;" alt="federicoviceconti"/>
<br />
<sub><b>Federico</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Z4rak">
<img src="https://avatars.githubusercontent.com/u/65371336?v=4" width="80;" alt="Z4rak"/>
<br />
<sub><b>Felipe</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/fkohrt">
<img src="https://avatars.githubusercontent.com/u/12914806?v=4" width="80;" alt="fkohrt"/>
<br />
<sub><b>Florian Kohrt</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/freddy-m">
<img src="https://avatars.githubusercontent.com/u/25013506?v=4" width="80;" alt="freddy-m"/>
<br />
<sub><b>Freddy</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/hw4n">
<img src="https://avatars.githubusercontent.com/u/38372575?v=4" width="80;" alt="hw4n"/>
<br />
<sub><b>Hwanhee Chae</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/jneplokh">
<img src="https://avatars.githubusercontent.com/u/46184597?v=4" width="80;" alt="jneplokh"/>
<br />
<sub><b>Jacob Neplokh</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/gongjason">
<img src="https://avatars.githubusercontent.com/u/52545545?v=4" width="80;" alt="gongjason"/>
<br />
<sub><b>Jason G</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/mxygem">
<img src="https://avatars.githubusercontent.com/u/21065409?v=4" width="80;" alt="mxygem"/>
<br />
<sub><b>Gemini Smith</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/monkeywithacupcake">
<img src="https://avatars.githubusercontent.com/u/7316730?v=4" width="80;" alt="monkeywithacupcake"/>
<br />
<sub><b>Jess</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/openjck">
<img src="https://avatars.githubusercontent.com/u/933396?v=4" width="80;" alt="openjck"/>
<br />
<sub><b>John Karahalis</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/edelbluth">
<img src="https://avatars.githubusercontent.com/u/4939388?v=4" width="80;" alt="edelbluth"/>
<br />
<sub><b>Juergen Edelbluth</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/fetzu">
<img src="https://avatars.githubusercontent.com/u/6372605?v=4" width="80;" alt="fetzu"/>
<br />
<sub><b>Julien</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/seclution">
<img src="https://avatars.githubusercontent.com/u/38378574?v=4" width="80;" alt="seclution"/>
<br />
<sub><b>Kai Biebel</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/koirand">
<img src="https://avatars.githubusercontent.com/u/17229643?v=4" width="80;" alt="koirand"/>
<br />
<sub><b>Kazuki Koide</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/Kotbenek">
<img src="https://avatars.githubusercontent.com/u/64036903?v=4" width="80;" alt="Kotbenek"/>
<br />
<sub><b>Dominik Piątkowski</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/leon-costa">
<img src="https://avatars.githubusercontent.com/u/83498565?v=4" width="80;" alt="leon-costa"/>
<br />
<sub><b>Leon Costa</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/lukecarr">
<img src="https://avatars.githubusercontent.com/u/24438483?v=4" width="80;" alt="lukecarr"/>
<br />
<sub><b>Luke Carr</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/lordpansar">
<img src="https://avatars.githubusercontent.com/u/14231148?v=4" width="80;" alt="lordpansar"/>
<br />
<sub><b>Magnus Sundström</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/mschwrdtnr">
<img src="https://avatars.githubusercontent.com/u/39745446?v=4" width="80;" alt="mschwrdtnr"/>
<br />
<sub><b>Max S.</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/nickali">
<img src="https://avatars.githubusercontent.com/u/1514992?v=4" width="80;" alt="nickali"/>
<br />
<sub><b>Nick Ali</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/Oymate">
<img src="https://avatars.githubusercontent.com/u/50857856?v=4" width="80;" alt="Oymate"/>
<br />
<sub><b>Oymate</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/pdelfino">
<img src="https://avatars.githubusercontent.com/u/7698207?v=4" width="80;" alt="pdelfino"/>
<br />
<sub><b>Pedro Delfino</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/sjamaan">
<img src="https://avatars.githubusercontent.com/u/128536?v=4" width="80;" alt="sjamaan"/>
<br />
<sub><b>Peter Bex</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/yaxollum">
<img src="https://avatars.githubusercontent.com/u/46109467?v=4" width="80;" alt="yaxollum"/>
<br />
<sub><b>Peter Ye</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/rorymbyrne">
<img src="https://avatars.githubusercontent.com/u/18581795?v=4" width="80;" alt="rorymbyrne"/>
<br />
<sub><b>Rory Byrne</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Silvhr">
<img src="https://avatars.githubusercontent.com/u/71043300?v=4" width="80;" alt="Silvhr"/>
<br />
<sub><b>Silvhr</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/spikecodes">
<img src="https://avatars.githubusercontent.com/u/19519553?v=4" width="80;" alt="spikecodes"/>
<br />
<sub><b>Spike</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/ThomasRettig">
<img src="https://avatars.githubusercontent.com/u/68767503?v=4" width="80;" alt="ThomasRettig"/>
<br />
<sub><b>Thomas Rettig</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/tim-v3">
<img src="https://avatars.githubusercontent.com/u/103248132?v=4" width="80;" alt="tim-v3"/>
<br />
<sub><b>Tim Weber</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/zeusalmighty717">
<img src="https://avatars.githubusercontent.com/u/79133911?v=4" width="80;" alt="zeusalmighty717"/>
<br />
<sub><b>Zeus Almighty</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/apraile">
<img src="https://avatars.githubusercontent.com/u/9890538?v=4" width="80;" alt="apraile"/>
<br />
<sub><b>Apraile</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/elesiuta">
<img src="https://avatars.githubusercontent.com/u/8146662?v=4" width="80;" alt="elesiuta"/>
<br />
<sub><b>Eric Lesiuta</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/fireneat">
<img src="https://avatars.githubusercontent.com/u/95147296?v=4" width="80;" alt="fireneat"/>
<br />
<sub><b>Fireneat</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/ignoramous">
<img src="https://avatars.githubusercontent.com/u/852289?v=4" width="80;" alt="ignoramous"/>
<br />
<sub><b>Ignoramous</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/kevodwyer">
<img src="https://avatars.githubusercontent.com/u/5311499?v=4" width="80;" alt="kevodwyer"/>
<br />
<sub><b>Kevodwyer</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/notthewave">
<img src="https://avatars.githubusercontent.com/u/74874782?v=4" width="80;" alt="notthewave"/>
<br />
<sub><b>Notthewave</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/ogoregen">
<img src="https://avatars.githubusercontent.com/u/37447279?v=4" width="80;" alt="ogoregen"/>
<br />
<sub><b>Oğuzhan</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/pabloscloud">
<img src="https://avatars.githubusercontent.com/u/93644977?v=4" width="80;" alt="pabloscloud"/>
<br />
<sub><b>Pabloscloud</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/partoneoftwo">
<img src="https://avatars.githubusercontent.com/u/1677630?v=4" width="80;" alt="partoneoftwo"/>
<br />
<sub><b>Partoneoftwo</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/pipboy96">
<img src="https://avatars.githubusercontent.com/u/46632672?v=4" width="80;" alt="pipboy96"/>
<br />
<sub><b>Pipboy96</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/theblackmallard">
<img src="https://avatars.githubusercontent.com/u/42389961?v=4" width="80;" alt="theblackmallard"/>
<br />
<sub><b>Theblackmallard</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/0x192">
<img src="https://avatars.githubusercontent.com/u/55300518?v=4" width="80;" alt="0x192"/>
<br />
<sub><b>W1nst0n</b></sub>
</a>
</td></tr>
</table>
---
### Newest Stargazers & Forkers
<table>
<tr>
<td align="center">
<table>
<tr>
<th>
Stargazers
</th>
<th>
Forkers
</th>
</tr>
<tr>
<td>
<a href="https://github.com/Lissy93/awesome-privacy/stargazers" title="View all Stargazers"><img src="https://reporoster.com/stars/dark/Lissy93/awesome-privacy" alt="View Stargazers" width="450" /></a>
</td>
<td>
<a href="https://github.com/Lissy93/awesome-privacy/network/members" title="View all Forks"><img src="https://reporoster.com/forks/dark/lissy93/awesome-privacy" alt="View Forks" width="450" /></a>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td align="center">
<p align="center">
<a href="https://seladb.github.io/StarTrack-js/#/preload?r=lissy93,awesome-privacy">
<img src="https://api.star-history.com/svg?repos=lissy93/awesome-privacy" width="600">
</a>
</p>
</td>
</tr>
</table>
---
### You?
Contributions of any type are very welcome!
Ways that you can help out include, submitting a pull request, raising an issue, sharing the repo with your network, starring / forking the project, or sponsoring us on GitHub.
> **Note** <br> If you're adding, editing or removing something from the list, please read through the [Contributing Docs](/.github/CONTRIBUTING.md) before submitting your PR.
## Follow for More
If you've enjoyed Awesome-Privacy, you might be interested on some of the other projects that I'm working on.<br>
Consider following me for updates!
[![Alicia Sykes on Twitter](https://img.shields.io/twitter/follow/Lissy_Sykes?style=social&logo=twitter)](https://twitter.com/Lissy_Sykes)
[![Alicia Sykes on GitHub](https://img.shields.io/github/followers/lissy93?label=Lissy93&style=social)](https://github.com/Lissy93)
[![Alicia Sykes on Mastodon](https://img.shields.io/mastodon/follow/1032965?domain=https%3A%2F%2Fmastodon.social)](https://mastodon.social/web/accounts/1032965)
[![Alicia Sykes on Keybase](https://img.shields.io/badge/AliciaSykes--lightgrey?style=social&logo=Keybase)](https://keybase.io/aliciasykes)
[![Alicia Sykes's Website](https://img.shields.io/badge/AliciaSykes.com--lightgrey?style=social&logo=Tencent%20QQ)](https://aliciasykes.com)
[![Alicia Sykes's Blog](https://img.shields.io/badge/Blog--lightgrey?style=social&logo=micro.blog)](https://notes.aliciasykes.com/)
[![Alicia Sykes's PGP](https://img.shields.io/badge/PGP--lightgrey?style=social&logo=Let%E2%80%99s%20Encrypt)](https://keybase.io/aliciasykes/pgp_keys.asc)

200
.github/README.md vendored Normal file
View File

@ -0,0 +1,200 @@
<p align="center" id="top"><a href="https://github.com/Lissy93/awesome-privacy"><img src="https://i.ibb.co/80Y5x2T/Awesome-Privacy.png" /></a></p>
<p align="center">🌐 <b><a href="https://awesome-privacy.xyz">awesome-privacy.xyz</a></b></p>
*<p align="center">A curated list of privacy & security-focused apps, software, and providers 🔐</p>*
[⏬ Skip to Content ⏬](#password-managers)
## Intro [![Awesome](https://awesome.re/badge.svg)](https://awesome.re)
Large data-hungry corporations dominate the digital world but with little, or no respect for your privacy.
Migrating to open-source applications with a strong emphasis on privacy and security will help stop
corporations, governments, and hackers from logging, storing or selling your personal data.
**Note**: Remember that [no software is perfect](#important-considerations), and it is important to follow good [security practices](https://github.com/Lissy93/personal-security-checklist/blob/HEAD/CHECKLIST.md).
A Codeberg mirror is available [here](https://codeberg.org/alicia/awesome-privacy).
<!-- awesome-privacy-start -->
<!-- awesome-privacy-end -->
---
## Final Notes
### Conclusion
Many corporations put profit before people, collecting data and exploiting privacy. They claim to be secure but without being open source it can't be verified, until there's been a breach and it's too late. Switching to privacy-respecting open source software will drastically help improving your security, privacy and anonymity online.
However, that's not all you need to do. It is also important to: use strong and unique passwords, 2-factor authentication,
adopt good networking practices and be mindful of data that are collected when browsing the web. You can see the full
**[personal security checklist](https://github.com/Lissy93/personal-security-checklist/blob/HEAD/CHECKLIST.md)** for more tips to stay safe.
### Important Considerations
**Compartmentalise, Update and Be Ready**<br>
No piece of software is truly secure or private. Further to this, software can only as secure as the system it is running on. Vulnerabilities are being discovered and patched all the time, so you much keep your system up-to-date. Breaches occur regularly, so compartmentalise your data to minimise damage. It's not just about choosing secure software, you must also follow good security practices.
**Attack Surface**<br>
It is a good idea to keep your trusted software base small, to reduce potential attack surface. At the same time trusting a single application for too many tasks or too much personal data could be a weakness in your system. So you will need to judge the situation according to your threat model, and carefully plan which software and applications you trust with each segment of your data.
**Convenience Vs Security**<br>
There is often a trade-off between convenience and security. Construct a threat model, and choose a balance that is right for you. In a similar way in some situations there is privacy and security conflict (e.g. Find My Phone is great for security, but terrible for privacy, and anonymous payments may be good for privacy but less secure than insured fiat currency). Again it is about assessing your situation, understanding the risks and making an informed decision.
**Hosted Vs Self-Hosted Considerations**<br>
When using a hosted or managed application that is open-source software - there is often no easy way to tell if the version running is the same as that of the published source code (even published signatures can be faked). There is always the possibility that additional backdoors may have been knowingly or unknowingly implemented in the running instance. One way round this is to self-host software yourself. When self-hosting you will then know for sure which code is running, however you will also be responsible for the managing security of the server, and so may not be recommended for beginners.
**Open Source Software Considerations**<br>
Open source software has long had a reputation of being more secure than its closed source counterparts. Since bugs are raised transparently, fixed quickly, the code can be checked by experts in the community and there is usually little or no data collection or analytics.
That being said, there is no piece of software that it totally bug free, and hence never truly secure or private. Being open source, is in no way a guarantee that something is safe. There is no shortage of poorly-written, obsolete or sometimes harmful open source projects on the internet. Some open source apps, or a dependency bundled within it are just plain malicious (such as, that time [Colourama was found in the PyPI Repository](https://hackaday.com/2018/10/31/when-good-software-goes-bad-malware-in-open-source/))
**Proprietary Software Considerations**<br>
When using a hosted or proprietary solution - always check the privacy policy, research the reputation of the organisation, and be weary about which data you trust them with. It may be best to choose open source software for security-critical situations, where possible.
**Maintenance**<br>
When selecting a new application, ensure it is still being regularly maintained, as this will allow for recently discovered security issues to be addressed. Software in an alpha or beta phase, may be buggy and lacking in features, but more importantly - it could have critical vulnerabilities open to exploit. Similarly, applications that are no longer being actively maintained may pose a security risk, due to lack of patching. When using a forked application, or software that is based on an upstream code base, be aware that it may receive security-critical patches and updates at a slightly later date than the original application.
**This List: Disclaimer**<br>
This list contains packages that range from entry-level to advanced, a lot of the software here will not be appropriate for all audiences. It is in no way a definitive list of secure applications, and aims only to be a guide, a collection of software and services that myself and other contributors have used, and would recommend. There will always be new vulnerabilities discovered or introduced, bugs and security-critical glitches, malicious actors and poorly configured systems. It is up to you to do your research, draw up a threat model, and decide where and how your data are managed.
If you find something on this list that should no longer be deemed secure or private/ or should have a warning note attached, please raise an issue. In the same way if you know of something that is missing, or would like to make an edit, then pull requests are welcome, and are much appreciated!
### Further Reading
**More Awesome Software Lists**<br>
This list was focused on privacy-respecting software. Below are other awesome lists, maintained by the community of open source software, categorised by operating system.
- Windows: [awesome-windows-apps](https://github.com/Awesome-Windows/Awesome) by 'many'
- MacOS: [awesome-macOS-apps](https://github.com/iCHAIT/awesome-macOS) by @iCHAIT
- Linux: [awesome-linux-software](https://github.com/luong-komorebi/Awesome-Linux-Software) by @luong-komorebi
- iOS: [open-source-ios-apps](https://github.com/dkhamsing/open-source-ios-apps) by @dkhamsing
- Android: [open-source-android-apps](https://github.com/pcqpcq/open-source-android-apps) by @pcqpcq
- Server: [awesome-selfhosted](https://github.com/awesome-selfhosted/awesome-selfhosted) by 'many'
- [**More GitHub Awesome Lists →**](https://github.com/Lissy93/personal-security-checklist/blob/master/articles/4_Privacy_And_Security_Links.md#more-awesome-github-lists)
**Security List**<br>
- [Personal Security Checklist](https://github.com/lissy93/personal-security-checklist) - A curated list of security and privacy advice, tools, and resources.
**News & Updates**<br>
A custom Reddit feed covering news and updates for all the apps covered here can be found [here](https://www.reddit.com/user/lissy93/m/software_projects/)
---
## The Website
The easist way to browse Awesome Privacy, is via our website, at **[awesome-privacy.xyz](https://awesome-privacy.xyz)**
[![screenshots](https://raw.githubusercontent.com/Lissy93/awesome-privacy/main/.github/screenshots/grid.png)](https://awesome-privacy.xyz)
### About Website
The source for the website is in the [`web/`](https://github.com/Lissy93/awesome-privacy/tree/main/web) directory.
This is a statically generated site, built with Astro, Svelte, TypeScript an SCSS.<br>
At build-time, it reads the data from [`awesome-privacy.yml`](https://github.com/Lissy93/awesome-privacy/blob/main/awesome-privacy.yml) and generates the pages.
### Running the Website Locally
You'll need [Node.js](https://nodejs.org/en) (20.11.1 or later) and [Git](https://git-scm.com/) installed.<br>
Then run the following commands to fetch the code, install dependencies and start the dev server.
```shell
git clone git@github.com:Lissy93/awesome-privacy.git
cd awesome-privacy/web
yarn
yarn dev
# Then open 127.0.0.1:4321 in your browser
```
### Deploying the Website
Follow the steps above, then run `yarn build` to generate the static files.<br>
You can then upload the `./dist` directory to any web server, static host or CDN.<br>
Alternatively, you can fork the repo and import it into either Vercel or Netlify.
---
## Contributing
We welcome suggestions, additions, edits and removals to the list.<br>
It's thanks to contributors like you that this project is possible 💜
All data is stored in [`awesome-privacy.yml`](https://github.com/Lissy93/awesome-privacy/blob/main/awesome-privacy.yml).
If you're adding, editing or removing a listing - **this is the only file you need to edit**.
Please familiarise yourself with the [Contributing Guidelines](https://github.com/Lissy93/awesome-privacy/blob/main/.github/CONTRIBUTING.md) before submiting your pull request, as we have some guidelines that **must be followed** to ensure your PR can be accepted.
If you're new to open source, you can find some resources to get you started at [git-in.to](https://git-in.to),
but feel free to reach out if you need any help 😊
---
## Acknowledgements
### Sponsors
Huge thanks to the following sponsors, for their ongoing support 💖
<!-- readme: sponsors -start -->
<!-- readme: sponsors -end -->
### Contributors
This project exists thanks to all the people who've helped build and maintain it 🌟
<!-- readme: contributors -start -->
<!-- readme: contributors -end -->
---
## License
> _**[Lissy93/Awesome-Privacy](https://github.com/Lissy93/awesome-privacy)** is licensed under [MIT](https://github.com/Lissy93/awesome-privacy/blob/HEAD/LICENSE) © [Alicia Sykes](https://aliciasykes.com) 2024._<br>
> <sup align="right">For information, see <a href="https://tldrlegal.com/license/mit-license">TLDR Legal > MIT</a></sup>
<details>
<summary>Expand License</summary>
```
The MIT License (MIT)
Copyright (c) Alicia Sykes <alicia@omg.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included install
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANT ABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
```
</details>
<!-- License + Copyright -->
<p align="center">
<i>© <a href="https://aliciasykes.com">Alicia Sykes</a> 2024</i><br>
<i>Licensed under <a href="https://gist.github.com/Lissy93/143d2ee01ccc5c052a17">MIT</a></i><br>
<a href="https://github.com/lissy93"><img src="https://i.ibb.co/4KtpYxb/octocat-clean-mini.png" /></a><br>
<sup>Thanks for visiting :)</sup>
</p>
<!-- Dinosaurs are Awesome -->
<!--
. - ~ ~ ~ - .
.. _ .-~ ~-.
//| \ `..~ `.
|| | } } / \ \
(\ \\ \~^..' | } \
\`.-~ o / } | / \
(__ | / | / `.
`- - ~ ~ -._| /_ - ~ ~ ^| /- _ `.
| / | / ~-. ~- _
|_____| |_____| ~ - . _ _~_-_
-->

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.0 MiB

Binary file not shown.

Binary file not shown.

BIN
.github/screenshots/1_category-page.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

BIN
.github/screenshots/2_section-page.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 KiB

BIN
.github/screenshots/3_browse-page.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 KiB

BIN
.github/screenshots/4_search-page.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

BIN
.github/screenshots/5_about-page.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

BIN
.github/screenshots/6_section-page2.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

BIN
.github/screenshots/grid.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

BIN
.github/screenshots/homepage.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 811 KiB

View File

@ -1,27 +1,11 @@
# Inserts list of contributors and community members into ./docs/credits.md
# Also generates an SVG showing all contributors, which is embedded into readme
name: 📊 Generate Contributor Credits
on:
workflow_dispatch: # Manual dispatch
schedule:
- cron: '0 5 * * 6' # Every Saturday morning.
jobs:
# Job #1 - Generate an embedded SVG asset, showing all contributors
generate-contributors:
runs-on: ubuntu-latest
steps:
- uses: bubkoo/contributors-list@v1
with:
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
svgPath: .github/assets/CONTRIBUTORS.svg
affiliation: all
includeBots: false
excludeUsers: snyk-bot
avatarSize: 96
userNameHeight: 20
svgWidth: 830
commitMessage: 'Updates contributor SVG'
# Job #2 - Inserts sponsors into credits page
# Job #1 - Inserts sponsors into README
insert-sponsors:
runs-on: ubuntu-latest
steps:
@ -31,8 +15,8 @@ jobs:
uses: JamesIves/github-sponsors-readme-action@1.0.5
with:
token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
file: '.github/CREDITS.md'
# Job #3 - Inserts contributors into credits page
file: '.github/README.md'
# Job #2 - Inserts contributors into README
insert-credits:
runs-on: ubuntu-latest
name: Inserts contributors into credits.md
@ -43,7 +27,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
with:
image_size: 80
readme_path: .github/CREDITS.md
readme_path: .github/README.md
columns_per_row: 6
commit_message: 'Updates contributors list'
committer_username: liss-bot

View File

@ -0,0 +1,50 @@
name: 📚 Inserts Awesome Privacy into README
on:
workflow_dispatch:
push:
branches: [ main ]
paths: ['awesome-privacy.yml']
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository 🛎️
uses: actions/checkout@v2
# Get current date-time (used for commit message)
- name: Get Date 📅
id: date
run: echo "::set-output name=date::$(date +'%d-%b-%Y')"
# Downloads + installs Python (used for running gen scripts)
- name: Set up Python 🐍
uses: actions/setup-python@v2
with:
python-version: '3.x'
# Install contents of requirements.txt
- name: Install dependencies 📥
run: |
python -m pip install --upgrade pip
cd lib && pip install -r requirements.txt
# The make command triggers all the Python scripts, generates output
- name: Run make command 🔨
run: python lib/awesome-privacy-readme-gen.py
# Commit and push the outputed readme
- name: Commit and push generated files ⤴️
run: |
git config --global user.name "Liss-Bot"
git config --global user.email "alicia-gh-bot@mail.as93.net"
git pull origin main
git add .github/README.md
if git diff --staged --quiet; then
echo "Nothin new added, so nothing to commit, exiting..."
else
git commit -m "Updated Awesome Privacy content (last modified on ${{ steps.date.outputs.date }})"
git push
fi

1754
README.md

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

4592
awesome-privacy.yml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,145 @@
"""
Reads app list from awesome-privacy.yml,
formats into markdown, and inserts into README.md
"""
import os
import re
import yaml
import logging
from urllib.parse import urlparse
# Configure Logging
LOG_LEVEL = os.environ.get("LOG_LEVEL", "INFO").upper()
logging.basicConfig(level=LOG_LEVEL)
logger = logging.getLogger(__name__)
# Determine the project root based on the script's location
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
app_list_file_path = os.path.join(project_root, 'awesome-privacy.yml')
readme_path = os.path.join(project_root, '.github/README.md')
icon_size=16
# Read the main YAML file, where all data lives
logger.info("Reading the awesome-privacy file...")
with open(app_list_file_path, 'r') as file:
data = yaml.safe_load(file)
def iconElement(serviceUrl, serviceIcon):
path = serviceIcon or f"https://icon.horse/icon/{urlparse(serviceUrl).netloc}"
return f"<img src='{path}' width='{icon_size}' height='{icon_size}' alt='icon' />"
def repoElement(repoUrl):
if not repoUrl:
return ""
return (
f"\t- [![GitHub: {repoUrl}](https://img.shields.io/github/stars/{repoUrl}"
f"?style=flat&logo=github&label={repoUrl.split('/')[1]}"
"&labelColor=%230d1117&color=%23302982&cacheSeconds=3600)]"
f"(https://github.com/{repoUrl})\n"
)
def makeHref(text):
if not text: return "#"
return re.sub(r'[^\w\s-]', '', text.lower()).replace(" ", "-")
def makeContents():
contents = "## Contents\n\n"
for category in data.get('categories'):
contents += f"- **{category.get('name')}**"
for section in category.get('sections'):
contents += (
f"\n\t- [{section.get('name')}](#{makeHref(section.get('name'))}) "
f"({len(section.get('services') or [])})"
)
contents += "\n"
return contents
def makeAwesomePrivacy():
markdown = ""
for category in data.get('categories'):
markdown += f"## {category.get('name')}\n\n"
for section in category.get('sections'):
markdown += f"### {section.get('name')}\n\n"
# Add intro
if section.get('intro'):
markdown += f"{section.get('intro')}\n"
# No services yet
if not section.get('services') or len(section.get('services')) == 0:
markdown += (
"<p align=\"center\">"
"<b>⚠️ This section is still a work in progress ⚠️</b><br />"
"<i>Check back soon, or help us complete it by submitting a pull request</i>"
"</p>"
)
# For each service, list it's name, icon, url, and description
for app in section.get('services') or []:
markdown += (
f"- **[{iconElement(app.get('url'), app.get('icon'))} {app.get('name')}]"
f"({app.get('url')})** - {app.get('description')} "
f"{repoElement(app.get('github'))}"
)
markdown += "\n"
# If word of warning exists, append it
if section.get('wordOfWarning'):
markdown += "<details>\n<summary>⚠️ <b>Word of Warning</b></summary>\n\n"
markdown += f"> {section.get('wordOfWarning')}\n\n"
markdown += "</details>\n\n"
# If notable mentions exists, append it (either as a list or a single string)
if section.get('notableMentions'):
markdown += "<details>\n<summary>✳️ <b>Notable Mentions</b></summary>\n\n"
if isinstance(section.get('notableMentions'), list):
for mention in section.get('notableMentions'):
markdown += f"> - [{mention.get('name')}]({mention.get('url')})" + (
f" - {mention.get('description')}" if mention.get('description') else "\n"
)
else:
notable_mentions = section.get('notableMentions').replace('\n', '\n> ')
markdown += f"> {notable_mentions}"
markdown += "</details>\n\n"
# If further info exists, append it
if section.get('furtherInfo'):
markdown += "<details>\n<summary> <b>Further Info</b></summary>\n\n"
markdown += f"> {section.get('furtherInfo')}"
markdown += "</details>\n\n"
markdown += "<p align=\"right\"><sup><a href=\"#top\">⬆️ [Back to Top]</a></sub></p>\n"
markdown += "\n---\n\n"
return markdown
awesome_privacy_results = makeContents() + makeAwesomePrivacy()
# Update the README.md between markers
logger.info("Reading README.md file...")
with open(readme_path, 'r') as file:
readme_content = file.read()
def update_content_between_markers(content, start_marker, end_marker, new_content):
logger.info(f"Updating content between {start_marker} and {end_marker} markers...")
start_index = content.find(start_marker)
end_index = content.find(end_marker)
if start_index != -1 and end_index != -1:
before_section = content[:start_index + len(start_marker)]
after_section = content[end_index:]
updated_content = before_section + '\n' + new_content + after_section
return updated_content
else:
logger.error(f"Markers {start_marker} and {end_marker} not found.")
return content
# Update guides and resources in README.md
readme_content = update_content_between_markers(
readme_content,
"<!-- awesome-privacy-start -->",
"<!-- awesome-privacy-end -->",
awesome_privacy_results
)
# Write back the updated content to README.md
logger.info("Writing back to README.md...")
with open(readme_path, 'w') as file:
file.write(readme_content)
# All done. Time to go home for tea and medals.
logger.info("Script completed successfully!")

2
lib/requirements.txt Normal file
View File

@ -0,0 +1,2 @@
PyYAML==6.0.1
requests==2.31.0

9
web/ .yarnrc.yml Normal file
View File

@ -0,0 +1,9 @@
npmScopes:
fortawesome:
npmAlwaysAuth: true
npmRegistryServer: "https://npm.fontawesome.com/"
npmAuthToken: ${FONTAWESOME_PACKAGE_TOKEN}
awesome:
npmAlwaysAuth: true
npmRegistryServer: "https://npm.fontawesome.com/"
npmAuthToken: ${FONTAWESOME_PACKAGE_TOKEN}

21
web/.gitignore vendored Normal file
View File

@ -0,0 +1,21 @@
# build output
dist/
# generated types
.astro/
# dependencies
node_modules/
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production
# macOS-specific files
.DS_Store

4
web/.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,4 @@
{
"recommendations": ["astro-build.astro-vscode"],
"unwantedRecommendations": []
}

11
web/.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "./node_modules/.bin/astro dev",
"name": "Development server",
"request": "launch",
"type": "node-terminal"
}
]
}

54
web/README.md Normal file
View File

@ -0,0 +1,54 @@
# Astro Starter Kit: Basics
```sh
npm create astro@latest -- --template basics
```
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/basics)
[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/basics)
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/basics/devcontainer.json)
> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!
![just-the-basics](https://github.com/withastro/astro/assets/2244813/a0a5533c-a856-4198-8470-2d67b1d7c554)
## 🚀 Project Structure
Inside of your Astro project, you'll see the following folders and files:
```text
/
├── public/
│ └── favicon.svg
├── src/
│ ├── components/
│ │ └── Card.astro
│ ├── layouts/
│ │ └── Layout.astro
│ └── pages/
│ └── index.astro
└── package.json
```
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
Any static assets, like images, can be placed in the `public/` directory.
## 🧞 Commands
All commands are run from the root of the project, from a terminal:
| Command | Action |
| :------------------------ | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:4321` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `npm run astro -- --help` | Get help using the Astro CLI |
## 👀 Want to learn more?
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).

24
web/astro.config.mjs Normal file
View File

@ -0,0 +1,24 @@
import { defineConfig } from 'astro/config';
import svelte from '@astrojs/svelte';
import partytown from '@astrojs/partytown';
import sitemap from '@astrojs/sitemap';
import vercel from "@astrojs/vercel/serverless";
// import netlify from "@astrojs/netlify";
const siteMapConfig = {
entryLimit: 10000,
changefreq: 'weekly',
priority: 0.7,
lastmod: new Date(),
filter: (page) => { // Exclude search result pages
return !page.url.startsWith('/search/') && page.url.split('/').length > 2;
},
};
export default defineConfig({
output: 'hybrid',
integrations: [svelte(), partytown(), sitemap(siteMapConfig)],
site: import.meta.env.SITE_URL || 'https://awesome-privacy.xyz',
adapter: vercel(),
// adapter: netlify(),
});

37
web/package.json Normal file
View File

@ -0,0 +1,37 @@
{
"name": "web",
"type": "module",
"version": "0.0.1",
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro check && astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"@astrojs/check": "^0.5.4",
"@astrojs/netlify": "^5.1.2",
"@astrojs/partytown": "^2.0.4",
"@astrojs/sitemap": "^3.1.0",
"@astrojs/svelte": "^5.0.3",
"@astrojs/vercel": "^7.3.2",
"@awesome.me/kit-c134243295": "^1.0.10",
"@fortawesome/fontawesome-pro": "^6.5.1",
"@fortawesome/fontawesome-svg-core": "^6.5.1",
"@fortawesome/free-brands-svg-icons": "^6.5.1",
"@fortawesome/pro-solid-svg-icons": "^6.5.1",
"@fortawesome/svelte-fontawesome": "^0.2.2",
"astro": "^4.3.6",
"fuse.js": "^7.0.0",
"js-yaml": "^4.1.0",
"marked": "^12.0.0",
"svelte": "^4.2.11",
"typescript": "^5.3.3"
},
"devDependencies": {
"@types/js-yaml": "^4.0.9",
"@types/node": "^20.11.19",
"sass": "^1.70.0"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

File diff suppressed because it is too large Load Diff

BIN
web/public/banner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

BIN
web/public/broken-image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
web/public/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

1
web/public/favicon.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path fill="#5f53f4" d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c8.4-19.3 10.6-41.4 4.8-63.3c-11.1-41.5-47.8-69.4-88.6-71.1c-5.8-.2-9.2 6.1-7.4 11.7c2.1 6.4 3.3 13.2 3.3 20.3c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zM373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5L373 389.9z"/></svg>

After

Width:  |  Height:  |  Size: 1015 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,93 @@
Copyright (c) 2008-2010, Isia Urbino (http://www.isiaurbino.net)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

0
web/security.txt.html Normal file
View File

View File

@ -0,0 +1,49 @@
---
interface Props {
title: string;
body: string;
href: string;
}
const { href, title, body } = Astro.props;
---
<li class="link-card">
<a href={href}>
<h2>
{title}
<span>&rarr;</span>
</h2>
<p>
{body}
</p>
</a>
</li>
<style lang="scss">
.link-card {
color: var(--foreground);
background: var(--background);
border: 2px solid var(--foreground);
box-shadow: 6px 6px 0 var(--foreground);
font-family: "Lekton", sans-serif;
font-weight: 700;
transition: all ease-in-out 0.1s;
list-style: none;
&:hover {
box-shadow: 8px 8px 0 var(--foreground);
background: var(--accent);
color: var(--background);
}
a {
box-sizing: border-box;
color: currentColor;
text-decoration: none;
transition: all ease-in-out 0.1s;
}
h2 {
margin: 0;
}
}
</style>

View File

@ -0,0 +1,145 @@
---
import FontAwesome from "@components/form/FontAwesome.svelte"
---
<div class="hero">
<h1>Awesome Privacy</h1>
<p class="intro">
Your guide to finding privacy-respecting alternatives to popular software and services.
</p>
<div class="github-link-wrap">
<a href="https://github.com/lissy93/awesome-privacy">
<FontAwesome iconName="github" />
View on GitHub
</a>
</div>
</div>
<nav class="top-right">
<ul>
<li>
<a href="/">Home</a>
</li>
<li>
<a href="/search">Search</a>
</li>
<li>
<a href="/browse">Browse</a>
</li>
<li>
<a href="/about">About</a>
</li>
<li>
<a href="https://github.com/lissy93/awesome-privacy">Source</a>
</li>
<li>
<a href="https://as93.net">More Apps</a>
</li>
</ul>
</nav>
<style lang="scss">
.hero {
color: var(--accent-fg);
border-radius: var(--curve-sm);
padding: 2rem 4rem;
display: flex;
flex-direction: column;
gap: 2rem;
}
svg {
position: absolute;
top: -32px;
left: 50%;
transform: translatex(-50%);
width: 220px;
height: auto;
opacity: 0.6;
display: none;
}
h1 {
margin: 0;
font-size: 5rem;
font-weight: 700;
line-height: 1;
text-align: center;
font-family: 'Libre Franklin', sans-serif;
color: var(--accent-3);
-webkit-text-fill-color: var(--accent-3);
-webkit-text-stroke-width: 2px;
-webkit-text-stroke-color: var(--foreground);
text-shadow: 3px 3px 0 var(--foreground);
}
.intro {
text-align: center;
font-size: 1.6rem;
padding: 0.5rem 1rem;
color: var(--accent-fg);
background: var(--accent-3);
border: 2px solid var(--foreground);
border-radius: var(--curve-sm);
box-shadow: 6px 6px 0 var(--foreground);
font-family: "Lekton", sans-serif;
font-weight: 700;
max-width: 735px;
margin: 0 auto;
}
.github-link-wrap {
font-family: "Lekton", sans-serif;
max-width: 735px;
text-align: center;
margin: 0 auto;
border: 1px solid var(--foreground);
box-shadow: 3px 3px 0 var(--foreground);
background: var(--accent);
border-radius: 18px;
padding: 0.5rem 1rem;
a {
text-decoration: none;
color: var(--accent-fg);;
font-size: 1.2rem;
font-family: "Lekton", sans-serif;
font-weight: bold;
display: flex;
align-items: center;
:global(svg) {
width: 1.5rem;
height: 1.5rem;
color: var(--accent-fg);
margin-right: 0.5rem;
}
}
}
.top-right {
position: absolute;
top: 0;
right: 1rem;
opacity: 0.8;
display: none;
&:hover {
opacity: 1;
}
ul {
list-style: none;
display: flex;
padding: 0;
gap: 0.5rem;
li {
&:not(:last-child) {
border-right: 1px solid var(--accent);
padding-right: 0.5rem;
}
}
li a {
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
}
</style>

View File

@ -0,0 +1,32 @@
---
const { text, url } = Astro.props;
---
<div class="button">
<a href={url}>{text}<slot /></a>
</div>
<style lang="scss">
.button {
font-family: "Lekton", sans-serif;
text-align: center;
border: 1px solid var(--foreground);
box-shadow: 3px 3px 0 var(--foreground);
background: var(--accent);
border-radius: 18px;
padding: 0.5rem 1rem;
transition: all 0.2s ease-in-out;
&:hover {
box-shadow: 4px 4px 0 var(--foreground);
}
a {
text-decoration: none;
color: var(--accent-fg);;
font-size: 1.2rem;
font-family: "Lekton", sans-serif;
font-weight: bold;
}
}
</style>

View File

@ -0,0 +1,54 @@
<script lang="ts">
import { FontAwesomeIcon } from '@fortawesome/svelte-fontawesome';
import * as solidIcons from '@fortawesome/pro-solid-svg-icons';
import * as brands from '@fortawesome/free-brands-svg-icons';
import type { IconDefinition } from '@fortawesome/fontawesome-svg-core';
export const iconMap: Record<string, IconDefinition> = {
// Branding
logo: solidIcons.faEyeSlash,
// GitHub Stat Icons
github: brands.faGithubAlt,
stars: solidIcons.faStar,
forks: solidIcons.faCodeFork,
issues: solidIcons.faBug,
license: solidIcons.faScaleBalanced,
owner: solidIcons.faUser,
language: solidIcons.faFileCode,
// Meta info icons
openSource: brands.faOsi,
closedSource: solidIcons.faHexagonExclamation,
securityAudited: solidIcons.faShieldKeyhole,
notSecurityAudited: solidIcons.faShieldXmark,
cryptoAccepted: brands.faMonero,
// Sections
essentials: solidIcons.faKey,
communication: solidIcons.faMessages,
networking: solidIcons.faNetworkWired,
'security-tools': solidIcons.faShieldHalved,
productivity: solidIcons.faBriefcase,
utilities: solidIcons.faWrench,
'operating-systems': brands.faLinux,
development: solidIcons.faCode,
'smart-home-&-iot': solidIcons.faHouseSignal,
finance: solidIcons.faCreditCard,
social: solidIcons.faShareNodes,
media: solidIcons.faPhotoFilmMusic,
creativity: solidIcons.faPaintbrushPencil,
};
export let iconName: string;
</script>
{#if iconMap[iconName]}
<FontAwesomeIcon
class="fa-icon"
icon={iconMap[iconName]} />
{/if}

View File

@ -0,0 +1,73 @@
---
interface IconProps {
icon: string;
color?: string;
class?: string;
width?: number;
height?: number;
}
const getSvgPath = (icon: string) => {
switch (icon) {
case 'star':
return {
vb: "0 0 24 24",
path: "M10 15l-5.5 3 1-5.5L0 7.5l5.6-0.5L10 2l2 5 5.5 0.5-4 4 1 5.5z",
};
case 'mastodon':
return {
vb: "0 0 512 512",
path: "M433 179.1c0-97.2-63.7-125.7-63.7-125.7-62.5-28.7-228.6-28.4-290.5 0 0 0-63.7 28.5-63.7 125.7 0 115.7-6.6 259.4 105.6 289.1 40.5 10.7 75.3 13 103.3 11.4 50.8-2.8 79.3-18.1 79.3-18.1l-1.7-36.9s-36.3 11.4-77.1 10.1c-40.4-1.4-83-4.4-89.6-54a102.5 102.5 0 0 1 -.9-13.9c85.6 20.9 158.7 9.1 178.8 6.7 56.1-6.7 105-41.3 111.2-72.9 9.8-49.8 9-121.5 9-121.5zm-75.1 125.2h-46.6v-114.2c0-49.7-64-51.6-64 6.9v62.5h-46.3V197c0-58.5-64-56.6-64-6.9v114.2H90.2c0-122.1-5.2-147.9 18.4-175 25.9-28.9 79.8-30.8 103.8 6.1l11.6 19.5 11.6-19.5c24.1-37.1 78.1-34.8 103.8-6.1 23.7 27.3 18.4 53 18.4 175z",
};
case 'twitter':
return {
vb: "0 0 512 512",
path: "M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z",
};
case 'hub':
return {
vb: "0 0 512 512",
path: "M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z",
};
case 'dev':
return {
vb: "0 0 512 512",
path: "M120.1 208.3c-3.9-2.9-7.8-4.4-11.7-4.4H91v104.5h17.5c3.9 0 7.8-1.5 11.7-4.4 3.9-2.9 5.8-7.3 5.8-13.1v-69.7c0-5.8-2-10.2-5.8-13.1zM404.1 32H43.9C19.7 32 .1 51.6 0 75.8v360.4C.1 460.4 19.7 480 43.9 480h360.2c24.2 0 43.8-19.6 43.9-43.8V75.8c-.1-24.2-19.7-43.8-43.9-43.8zM154.2 291.2c0 18.8-11.6 47.3-48.4 47.3h-46.4V173h47.4c35.4 0 47.4 28.5 47.4 47.3l0 70.9zm100.7-88.7H201.6v38.4h32.6v29.6H201.6v38.4h53.3v29.6h-62.2c-11.2 .3-20.4-8.5-20.7-19.7V193.7c-.3-11.2 8.6-20.4 19.7-20.7h63.2l0 29.5zm103.6 115.3c-13.2 30.8-36.9 24.6-47.4 0l-38.5-144.8h32.6l29.7 113.7 29.6-113.7h32.6l-38.5 144.8z",
};
case 'linkedin':
return {
vb: "0 0 512 512",
path: "M416 32H31.9C14.3 32 0 46.5 0 64.3v383.4C0 465.5 14.3 480 31.9 480H416c17.6 0 32-14.5 32-32.3V64.3c0-17.8-14.4-32.3-32-32.3zM135.4 416H69V202.2h66.5V416zm-33.2-243c-21.3 0-38.5-17.3-38.5-38.5S80.9 96 102.2 96c21.2 0 38.5 17.3 38.5 38.5 0 21.3-17.2 38.5-38.5 38.5zm282.1 243h-66.4V312c0-24.8-.5-56.7-34.5-56.7-34.6 0-39.9 27-39.9 54.9V416h-66.4V202.2h63.7v29.2h.9c8.9-16.8 30.6-34.5 62.9-34.5 67.2 0 79.7 44.3 79.7 101.9V416z",
};
case 'essentials':
return {
vb: "0 0 512 512",
path: "M208 176c0-70.7 57.3-128 128-128s128 57.3 128 128s-57.3 128-128 128c-10.4 0-20.5-1.2-30.1-3.6c-8.1-2-16.7 .4-22.6 6.4L254.1 336H200c-13.3 0-24 10.7-24 24v40H136c-13.3 0-24 10.7-24 24v40H48V385.9L205.2 228.7c5.9-5.9 8.3-14.5 6.4-22.6c-2.3-9.6-3.6-19.7-3.6-30.1zM336 0C238.8 0 160 78.8 160 176c0 9.5 .7 18.8 2.2 27.9L7 359c-4.5 4.5-7 10.6-7 17V488c0 13.3 10.7 24 24 24H136c13.3 0 24-10.7 24-24V448h40c13.3 0 24-10.7 24-24V384h40c6.4 0 12.5-2.5 17-7l27.2-27.2c9.1 1.4 18.4 2.2 27.9 2.2c97.2 0 176-78.8 176-176S433.2 0 336 0zm32 176a32 32 0 1 0 0-64 32 32 0 1 0 0 64z",
};
case 'communication':
return {
vb: "0 0 640 512",
path: "M48 72c0-13.3 10.7-24 24-24H344c13.3 0 24 10.7 24 24V248c0 13.3-10.7 24-24 24H216c-4.7 0-9.4 1.4-13.3 4L144 315.2V296c0-13.3-10.7-24-24-24H72c-13.3 0-24-10.7-24-24V72zM72 0C32.2 0 0 32.2 0 72V248c0 39.8 32.2 72 72 72H96v40c0 8.9 4.9 17 12.7 21.2s17.3 3.7 24.6-1.2l90-60H344c39.8 0 72-32.2 72-72V72c0-39.8-32.2-72-72-72H72zM256 376c0 39.8 32.2 72 72 72h88.7l90 60c7.4 4.9 16.8 5.4 24.6 1.2S544 496.9 544 488V448h24c39.8 0 72-32.2 72-72V200c0-39.8-32.2-72-72-72H448v48H568c13.3 0 24 10.7 24 24V376c0 13.3-10.7 24-24 24H520c-13.3 0-24 10.7-24 24v19.2L437.3 404c-3.9-2.6-8.6-4-13.3-4H328c-13.3 0-24-10.7-24-24V352H256v24z",
};
case 'security-tools':
return {
vp: "0 0 512 512",
path: "M232 60.8V447.4c-66.9-37.8-108.8-94.3-134.1-152.6C71 232.9 63.1 169.5 64.1 126L232 60.8zm48 386.5V60.8L448 126c1 43.5-6.9 106.9-33.8 168.8C388.8 353.1 346.9 409.5 280 447.3zM495.5 113l-1.2-20.5L475.1 85 267.6 4.5 256 0 244.4 4.5 36.9 85 17.8 92.5 16.6 113c-2.9 49.9 4.9 126.3 37.3 200.9c32.7 75.2 91 150 189.4 192.6L256 512l12.7-5.5c98.4-42.6 156.7-117.3 189.4-192.6c32.4-74.7 40.2-151 37.3-200.9z"
};
// Add more icons as needed...
default:
return { vb: "", path: "" }; // Default path or a placeholder icon
}
};
// Props are defined in the component's signature
const { icon, color = 'currentcolor', class: className = '', width = 80, height = 50 } = Astro.props as IconProps;
const svgStyle = { fill: color };
const { vb, path } = getSvgPath(icon);
---
<svg class={className} style={svgStyle} xmlns="http://www.w3.org/2000/svg" viewBox={vb} width={width} height={height ?? width}>
<path d={path} />
</svg>

View File

@ -0,0 +1,23 @@
<footer>
<a href="/about">Awesome Privacy</a> is licensed
under <a href="https://github.com/Lissy93/awesome-privacy/blob/main/LICENSE">MIT</a>
© <a href="https://aliciasykes.com">Alicia Sykes</a> 2024 |
Source code available on <a href="https://github.com/Lissy93/awesome-privacy">GitHub</a>
</footer>
<style lang="scss">
footer {
font-family: "Lekton", sans-serif;
font-weight: bold;
text-align: center;
padding: 0.5rem 0;
margin: 0 auto;
a {
font-family: "Lekton", sans-serif;
color: var(--accent);
}
}
</style>

View File

@ -0,0 +1,17 @@
<main>
<slot />
</main>
<style lang="scss">
main {
margin: 2rem auto;
padding: 1rem;
width: 1000px;
max-width: calc(100% - 5rem);
border: 2px solid var(--foreground);
box-shadow: 6px 6px 0 var(--foreground);
background: var(--accent-fg);
}
</style>

View File

@ -0,0 +1,90 @@
---
import FontAwesome from "@components/form/FontAwesome.svelte"
---
<div class="nav">
<a href="/" class="homepage">
<FontAwesome iconName="logo" />
<h1>Awesome Privacy</h1>
</a>
<nav>
<a href="/browse">Browse</a>
<a href="/search">Search</a>
<a href="/about">About</a>
<a href="https://github.com/lissy93/awesome-privacy">GitHub</a>
</nav>
</div>
<style lang="scss">
.nav {
background: var(--accent-fg);
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 1rem;
border-bottom: 2px solid var(--foreground);
.homepage {
text-decoration: none;
display: flex;
align-items: center;
padding: 0 0.5rem;
h1 {
margin: 0;
font-size: 2.4rem;
padding: 0 1rem;
color: var(--foreground);
font-family: "Lekton", sans-serif;
}
:global(svg) {
width: 2.5rem;
height: 2.5rem;
color: var(--accent-3);
transition: all 0.2s ease-in-out;
}
&:hover {
:global(svg) {
color: var(--accent);
transform: scale(1.05);
}
}
}
nav {
display: flex;
align-items: center;
height: 3rem;
a {
padding: 1rem;
font-size: 1.2rem;
font-family: "Lekton", sans-serif;
font-weight: bold;
color: var(--foreground);
border-left: 2px solid var(--foreground);
transition: background 0.3s, transform 0.3s, box-shadow 0.3s;
transition-timing-function: ease-in-out;
&:hover {
background: var(--accent-3);
color: var(--accent-fg);
border-bottom: 2px solid var(--foreground);
&:nth-child(4n+1) {
background-color: var(--accent);
}
&:nth-child(4n+2) {
background-color: var(--accent-2);
color: var(--foreground);
}
&:nth-child(4n+3) {
background-color: var(--accent-3);
}
&:nth-child(4n+4) {
background-color: var(--accent-4);
color: var(--foreground);
}
}
}
}
}
</style>

View File

@ -0,0 +1,24 @@
<svelte:head>
<script async lang="javascript">
var remark_config = {
host: "https://comments.as93.net",
site_id: "awesome-privacy",
components: ["embed"],
show_rss_subsription: true,
theme: "dark",
};
!(function (e, n) {
for (var o = 0; o < e.length; o++) {
var r = n.createElement("script"),
c = ".js",
d = n.head || n.body;
"noModule" in r ? ((r.type = "module"), (c = ".mjs")) : (r.async = !0),
(r.defer = !0),
(r.src = remark_config.host + "/web/" + e[o] + c),
d.appendChild(r);
}
})(remark_config.components || ["embed"], document);
</script>
</svelte:head>
<div id="remark42" />

View File

@ -0,0 +1,154 @@
---
import FontAwesome from "@components/form/FontAwesome.svelte";
const { github } = Astro.props;
// const [user, repo] = github.split("/");
/**
* For a given `user/repo` fetch repository stats from the GitHub API data
* If API key is available through env var, use it to increase rate limit
* Returns the response data and status code (200 == success)
* @param repo
*/
const fetchGitHubData = async (repo: string) => {
const apiKey = import.meta.env.GITHUB_API_KEY;
const headers = new Headers();
if (apiKey) {
headers.append("Authorization", `token ${apiKey}`);
}
let data = {};
let statusCode = 0;
const response = await fetch(`https://api.github.com/repos/${repo}`, {
headers: headers,
}).catch((e) => {
console.error(`Network error: ${e.message}`);
// Return a placeholder response to handle this gracefully
return null;
});
// Check if fetch was successful but caught by the catch block
if (!response) {
return { data, statusCode: 503 }; // Use an appropriate error status code
}
statusCode = response.status;
// Only attempt to parse JSON if the response was OK
if (response.ok) {
data = await response.json();
} else {
console.error(`HTTP error: Received status code ${response.status}`);
}
return { data, statusCode };
};
/**
* Given a license object, return SPDX ID, or a formatted name
* @param license
*/
const formatLicense = (license: { spdx_id?: string, name?: string }) => {
if (!license) {
return "Unknown";
}
if (license.spdx_id === "NOASSERTION") {
return "No License";
}
return license.spdx_id;
}
/**
* Given a (possibly large) number, return a formatted string
* E.g. If greater than thousand, then return in k format
* @param num
*/
const formatBigNumber = (num: number) => {
if (num > 1000) {
return `${(num / 1000).toFixed(1)}k`;
}
return num;
}
// Initiate GitHub fetch, and make available to the component
const stats = (await fetchGitHubData(github)) as any;
const {
stargazers_count, forks_count, open_issues_count, language, license,
} = stats.data;
---
<div class="github-metrics">
<!-- <span title={`Built / owned by @${user}`}>
<img src={`https://github.com/${user}.png`} alt={user || owner?.login} width="20" height="20" />
</span> -->
<!-- <span title={`${repo} is open source, with code published on GitHub`}>
<FontAwesome iconName="openSource" /> Open Source
</span> -->
<span title={`GitHub repository: ${github}`}>
<a href={`https://github.com/${github}`}><FontAwesome iconName="github" /> {github}</a>
</span>
{ stats.statusCode === 200 && stats.data && (
<span title={`GitHub Star Count: ${stargazers_count}`}>
<FontAwesome iconName="stars" /> {formatBigNumber(stargazers_count)}
</span>
<span title={`GitHub Fork Count: ${forks_count}`}>
<FontAwesome iconName="forks" /> {formatBigNumber(forks_count)}
</span>
<span title={`Open Issues / Bugs: ${open_issues_count}`}>
<FontAwesome iconName="issues" /> {formatBigNumber(open_issues_count)}
</span>
<span title={`Code licensed under ${license?.name}`}>
<FontAwesome iconName="license" /> {formatLicense(license)}
</span>
<span title={`Primary programming language ${language}`}>
<FontAwesome iconName="language" /> {language}
</span>
)}
</div>
<style lang="scss">
.github-metrics {
display: flex;
align-items: center;
gap: 1rem;
font-size: 0.9rem;
opacity: 0.6;
span {
display: flex;
align-items: center;
justify-content: center;
color: var(--foreground);
gap: 0.25rem;
padding: 0.5rem 0;
a {
color: var(--foreground);
text-decoration: none;
align-items: center;
display: flex;
gap: 0.25rem;
transition: all 0.2s ease-in-out;
color: var(--accent-3);
&:hover {
color: var(--accent-3);
text-decoration: underline;
}
}
img {
border-radius: var(--curve-md);
}
}
:global(svg) {
color: var(--foregroung);
width: 1.1rem;
height: 1.1rem;
}
}
</style>

View File

@ -0,0 +1,215 @@
<script lang="ts">
import { onMount } from 'svelte';
import Fuse from 'fuse.js';
import { slugify } from '@utils/fetch-data';
import type { Category, Section, Service, ShortService } from '../../types/Service';
import { formatLink } from '@utils/parse-markdown';
import { prepareSearchItems, searchOptions } from '@utils/do-searchy-searchy';
export let data: Category[];
export let previousSearch: string | undefined = undefined;
let fuse: Fuse<any>;
let searchQuery = '';
let results: any[] = [];
// Initialize Fuse.js
onMount(() => {
const items = prepareSearchItems(data);
fuse = new Fuse(items, searchOptions);
});
const makeResultLink = (cat?: string, sec?: string, itm?: string) => {
if (!cat) return '/'
if (!sec) return `/${slugify(cat)}`
if (!itm) return `/${slugify(cat)}/${slugify(sec)}`
return `/${slugify(cat)}/${slugify(sec)}#${slugify(itm)}`;
};
const makeResultText = (cat?: string, sec?: string, itm?: string) => {
if (itm) return itm;
if (sec) return sec;
if (cat) return cat;
return '';
};
const makeLogoSrc = (logo: string, url: string) => {
if (!logo && !url) return '/broken-image.png';
return logo || `https://icon.horse/icon/${formatLink(url)}`;
};
const makeTitle = (typ: string, desc: string) => {
if (desc && typ === 'Service') {
return `${desc.slice(0, 60)}...`
}
return '';
};
function handleKeyDown(event: KeyboardEvent) {
if (event.key === 'Enter') {
event.preventDefault();
if (window) {
window.location.href = `/search/${encodeURIComponent(searchQuery)}`;
}
}
if (event.key === 'Escape') {
searchQuery = '';
}
}
// Watch for changes in the search query and update results
$: if (searchQuery) {
results = fuse.search(searchQuery).map(result => result.item).splice(0, 25);
} else {
results = [];
}
</script>
<div class="search-wrap">
<label for="search">
What are you looking for?
{#if searchQuery.length > 0}
<span class="enter-hint">Press enter to view all results</span>
{/if}
</label>
<input
id="search"
placeholder={previousSearch || 'Start typing...'}
autocomplete="off"
bind:value={searchQuery}
on:keydown={handleKeyDown}
/>
{#if searchQuery.length > 0}
<div class="suggestions">
<ul>
{#each results as result}
<li class="result-row">
<a
href={makeResultLink(result.category, result.sectionName, result.name)}
title={makeTitle(result.type, result.description)}
>
<span class="name">
{#if result.type === 'Service'}
<img src={makeLogoSrc(result.logo, result.url)} alt={result.name} width="20" height="20" loading="lazy" />
{/if}
{makeResultText(result.category, result.sectionName, result.name)}
{#if result.itemCount}
<i>({result.itemCount})</i>
{/if}
</span>
<span class="path">
{result.category ? `${result.category}` : ''}
{result.sectionName ? `➔ ${result.sectionName}` : ''}
{result.name ? `➔ ${result.name}` : ''}
</span>
</a>
</li>
{/each}
</ul>
</div>
{/if}
</div>
<style lang="scss">
.search-wrap {
display: flex;
flex-direction: column;
position: relative;
margin: 1rem auto;
max-width: 900px;
margin: 0 auto;
width: 80vw;
label {
margin: 0.5rem 0;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
.enter-hint {
font-size:0.8rem;
opacity: 0.7;
}
}
input {
padding: 0.5rem 1rem;
font-size: 1.8rem;
border: 2px solid var(--foreground);
border-radius: var(--curve-lg);
box-shadow: 3px 3px 0 var(--foreground);
z-index: 4;
&:focus {
outline: none;
border-color: var(--accent);
box-shadow: 3px 3px 0 var(--accent);
}
}
.suggestions {
ul {
position: absolute;
background: white;
z-index: 3;
width: 100%;
list-style: none;
padding: 0;
margin: 0;
border: 2px solid var(--foreground);
border-radius: 0 0 var(--curve-lg) var(--curve-lg);
box-shadow: 3px 3px 0 var(--foreground);
transform: translateY(-0.5rem);
max-height: 500px;
overflow-y: scroll;
li.result-row {
padding: 0.5rem 1rem;
margin: 0.5rem 0;
a {
color: var(--foreground);
text-decoration: none;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
.name {
display: flex;
align-items: center;
gap: 0.5rem;
i {
color: var(--accent);
font-weight: bold;
font-style: normal;
}
img {
border-radius: var(--curve-md);
width: 1.25rem;
height: 1.25rem;
font-size: 10px;
color: var(--accent);
overflow: hidden;
background: #f453974d;
padding: 1px;
}
}
.path {
font-size: 0.85rem;
opacity: 0.7;
}
}
&:hover {
background: var(--accent);
.name i {
color: var(--accent-fg);
}
}
}
}
}
}
</style>

View File

@ -0,0 +1,151 @@
---
import FontAwesome from '@components/form/FontAwesome.svelte';
import { slugify } from '../../utils/fetch-data';
import type { Section } from '../../types/Service';
interface Props {
title: string;
sections: Section[];
bigTitle?: boolean;
}
const { title, sections, bigTitle } = Astro.props;
---
<div class="wrap">
{ bigTitle ?
<h2><FontAwesome iconName={slugify(title)} />{title} </h2> :
<a class="category-title" href={`/${slugify(title)}`}><h3>{title}</h3></a>
}
{ !bigTitle && <span class="section-icon"><FontAwesome iconName={slugify(title)} /></span> }
<ul>
{sections.map((section) => (
<li class="section">
<a href={`/${slugify(title)}/${slugify(section.name)}`}>
<span>{section.name}</span>
<span class="service-count">({section.services ? section.services.length : 0})</span>
</a>
</li>
))}
</ul>
</div>
<style lang="scss">
.wrap {
position: relative;
&:hover {
.section-icon :global(svg){
opacity: 1;
transform: scale(1.2);
}
}
}
h2 {
font-family: "Lekton", sans-serif;
font-size: 2rem;
margin: -2rem 0 2rem -2rem;
box-shadow: 6px 6px 0 var(--foreground);
background: var(--accent);
color: var(--accent-fg);
width: fit-content;
padding: 0.25rem 0.5rem;
display: flex;
justify-content: center;
gap: 1rem;
:global(svg) {
width: 2rem;
height: 2rem;
color: var(--accent-fg);
}
}
.category-title {
text-decoration: none;
color: var(--foreground);
z-index: 2;
position: relative;
h3 {
font-family: "Lekton", sans-serif;
font-weight: bold;
margin: 0;
font-size: 1.8rem;
position: relative;
&:after {
background: none repeat scroll 0 0 transparent;
bottom: 0;
content: "";
display: block;
height: 3px;
left: 50%;
position: absolute;
background: var(--accent);
transition: width 0.3s ease 0s, left 0.3s ease 0s;
width: 0;
}
&:hover:after {
width: 80%;
left: 0;
}
}
}
ul {
list-style: circle;
padding-left: 1rem;
li {
margin: 0.5rem 0;
font-size: 1.25rem;
a {
text-decoration: none;
color: var(--foreground);
position: relative;
&:after {
background: none repeat scroll 0 0 transparent;
bottom: 0;
content: "";
display: block;
height: 2px;
left: 50%;
position: absolute;
background: var(--accent-3);
transition: width 0.15s ease 0s, left 0.15s ease 0s;
width: 0;
}
&:hover:after {
text-decoration: underline;
width: 80%;
left: 0;
}
}
.service-count {
color: var(--accent-3);
}
}
}
.section-icon {
position: absolute;
right: 0;
top: 0;
width: fit-content;
:global(svg) {
width: 2rem;
height: 2rem;
opacity: 0.5;
text-shadow: 3px 3px 0 black;
color: var(--accent-3);
transition: all 0.2s ease-in-out;
}
}
</style>

View File

@ -0,0 +1,226 @@
---
import Button from '@components/form/Button.astro';
import { parseMarkdown, formatLink } from '@utils/parse-markdown';
import type { Service } from 'src/types/Service';
import FontAwesome from '@components/form/FontAwesome.svelte';
import { slugify } from '@utils/fetch-data';
import GitHubMetrics from '@components/things/ItemGitHubMetrics.astro';
const { services, subHeading, buttonLink, noGitHubMetrics } = Astro.props;
---
<script>
document.addEventListener('DOMContentLoaded', () => {
const serviceIcons = document.querySelectorAll<HTMLImageElement>('.service-icon');
const broke = '/broken-image.png';
serviceIcons.forEach(function(icon) {
icon.onerror = function() {
const imgElement = this as HTMLImageElement;
const serviceUrl = imgElement.getAttribute('data-service-url');
const newSrcAttribute = (imgElement.src.includes('on.ho') ? broke : `https://icon.horse/icon/${serviceUrl}`);
imgElement.src = imgElement.src !== newSrcAttribute ? newSrcAttribute : broke;
imgElement.onerror = null;
};
});
});
</script>
<section>
{services ? (
<ul>
{services.map((service: Service) => (
<li id={slugify(service.name)}>
<div class="service-head">
<img
width="40"
height="40"
loading="lazy"
decoding="async"
class="service-icon"
alt={`${service.name} Icon`}
data-service-url={formatLink(service.url)}
src={service.icon || `https://icon.horse/icon/${formatLink(service.url)}`}
/>
{subHeading ? <h4>{service.name}</h4> : <h3>{service.name}</h3>}
{service.followWith && <p class="follow-with">({service.followWith})</p> }
<a class="service-link" href={service.url}>{formatLink(service.url)}</a>
</div>
<div class="service-body">
<p set:html={parseMarkdown(service.description)}></p>
<div class="service-stats">
{ service.securityAudited && (
<span class="meta-item great" title={`${service.name} has been security audited by an accredited auditor, with results published publicly`}>
<FontAwesome iconName="securityAudited" /> Security Audited
</span>
)}
{ service.acceptsCrypto && (
<span class="meta-item great" title={`${service.name} accepts anonymous payment methods`}>
<FontAwesome iconName="cryptoAccepted" /> Crypto Payments Accepted
</span>
)}
{ service.securityAudited === false && (
<span class="meta-item warning" title={`${service.name} has not been audited`}>
<FontAwesome iconName="notSecurityAudited" /> No Security Audit
</span>
)}
{ (service.openSource === false) && (
<span class="warning">
<FontAwesome iconName="closedSource" />
Not Open Source
</span>
)}
{ service.openSource || (service.github && service.openSource !== false) ? (
<span class="meta-item great" title={`${service.name} is open source`}>
<FontAwesome iconName="openSource" /> Open Source
</span>
) : null }
{ service.github && !noGitHubMetrics && <GitHubMetrics github={service.github} /> }
{ service.github && noGitHubMetrics && (
<span class="meta-item" title={`View ${service.name} on GitHub`}>
<a href={`https://github.com/${service.github}`}>
<FontAwesome iconName="github" /> {service.github}
</a>
</span>
) }
</div>
</div>
</li>
))}
</ul>
) : (
<p class="nothing-yet">
<strong>⚠️ This section is still a work in progress ⚠️</strong><br />
Check back soon, or help us complete it by submiting a pull request on GitHub.
</p>
)}
{buttonLink && ( <Button text="View More..." url={buttonLink} /> )}
</section>
<style lang="scss">
section {
padding: 1rem 0;
position: relative;
&:not(:last-child) {
border-bottom: 2px solid var(--accent-3);
}
}
.nothing-yet {
font-size: 1.4rem;
opacity: 0.8;
font-style: italic;
text-align: center;
margin-bottom: 3rem;
}
ul {
list-style: none;
padding: 0;
margin: 0 0 3rem 0;
li {
margin-bottom: 1rem;
.service-head {
display: flex;
align-items: center;
gap: 0.5rem;
h3, h4 {
margin: 0;
font-size: 1.6rem;
}
.service-icon {
width: 2.5rem;
height: 2.5rem;
border-radius: var(--curve-sm);
font-size: 10px;
overflow: hidden;
color: var(--accent);
}
.follow-with {
opacity: 0.7;
font-style: italic;
margin: 0;
}
.service-link {
max-width: 300px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
}
.service-body {
margin: 0.5rem 0 2rem;
opacity: 0.8;
:global(p) {
margin: 0;
font-size: 1.2rem;
:global(a) {
color: var(--foregorund);
}
}
.service-stats {
display: flex;
gap: 1rem;
}
.meta-item, .warning {
display: flex;
align-items: center;
// justify-content: center;
gap: 0.25rem;
opacity: 0.6;
font-size: 0.9rem;
padding: 0.5rem 0;
:global(svg) {
color: var(--foreground);
width: 1.2rem;
height: 1.2rem;
}
a {
text-decoration: none;
color: var(--foreground);
display: flex;
gap: 0.25rem;
&:hover {
color: var(--accent-3);
:global(svg) {
color: var(--accent-3);
}
}
}
}
.warning {
color: var(--danger);
:global(svg) {
color: var(--danger);
}
}
.great {
color: #007930; // var(--success);
:global(svg) {
color: #007930; // var(--success);
}
}
}
}
}
section :global(.button) {
width: fit-content;
position: absolute;
right: 1rem;
margin-top: -2.5rem;
background: var(--accent-3);
}
</style>

View File

@ -0,0 +1,144 @@
<script lang="ts">
import { onMount } from "svelte";
import { writable } from "svelte/store";
import type { Category, Service } from "../../types/Service";
import { formatLink } from '@utils/parse-markdown';
import { slugify } from '@utils/fetch-data';
interface ServiceResult extends Service {
path: string;
}
export let categories: Category[];
export let searchTerm: string;
let results = writable<ServiceResult[]>([]);
const normalize = (str: string) => str.toLowerCase().replace(/[^a-z0-9]/g, '');
onMount(async () => {
const apiEndpoint = `https://awesome-privacy.as93.workers.dev/${searchTerm}`;
const fetchedServices = await fetch(apiEndpoint)
.then((response) => response.json())
.then((data) => (JSON.parse(data) || []).map((servName: string) => normalize(servName)));
const tmpResults: ServiceResult[] = [];
categories.forEach((category) => {
(category.sections || []).forEach((section) => {
(section.services || []).forEach((service) => {
if (fetchedServices.includes(normalize(service.name))) {
const path = `/${slugify(category.name)}/${slugify(section.name)}#${slugify(service.name)}`
tmpResults.push({ ...service, path });
return;
}
});
});
});
results.set(tmpResults);
});
</script>
{#if $results.length === 1}
<h3>Top Result</h3>
{/if}
{#if $results.length > 1}
<h3>Top Results</h3>
{/if}
<section>
{#each $results as service (service)}
<a class="service-result" href={service.path}>
<div class="service-head">
<img
width="40"
height="40"
loading="lazy"
decoding="async"
class="service-icon"
alt={`${service.name} Icon`}
data-service-url={formatLink(service.url)}
src={service.icon || `https://icon.horse/icon/${formatLink(service.url)}`}
/>
<div>
<h4>
{service.name}
{#if service.followWith}
<p class="follow-with">({service.followWith})</p>
{/if}
</h4>
<a class="service-link" href={service.url}>{formatLink(service.url)}</a>
</div>
</div>
</a>
{/each}
</section>
<style lang="scss">
h3 {
width: 80vw;
max-width: 900px;
margin: 1rem auto;
color: var(--accent-3);
font-size: 1.6rem;
}
section {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1rem;
width: 80vw;
max-width: 900px;
margin: 0 auto;
.service-result {
background: var(--accent-fg);
padding: 1rem;
border-radius: var(--curve-sm);
border: 1px solid var(--foreground);
box-shadow: 3px 3px 0 var(--foreground);
color: var(--foreground);
text-decoration: none;
.service-head {
display: flex;
align-items: center;
gap: 0.5rem;
h4 {
margin: 0;
font-size: 1.6rem;
display: flex;
align-items: center;
gap: 0.5rem;
.follow-with {
opacity: 0.7;
font-style: italic;
margin: 0;
font-size: 0.85rem;
font-weight: 400;
max-width: 100px;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
}
.service-icon {
width: 2.5rem;
height: 2.5rem;
border-radius: var(--curve-sm);
font-size: 10px;
overflow: hidden;
color: var(--accent);
}
.service-link {
max-width: 300px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
}
}
}
</style>

2
web/src/env.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" />

View File

@ -0,0 +1,107 @@
---
import { ViewTransitions } from 'astro:transitions'
import NavBar from '@components/scafold/NavBar.astro';
import Footer from '@components/scafold/Footer.astro';
interface Props {
title?: string; // Page title
description?: string; // Overide description tag
keywords?: string; // Overide keywords tag
hideNav?: boolean; // Don't show the navbar (just homepage)
author?: string; // Author of the content
}
const {
title='Awesome Privacy | The Ultimate List of Private Apps',
description='Your guide to finding privacy-respecting alternatives to popular software and services.',
keywords='security, privacy, awesome privacy, data collection, free software, open source, privacy tools, privacy respecting software',
hideNav=false,
author='Alicia Sykes'
} = Astro.props;
---
<!doctype html>
<html lang="en">
<head>
<ViewTransitions />
<!-- Core info -->
<title>{title}</title>
<meta name="description" content={description}>
<meta name="keywords" content={keywords}>
<meta name="author" content={author}>
<!-- Page info, viewport, Astro credit -->
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<!-- Icons and colors -->
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="icon" type="image/png" sizes="64x64" href="/favicon.png">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<!-- Social media meta tags (Open Graphh + Twitter) -->
<meta property="og:type" content="website">
<meta property="og:url" content="https://awesome-privacy.xyz">
<meta property="og:title" content={title}>
<meta property="og:description" content={description}>
<meta property="og:image" content="/banner.png">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:url" content="https://awesome-privacy.xyz">
<meta name="twitter:title" content={title}>
<meta name="twitter:description" content={description}>
<meta name="twitter:image" content="/banner.png">
<!-- Non-tracking hit counter -->
<script defer
type="text/partytown"
data-domain="awesome-privacy.xyz"
src="https://no-track.as93.net/js/script.js">
</script>
</head>
<body>
{!hideNav && <NavBar /> }
<slot />
<Footer />
</body>
</html>
<style is:global>
@import '../styles/values.css';
@import '../styles/typography.css';
</style>
<style is:global>
:root {
--accent: 136, 58, 234;
--accent-light: 224, 204, 250;
--accent-dark: 49, 10, 101;
--accent-gradient: linear-gradient(
45deg,
rgb(var(--accent)),
rgb(var(--accent-light)) 30%,
white 60%
);
}
html {
background: var(--background);
background-size: 224px;
::selection {
background: var(--accent);
color: var(--accent-fg);
}
scroll-behavior: smooth;
}
body {
margin: 0;
min-height: 100vh;
background: #feecff;
background: -webkit-radial-gradient(circle, #feecff 0%, #e1e4fb 100%);
background: radial-gradient(circle, #feecff 0%, #e1e4fb 100%);
}
</style>

98
web/src/pages/404.astro Normal file
View File

@ -0,0 +1,98 @@
---
import Layout from '@layouts/Layout.astro';
import Buton from '@components/form/Button.astro';
---
<Layout title="404 | Awesome Privacy">
<article class="oh-crap">
<h2>404</h2>
<p class="what-happened">Page not found 😢</p>
<p class="why-happened">
It seems this page doesn't exist (yet). We're sorry about that.
</p>
<span class="back-you-go-then"><Buton url="/">Go back home</Buton></span>
<nav class="other-places">
Looking for somewhere else?
<ul>
<li>
<a href="/search">Search</a>
</li>
<li>
<a href="/browse">Browse</a>
</li>
<li>
<a href="/about">About</a>
</li>
<li>
<a href="https://github.com/lissy93/awesome-privacy">Source</a>
</li>
<li>
<a href="https://as93.net">More Apps</a>
</li>
</ul>
</nav>
</article>
</Layout>
<style lang="scss">
.oh-crap {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: calc(100vh - 6rem);
h2 {
font-size: 12rem;
margin: 0;
}
.what-happened {
font-size: 4rem;
margin: 0;
opacity: 0.8;
}
.why-happened {
font-size: 1.5rem;
text-align: center;
margin-bottom: 2rem;
opacity: 0.6;
}
.back-you-go-then :global(a) {
font-size: 2rem;
}
.other-places {
opacity: 0.8;
margin: 2rem auto 1rem auto;
text-align: center;
&:hover {
opacity: 1;
}
ul {
list-style: none;
display: flex;
padding: 0;
gap: 0.5rem;
li {
&:not(:last-child) {
border-right: 1px solid var(--accent);
padding-right: 0.5rem;
}
}
li a {
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
}
}
</style>

View File

@ -0,0 +1,277 @@
---
import Layout from '@layouts/Layout.astro';
import Button from '@components/form/Button.astro';
import Main from '@components/scafold/MainCard.astro';
import ServiceList from '@components/things/ServiceList.astro';
import type { AwesomePrivacy, Section, Service, ShortService } from '../types/Service';
import { fetchData, slugify } from '@utils/fetch-data';
import { parseMarkdown } from '@utils/parse-markdown';
interface Props {
slug: string,
title: string,
services: Service[],
intro?: string,
notableMentions?: ShortService[] | string,
furtherInfo?: string,
wordOfWarning?: string,
alternativeTo?: string[];
categoryName: string,
otherSections: Section[],
}
const {
title, otherSections, services, notableMentions, furtherInfo, wordOfWarning,
alternativeTo, categoryName, intro,
} = Astro.props;
/**
* Make a list of keywords, for the <meta keywords> tag
*/
const makeKeyWordTag = () => {
const keywords = [];
keywords.push(`free and open source ${title} software`);
keywords.push(`private ${title} comparison`);
(alternativeTo || []).forEach((alt: string) => {
keywords.push(`privacy-respecting ${alt} alternative`);
keywords.push(`free open source ${alt} alternative`);
});
(services || []).forEach((serv: Service) => {
keywords.push(serv.name);
});
keywords.push('ad free');
keywords.push('open source software');
keywords.push('privacy respecting apps');
return keywords.join(', ');
};
/**
* Make a page title
*/
const makePageTitle = () => {
return `${title} | Awesome Privacy`;
};
/**
* Make a string page intro, for the description tag
*/
const makeDescriptionTag = () => {
let description = `A list of privacy respecting ${title}. `;
if (services && services.length > 0) {
const serviceList = services.map((serv: Service) => serv.name);
description += `Compare ${serviceList.join(', ')} and more private apps and services. `
} else {
description += `Find private apps and services. This section is still a work in progress. `;
}
description += 'All this, and much more at Awesome Privacy, '
description += 'the free and open source list of private software alternatives.';
return description;
};
export async function getStaticPaths() {
const pages = await fetchData().then((data) => {
const results: Array<Props> = [];
if (!data || !data.categories) return results;
(data as AwesomePrivacy).categories.forEach((category) => {
const sections = category.sections.map((section) => ({
slug: `${slugify(category.name)}/${slugify(section.name)}`,
title: section.name,
services: section.services,
intro: section.intro,
notableMentions: section.notableMentions,
furtherInfo: section.furtherInfo,
wordOfWarning: section.wordOfWarning,
alternativeTo: section.alternativeTo,
categoryName: category.name,
otherSections: category.sections,
}));
results.push(...sections);
});
return results;
});
return pages.map((props: Props) => {
return {params: { section: props.slug }, props };
});
}
const makePaginationLinks = () => {
const index = otherSections.findIndex(section => section.name === title);
const previousSection = index > 0 ? otherSections[index - 1].name : null;
const nextSection = index < otherSections.length - 1 ? otherSections[index + 1].name : null;
return { previous: previousSection, next: nextSection };
};
const { previous, next } = makePaginationLinks();
---
<Layout title={makePageTitle()} keywords={makeKeyWordTag()} description={makeDescriptionTag()} >
<Main>
<h2>{title}</h2>
{intro && (
<section class="intro">
<p set:html={parseMarkdown(intro)}></p>
</section>
)}
<ServiceList title={title} services={services} />
{notableMentions && (
<section class="notable-mentions">
<h3>Notable Mentions</h3>
{ Array.isArray(notableMentions) ?
<ul>
{notableMentions.map((mention) => (
<li>
<a href={mention.url}>{mention.name}</a>
{mention.description && (
<span>- </span>
<span set:html={parseMarkdown(mention.description)}></span>
)}
</li>
))}
</ul> :
<p set:html={parseMarkdown(notableMentions)}></p>
}
</section>
)}
{furtherInfo && (
<section class="further-info">
<h3>Notes</h3>
<p set:html={parseMarkdown(furtherInfo)}></p>
</section>
)}
{wordOfWarning && (
<section class="word-of-warning">
<h3>Word of Warning</h3>
<p set:html={parseMarkdown(wordOfWarning)}></p>
</section>
)}
<div class="pagination-navigation">
{ previous ? (
<Button url={`/${slugify(categoryName)}/${slugify(previous)}`}>
<span>← Previous</span>
<p>{previous}</p>
</Button>
) : <p class="nothing"></p>}
<a href={`/${slugify(categoryName)}`} class="go-to-category">
View all in {categoryName} ({otherSections.length})
</a>
{ next && (
<Button url={`/${slugify(categoryName)}/${slugify(next)}`}>
<span>Next →</span>
<p>{next}</p>
</Button>
)}
</div>
</Main>
</Layout>
<style lang="scss">
section {
padding-bottom: 1rem;
&:not(:last-child) {
border-bottom: 2px solid var(--accent-3);
}
}
h2 {
font-size: 2rem;
margin: -2rem 0 2rem -2rem;
box-shadow: 6px 6px 0 var(--foreground);
background: var(--accent);
color: var(--accent-fg);
width: fit-content;
padding: 0.25rem 0.5rem;
}
.pagination-navigation {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 0;
:global(.button) {
min-width: 120px;
width: fit-content;
padding: 0.25rem 1rem;
text-align: right;
&:first-child { text-align: left; }
p {
margin: 0;
font-weight: normal;
font-size: 1rem;
}
span {
font-size: 0.8rem;
}
}
.nothing {
width: 120px;
}
.go-to-category {
color: var(--foreground);
font-size: 0.8rem;
opacity: 0.5;
@media (max-width: 768px) {
display: none;
}
}
}
.intro {
font-style: italic;
opacity: 0.7;
}
.further-info, .notable-mentions, .word-of-warning {
h3 {
font-size: 1.4rem;
margin: 1rem 0;
}
:global(p) {
font-size: 0.9rem;
opacity: 0.7;
:global(strong) {
font-weight: 500;
}
:global(a) {
color: var(--foreground);
transition: all 0.15s ease-in-out;
&:hover {
color: var(--accent);
}
}
}
:global(strong) {
font-weight: 500;
}
:global(h4) {
font-size: 1.2rem;
margin: 0.5rem 0 0 0;
}
:global(ul) {
list-style: circle;
padding-left: 1rem;
font-size: 0.9rem;
opacity: 0.7;
li {
margin-bottom: 0.25rem;
:global(p) {
display: inline;
}
}
}
}
</style>

View File

@ -0,0 +1,102 @@
---
import Layout from '@layouts/Layout.astro';
import SectionList from '@components/things/SectionList.astro';
import Main from '@components/scafold/MainCard.astro';
import { fetchData, slugify } from '@utils/fetch-data';
import type { Section } from '../types/Service';
import Button from '@components/form/Button.astro';
interface Props {
title: string;
sections: Section[];
}
const { title, sections } = Astro.props;
const categories = (await fetchData()).categories;
export async function getStaticPaths() {
const pages = await fetchData().then((data) => {
return data.categories.map((category) => {
return {
category: slugify(category.name),
title: category.name,
sections: category.sections,
};
});
});
return pages.map(({ category, title, sections }) => {
return {
params: { category },
props: { title, sections },
};
});
}
const makePaginationLinks = () => {
const index = categories.findIndex(category => category.name === title);
const previousCategory = index > 0 ? categories[index - 1].name : null;
const nextCategory = index < categories.length - 1 ? categories[index + 1].name : null;
return { previous: previousCategory, next: nextCategory };
};
const { previous, next } = makePaginationLinks();
---
<Layout title={`${title} | Awesome Privacy`}>
<Main>
<SectionList title={title} sections={sections} bigTitle={true} />
<div class="pagination-navigation">
{ previous ? (
<Button url={`/${slugify(previous)}`}>
<span>← Previous</span>
<p>{previous}</p>
</Button>
) : <p class="nothing"></p>}
{ next && (
<Button url={`/${slugify(next)}`}>
<span>Next →</span>
<p>{next}</p>
</Button>
)}
</div>
</Main>
</Layout>
<style lang="scss">
.pagination-navigation {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 0;
:global(.button) {
min-width: 120px;
width: fit-content;
padding: 0.25rem 1rem;
text-align: right;
&:first-child { text-align: left; }
p {
margin: 0;
font-weight: normal;
font-size: 1rem;
}
span {
font-size: 0.8rem;
}
}
.nothing {
width: 120px;
}
.go-to-category {
color: var(--foreground);
font-size: 0.8rem;
opacity: 0.5;
}
}
</style>

429
web/src/pages/about.astro Normal file
View File

@ -0,0 +1,429 @@
---
import Layout from '@layouts/Layout.astro';
import Main from '@components/scafold/MainCard.astro';
import Icon from '@components/form/Icon.astro';
import { parseMarkdown } from '@utils/parse-markdown';
const contributorsResource = async () => {
const url = 'https://api.github.com/repos/lissy93/personal-security-checklist/contributors?per_page=100';
const response = await fetch(url);
if (!response.ok) {
throw new Error('Failed to fetch contributors');
}
return await response.json();
};
const sponsorsResource = async () => {
const url = 'https://github-sponsors.as93.workers.dev/lissy93';
const response = await fetch(url);
if (!response.ok) {
throw new Error('Failed to fetch sponsors');
}
return await response.json();
};
const licenseContent = async () => {
const url = 'https://raw.githubusercontent.com/Lissy93/awesome-privacy/HEAD/LICENSE';
const response = await fetch(url);
if (!response.ok) {
throw new Error('Failed to fetch license');
}
return await response.text();
};
const projectRequrements = `
For software to be included in this list, it must meet the following requirements:
- **Privacy Respecting**
- The project must respect users privacy, not collect more data than necessary, and store info securely
- For hosted services, the project must have a clear privacy policy
- The user must remain in full control of their data, and be able to delete it at any time
- **Secure**
- The software must be secure by default, without requiring additional configuration
- There should be no current, critical security issues
- The handling of past issues must have been prompt, transparent and effective
- **Open Source**
- The full source code should be released under an open source license
- Ideally it should be possible for the user to build and run/deploy the software themselves from source
- **Actively Maintained**
- The developers should address dependency updates and security patches in a timely manner
- **Transparent**
- It should be clear who is behind the project, what their motives are, and what (if any) the funding model is
- **Ethical**
- Must not suppress free speech, discriminate or disregard any human rights
- **Relevant**
- The software must be relevant, and fit into one of the existing categories
- **Functional**
- Must be fully functional, and not just a concept or idea
- A stable (non-alpha/beta) release is required at a minimum
- Must be accessible to the general public, and not just a select group of people
- If technical knowledge is required to run it, the software must be well documented
_There may be some exceptions, but these would need to be fully justified, reviewed
by the community, and the drawbacks / anti-features must be clearly listed along-side the software.
Usually these entries go within the "Notable Mentions" section instead._
`;
const projects = [
{
title: 'Email Comparison',
description: 'Objective comparison of private/secure mail providers',
icon: 'https://email-comparison.as93.net/favicon.png',
link: 'https://github.com/lissy93/email-comparison',
},
{
title: 'Personal Security Checklist',
description: 'Checklist of security tips, for protecting your privacy',
icon: 'https://i.ibb.co/Rb6P6h6/shield.png',
link: 'https://github.com/Lissy93/personal-security-checklist',
},
{
title: 'Web-Check',
description: 'OSINT tool for analysing any website',
icon: 'https://icon.horse/icon/web-check.xyz',
link: 'https://github.com/lissy93/web-check',
},
{
title: 'Dashy',
description: 'Dashboard app, for organising your self-hosted services',
icon: 'https://dashy.to/img/dashy.png',
link: 'https://github.com/lissy93/dashy',
},
{
title: 'Portainer-Templates',
description: 'Compiled repository of 1-click Docker apps for self-hosting',
icon: 'https://portainer-templates.as93.net/favicon.png',
link: 'https://github.com/lissy93/portainer-templates',
},
{
title: 'AdGuardian',
description: 'CLI tool for monitoring your networks traffic and AdGuard DNS stats',
icon: 'https://adguardian.as93.net/favicon.png',
link: 'https://github.com/lissy93/adguardian-term',
},
{
title: 'Bug-Bounties',
description: 'Database of websites which accept responsible vulnerability discolsure',
icon: 'https://bug-bounties.as93.net/favicon.png',
link: 'https://github.com/lissy93/bug-bounties',
},
{
title: 'Git-In',
description: 'Tools and resources to help beginners get into open source',
icon: 'https://www.git-in.to/favicon.png',
link: 'https://github.com/lissy93/git-in',
},
];
export const socials = [
{
title: 'GitHub',
icon: 'hub',
link: 'https://github.com/lissy93',
},
{
title: 'Twitter',
icon: 'twitter',
link: 'https://x.com/lissy_sykes',
},
{
title: 'Mastodon',
icon: 'mastodon',
link: 'https://mastodon.social/@lissy93',
},
{
title: 'Dev',
icon: 'dev',
link: 'https://dev.to/lissy93',
},
{
title: 'LinkedIn',
icon: 'linkedin',
link: 'https://linkedin.com/in/aliciasykes',
},
];
const description = 'Privacy is a fundamental human right; '
+ 'without it, we\'re just open books in a world where everyone\'s '
+ 'watching. Let\'s take control back.\n'
+ 'Migrating open-source applications which do not collect, sell or log your data is a great first step.'
+ 'Awesome Privacy is a directory of alternative privacy-respecting software and services.';
---
<Layout title="About | Awesome Privacy" description={description}>
<div class="about-page">
<Main>
<h2>Objective</h2>
<p>
Large data-hungry corporations dominate the digital world but with little,
or no respect for your privacy.
We believe that privacy is a fundamental human right and that it should be protected.
<br /><br />
Migrating to open-source applications and those with a strong emphasis on
privacy & security will help safegaurd you from corporations,
governments, and hackers who log, store or sell your sensetive personal data.
<br /><br />
Awesome Privacy is a directory of alternative software which respects your privacy.
<br /><br />
</p>
<hr />
<h2>Software Requirements</h2>
<div class="software-requirements"><p set:html={parseMarkdown(projectRequrements)}></p></div>
<hr />
<h2>Contributing</h2>
<p>
Awesome Privacy (including all data and code) is fully open source,
maintained by a core group of volenteers, with a lot of help from the community.
<br /><br />
We welcome suggestions, additions, edits and removals to the list.<br />
It's thanks to contributors like you that this project is possible 💜
<br /><br />
To get started, head over to our <a href="https://github.com/lissy93/awesome-privacy">GitHub repository</a>.
From here, you'll be able to edit <a href="#"><code>awesome-privacy.yml</code></a>
where all data is stored, and then submit your changes as a pull request.<br />
<br /><br />
If you're new to open source, you can find some resources to get you started
at <a href="https://git-in.to">git-in.to</a>, but feel free to reach out if you need any help 😊
</p>
<hr />
<h2>Credits</h2>
<h3>Sponsors</h3>
<p>Huge thanks to the following sponsors, for their ongoing support 💖</p>
<div class="user-list">
{sponsorsResource().then((sponsors) => {
return sponsors.map((sponsor: any) => (
<a class="user" href={`https://github.com/${sponsor.login}`} target="_blank" rel="noreferrer">
<img src={sponsor.avatarUrl} alt={sponsor.login} />
<p>{sponsor.name || sponsor.login}</p>
</a>
));
})}
</div>
<h3>Contributors</h3>
<p>
This project exists thanks to all the people who've helped build and maintain it.<br />
Special thanks to the below, top-100 contributors 🌟
</p>
<div class="user-list">
{contributorsResource().then((sponsors) => {
return sponsors.map((sponsor: any) => (
<a class="user" href={sponsor.html_url} target="_blank" rel="noreferrer">
<img src={sponsor.avatar_url} alt={sponsor.login} />
<p>{sponsor.login}</p>
</a>
));
})}
</div>
<hr />
<h2>Author</h2>
<article class="author">
<p>
This project was originally started by
me, <a href="https://aliciasykes.com">Alicia Sykes</a>
(with a lot of help from the community)
</p>
<br />
<p class="about">
I build apps which aim to help people escape big tech, secure their data,
and protect their privacy.
</p>
<br />
<div class="pic-and-socials">
<a href="https://aliciasykes.com">
<img class="pic" width="180" height="240" alt="Alicia Sykes" src="https://i.ibb.co/fq10qhL/DSC-0597.jpg" />
</a>
<div class="socials">
{
socials.map((social) => (
<a href={social.link}>
<Icon icon={social.icon} width={24} height={24} />
</a>
))
}
</div>
</div>
<p class="more-about">
I have a particular interest in self-hosting, Linux, security and OSINT.<br />
So if this type of stuff interests you, check out these other projects:
</p>
<ul>
{
projects.map((project) => (
<li>
<img width="20" height="20" alt={project.title} src={project.icon} />
<a href={project.link} target="_blank" rel="noreferrer">
{project.title}
</a> - {project.description}
</li>
))
}
<li>
...and loads more, over at <a href="https://apps.aliciasykes.com/">apps.aliciasykes.com</a>
and on <a href="https://github.com/lissy93">github.com/lissy93</a>
</li>
</ul>
<br />
<p>
If you'd like to support the ongoing work on Awesome Privacy,
and other similar projects,
consider <a href="https://github.com/sponsors/lissy93">sponsoring me</a> on GitHub 💖
</p>
</article>
<hr />
<h2>License</h2>
<p>
All content on Awesome Privacy is freely available, within the public domain,
licensed under Creative Commons Zero v1.0 Universal.
The code for the website is licensed under MIT.
</p>
<p>
{licenseContent().then((license) => {
return (<pre class="license-content">{license}</pre>)
})}
</p>
</Main>
</div>
</Layout>
<style lang="scss">
h2 {
font-size: 2rem;
margin: 2rem 0 1rem 0;
}
p {
font-size: 1.4rem;
}
hr {
margin: 2rem 0;
border: 0;
border-top: 2px solid var(--accent);
&:nth-child(6) {
border-color: var(--accent-3);
}
&:nth-child(9) {
border-color: var(--accent-2);
}
}
h3 {
font-size: 1.6rem;
}
.software-requirements {
:global(p) {
font-size: 1.4rem;
}
:global(ul) {
padding-left: 0.5rem;
list-style: none;
font-size: 1.1rem;
:global(li ul) {
padding: 0 0 0.5rem 1rem;
list-style: circle;
}
}
:global(ul li:hover strong) {
transition: color 0.2s ease-in-out;
color: var(--accent-3);
}
}
.user-list {
display: flex;
flex-wrap: wrap;
gap: 1rem;
margin: 2rem 0;
.user {
width: 6rem;
overflow: hidden;
text-align: center;
img {
width: 5rem;
height: 5rem;
border-radius: var(--curve-md);
}
p {
font-size: 1rem;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
}
}
}
.license-content {
max-height: 500px;
overflow: scroll;
background: #e6e6ee;
width: fit-content;
border-radius: var(--curve-sm);
padding: 0.5rem;
font-size: 0.7rem;
font-family: mono;
}
.author {
p {
margin: 1rem 0 0 0;
}
ul {
padding-left: 1.5rem;
list-style: circle;
margin-bottom: 0;
li {
font-size: 1.1rem;
}
}
.about {
font-size: 1.4rem;
font-style: italic;
font-weight: 300;
color: var(--accent-3);
text-align: center;
max-width: 550px;
margin: 0 auto;
}
small {
font-size: 1rem;
margin: 1rem 0;
}
.pic-and-socials {
float:right;
img {
margin: 0.5rem 1rem;
border-radius: var(--curve-md);
border: 2px solid var(--foreground);
box-shadow: 4px 4px 0 var(--foreground);
}
.socials {
display: flex;
margin: 0.5rem 1rem;
justify-content: space-between;
:global(a svg) {
color: var(--foreground);
transition: color 0.2s ease-in-out;
&:hover {
color: var(--accent);
}
}
}
}
}
</style>

205
web/src/pages/all.astro Normal file
View File

@ -0,0 +1,205 @@
---
import Layout from '@layouts/Layout.astro';
import ServiceList from '@components/things/ServiceList.astro';
import type { AwesomePrivacy, Section } from '../types/Service';
import { fetchData, slugify } from '@utils/fetch-data';
import FontAwesome from '@components/form/FontAwesome.svelte';
const categories = (await fetchData() as AwesomePrivacy)?.categories || [];
---
<script>
const toggleSidebar = () => {
const mainContent = document.querySelector('#content');
mainContent && mainContent.classList.toggle('narrow');
const sidebar = document.querySelector('.sidebar');
sidebar && sidebar.classList.toggle('hidden');
const button = document.querySelector('#toggle-sidebar');
if (button) {
button.textContent = (button.textContent === "Show Contents" ? "Hide Contents" : "Show Contents");
}
};
const tgt = document.querySelector("#toggle-sidebar");
tgt && tgt.addEventListener("click", () => {
toggleSidebar();
});
</script>
<Layout title="Awesome Privacy">
<main>
<div id="content">
<button id="toggle-sidebar">Show Contents</button>
{categories.map((category) => (
<h2 id={`${slugify(category.name)}`}>
<FontAwesome iconName={slugify(category.name)} />
<a href={`/${slugify(category.name)}`}>{category.name}</a>
</h2>
<article>
{category.sections.map((section: Section) => (
<section class="card">
<h3 id={`${slugify(category.name)}-${slugify(section.name)}`}>
<a href={`/${slugify(category.name)}/${slugify(section.name)}`}>{section.name}</a>
</h3>
<ServiceList
title={section.name}
services={section.services}
subHeading={true}
buttonLink={`/${slugify(category.name)}/${slugify(section.name)}`}
noGitHubMetrics={true}
/>
</section>
))}
</article>
))}
</div>
<div class="sidebar hidden">
<p class="sidebar-title">Contents</p>
<ul>
{categories.map((category) => (
<li class="top-level">
<a href={`#${slugify(category.name)}`}>{category.name}</a>
</li>
<li>
<ul>
{category.sections.map((section: Section) => (
<li>
<a href={`#${slugify(category.name)}-${slugify(section.name)}`}>{section.name}</a>
</li>
))}
</ul>
</li>
))}
</ul>
</div>
</main>
</Layout>
<style lang="scss">
main {
margin: 2rem auto;
padding: 1rem;
width: 1000px;
max-width: calc(100% - 5rem);
display: flex;
justify-content: space-between;
}
.narrow {
width: 70%;
}
.hidden {
display: none;
}
#toggle-sidebar {
// float: right;
background: none;
outline: none;
border: none;
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
.sidebar {
width: 25%;
height: 87vh;
overflow: scroll;
position: fixed;
right: 1rem;
border-radius: var(--curve-md);
.sidebar-title {
text-align: center;
font-size: 1.6rem;
margin: 0;
background: var(--accent-3);
color: var(--accent-fg);
border: 2px solid var(--foreground);
box-shadow: 6px 6px 0 var(--foreground);
border-radius: var(--curve-sm);
margin: 0 1rem;
}
ul {
list-style: none;
padding-left: 1.6rem;
a {
text-decoration: none;
color: var(--foreground);
opacity: 0.6;
transition: all 0.18s ease-in-out;
&:hover {
color: var(--accent);
opacity: 1;
}
}
li ul {
list-style: circle;
}
.top-level {
font-weight: bold;
margin-top: 0.5rem;
&:hover a {
color: var(--accent-3);
}
}
}
}
.card {
margin: 2rem auto 5rem auto;
padding: 0 2rem;
border: 2px solid var(--foreground);
box-shadow: 6px 6px 0 var(--foreground);
background: var(--accent-fg);
}
h2 {
margin: 0;
font-size: 3rem;
text-align: center;
opacity: 0.6;
display: flex;
justify-content: center;
align-items: center;
gap: 1rem;
a {
color: var(--accent-3);
text-decoration: none;
}
:global(svg) {
width: 3rem;
height: 3rem;
color: var(--accent-3);
}
}
h3 {
font-size: 2rem;
margin: -2rem 0 2rem -4rem;
box-shadow: 6px 6px 0 var(--foreground);
background: var(--accent);
color: var(--accent-fg);
width: fit-content;
padding: 0.25rem 0.5rem;
a {
color: var(--accent-fg);
font-family: "Lekton", sans-serif;
text-decoration: none;
}
}
</style>

203
web/src/pages/browse.astro Normal file
View File

@ -0,0 +1,203 @@
---
import Layout from '@layouts/Layout.astro';
import SectionList from '@components/things/SectionList.astro';
import { fetchData } from '@utils/fetch-data';
import type { Category } from '../types/Service';
const categories: Category[] = (await fetchData())?.categories;
---
<Layout title="Browse">
<div class="head-wrap">
<h2>Browse</h2>
<span>
<input id="searchInput" type="search" placeholder="Start typing to filter..." />
<p id="press-enter-msg" class="press-enter">Press enter for deep search</p>
</span>
</div>
<ul class="categories">
{categories.map((category) => (
<li class="category">
<SectionList title={category.name} sections={category.sections} />
</li>
))}
</ul>
<div class="no-results">
<p class="zilch">Nothing found 😢</p>
<p>Try a <a href="/search">deep search</a> instead</p>
</div>
</Layout>
<script>
// Time for some good ol' fashioned vanilla JS...
// I reccomend you not to look at this code for too long, it's not pretty.
const filterInput = document.querySelector('input');
const categories = document.querySelectorAll<HTMLElement>('.category');
const pressEnterMsg = document.getElementById('press-enter-msg') as HTMLElement | null;
const noResults = document.querySelector('.no-results') as HTMLElement | null;
if (!pressEnterMsg || !noResults) {
throw new Error('No pressEnterMsg or noResults');
};
filterInput?.addEventListener('input', (e) => {
let resultsCount = 0;
const filter = (e.target as HTMLInputElement).value.toLowerCase();
if (filter.length > 0) {
pressEnterMsg.style.visibility = 'visible';
} else {
pressEnterMsg.style.visibility = 'hidden';
}
categories.forEach((category) => {
const titleElement = category.querySelector('.category-title');
const title = titleElement ? titleElement.textContent?.toLowerCase() : '';
const sections = category.querySelectorAll<HTMLElement>('.section');
let count = 0;
sections.forEach((section) => {
const sectionTitle = section.textContent?.toLowerCase();
if (sectionTitle?.includes(filter)) {
section.style.display = 'block';
count++;
resultsCount++;
} else {
section.style.display = 'none';
}
});
if (title && title.includes(filter)) {
category.style.display = 'inline-flex';
resultsCount++;
} else if (count === 0) {
category.style.display = 'none';
}
});
noResults.style.display = resultsCount === 0 ? 'block' : 'none';
});
if (typeof window !== 'undefined') {
document.addEventListener('DOMContentLoaded', function() {
const searchInput = document.getElementById('searchInput') as HTMLInputElement | null;
if (searchInput === null) return;
searchInput.addEventListener('keydown', function(event) {
if (event.key === 'Enter') {
event.preventDefault();
const searchTerm = encodeURIComponent(searchInput.value);
window.location.href = `/search/${searchTerm}`;
}
if (event.key === 'Escape') {
searchInput.value = '';
searchInput.blur();
pressEnterMsg.style.visibility = 'hidden';
categories.forEach((category) => {
category.style.display = 'inline-flex';
const sections = category.querySelectorAll<HTMLElement>('.section');
sections.forEach((section) => {
section.style.display = 'block';
});
});
}
});
});
}
</script>
<style lang="scss">
.head-wrap {
margin: 1rem auto;
width: 85vw;
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
.press-enter {
margin: 0.5rem 0 0 0;
opacity: 0.5;
visibility: hidden;
}
input {
padding: 0.5rem;
border: 2px solid var(--foreground);
border-radius: var(--curve-sm);
font-size: 1.2rem;
box-shadow: 3px 3px 0 var(--foreground);
color: var(--accent-3);
&:focus {
outline: none;
box-shadow: 4px 4px 0 var(--foreground);
}
}
h2 {
font-family: "Lekton", sans-serif;
font-weight: bold;
font-size: 3rem;
margin: 0;
color: var(--accent-3);
}
}
.no-results {
text-align: center;
width: 85vw;
margin: 2rem auto 4rem auto;
display: none;
.zilch {
font-size: 2rem;
color: var(--accent-3);
opacity: 0.5;
font-weight: bold;
}
p {
margin: 0.25rem;
}
}
.categories {
columns: 6 300px;
column-gap: 1rem;
margin: 0 auto;
width: 85vw;
max-width: 1800px;
padding: 1rem 0 2rem 0;
.category {
background: var(--accent-fg);
border: 2px solid var(--foreground);
border-radius: var(--curve-sm);
box-shadow: 4px 4px 0 var(--foreground);
padding: 1rem;
width: 85%;
display: inline-flex;
flex-direction: column;
margin: 1rem;
.category-title {
text-decoration: none;
color: var(--foreground);
}
h3 {
font-family: "Lekton", sans-serif;
font-weight: bold;
margin: 0;
font-size: 1.8rem;
}
.sections {
padding-left: 1rem;
.section {
a {
text-decoration: none;
color: var(--foreground);
}
.service-count {
color: var(--accent-3);
}
}
}
}
}
</style>

168
web/src/pages/index.astro Normal file
View File

@ -0,0 +1,168 @@
---
import Layout from '@layouts/Layout.astro';
import Hero from '@components/Hero.astro';
import Search from '@components/things/Search.svelte';
import SectionList from '@components/things/SectionList.astro';
import { fetchData } from '@utils/fetch-data';
import Button from '@components/form/Button.astro';
import type { Category } from 'src/types/Service';
const categories = (await fetchData())?.categories || [] as Category[];
const description = 'Privacy is a fundamental human right; '
+ 'without it, we\'re just open books in a world where everyone\'s '
+ 'watching. Let\'s take control back.\n'
+ 'Migrating open-source applications which do not collect, sell or log your data is a great first step.'
+ 'Awesome Privacy is a directory of alternative privacy-respecting software and services.';
---
<Layout title="Home | Awesome Privacy" hideNav={true} description={description}>
<Hero />
<main>
<h2>
<a href="/search">Search</a>
</h2>
<Search client:visible data={categories} />
<h2 class="browse-title"><a href="/browse">Browse</a></h2>
<ul class="categories">
{categories.map((category) => (
<li class="category">
<SectionList title={category.name} sections={category.sections} />
</li>
))}
</ul>
<div class="view-all">
<span>Or, just</span>
<Button url="/all" text="View Everything" />
</div>
<h2>
<a href="/about">About</a>
</h2>
<div class="about-summary">
<p>
Awesome Privacy is a collection of privacy-respecting services and tools.
The aim is to help you escape big tech, and choose software that respects your privacy.
</p>
<p>
Why? Because privacy is a fundamental human right; without it, we're just open books
in a world where everyone's watching. Let's take control back.
</p>
<p>
Noticed something that should be added / removed / ammended?
We're a community-driven resource, so welcome contributions of any nature.
All content and code is <a href="https://github.com/lissy93/awesome-privacy">open source</a>.
</p>
<p>
If you've found Awesome Privacy useful, help us out by sharing it with others,
contributing, or consider <a href="https://github.com/sponsors/lissy93">sponsoring me</a> on GitHub.
</p>
</div>
<div class="view-all">
<span>Want to learn more?</span>
<Button url="/about" text="Keep Reading..." />
</div>
</main>
</Layout>
<style lang="scss">
main {
margin: auto;
padding: 1rem;
width: 1100px;
max-width: calc(100% - 5rem);
font-size: 20px;
line-height: 1.6;
.view-all {
text-align: center;
width: 16rem;
margin: 0 auto;
span {
opacity: 0.5;
font-size: 1rem;
}
}
}
h2 {
font-size: 3rem;
color: var(--accent-3);
font-family: "Lekton", sans-serif;
text-align: center;
margin: 3rem 0 1rem 0;
a {
text-decoration: none;
color: var(--accent-3);
font-family: "Lekton", sans-serif;
position: relative;
&:after {
background: none repeat scroll 0 0 transparent;
bottom: 0;
content: "";
display: block;
height: 3px;
left: 50%;
position: absolute;
background: var(--accent);
transition: width 0.3s ease 0s, left 0.3s ease 0s;
width: 0;
}
&:hover:after {
width: 100%;
left: 0;
}
}
}
.about-summary {
background: var(--accent-fg);
border: 2px solid var(--foreground);
border-radius: var(--curve-sm);
box-shadow: 4px 4px 0 var(--foreground);
padding: 1rem;
width: 85%;
margin: 0 auto;
}
.categories {
columns: 6 300px;
column-gap: 1rem;
.category {
background: var(--accent-fg);
border: 2px solid var(--foreground);
border-radius: var(--curve-sm);
box-shadow: 4px 4px 0 var(--foreground);
padding: 1rem;
width: 85%;
display: inline-flex;
flex-direction: column;
margin: 1rem;
.category-title {
text-decoration: none;
color: var(--foreground);
}
h3 {
font-family: "Lekton", sans-serif;
font-weight: bold;
margin: 0;
font-size: 1.8rem;
}
.sections {
padding-left: 1rem;
.section {
a {
text-decoration: none;
color: var(--foreground);
}
.service-count {
color: var(--accent-3);
}
}
}
}
}
</style>

View File

@ -0,0 +1,192 @@
---
import Fuse from 'fuse.js';
import Layout from '@layouts/Layout.astro';
import { fetchData, slugify } from '@utils/fetch-data';
import { prepareSearchItems, searchOptions } from '@utils/do-searchy-searchy';
import Search from '@components/things/Search.svelte';
import SmartSuggestions from '@components/things/SmartSuggestions.svelte';
import FontAwesome from '@components/form/FontAwesome.svelte';
import type { Service } from '../../types/Service';
export const prerender = false;
let fuse: Fuse<any>;
const categories = (await fetchData())?.categories;
const items = prepareSearchItems(categories);
fuse = new Fuse(items, searchOptions);
const searchTerm = Astro.params.searchTerm;
const searchResults = fuse.search(searchTerm || '').map(result => result.item);
const services = searchResults.filter(result => result.type === 'Service');
const putResultsIntoGroups = () => {
const grouped = services.reduce((acc, item) => {
const { category: categoryName, sectionName, ...service } = item;
if (!acc[categoryName]) {
acc[categoryName] = { categoryName, sections: {} };
}
if (!acc[categoryName].sections[sectionName]) {
acc[categoryName].sections[sectionName] = { sectionName, items: [] };
}
acc[categoryName].sections[sectionName].items.push(service);
return acc;
}, {});
// Convert the grouped object into the desired array structure.
// And fuck it, let's use `any`
return Object.values(grouped).map((category: any) => ({
categoryName: category.categoryName,
sections: Object.values(category.sections)
}));
};
const beer = putResultsIntoGroups();
---
<Layout title="Search | Awesome Privacy">
<section>
<h1>Search</h1>
<Search client:visible data={categories} previousSearch={searchTerm} />
</section>
<SmartSuggestions client:visible searchTerm={searchTerm || ''} categories={categories} />
<section class="result-count">
<h3>Deep Search</h3>
<p>Showing {services.length} results for "{searchTerm}" sorted by relevence</p>
</section>
<div class="results">
{
beer.map((category: any) => (
<div class="category">
<a class="category-title" href={`/${slugify(category.categoryName)}`}>
<h3>{category.categoryName}</h3>
</a>
<span class="section-icon"><FontAwesome iconName={slugify(category.categoryName)} /></span>
<ul class="section-list">
{category.sections.map((section: any) => (
<li class="section-item">
<h4>{section.sectionName}</h4>
<ul class="service-list">
{section.items.map((item: Service) => (
<li>
<a href={item.url}>{item.name}</a>
</li>
))}
</ul>
</li>
))}
</ul>
</div>
))
}
</div>
</Layout>
<style lang="scss">
section {
display: flex;
flex-direction: column;
padding: 1rem;
margin: 2rem auto;
padding: 1rem;
width: calc(100% - 2rem);
max-width: 1100px;
h1 {
font-size: 3rem;
color: var(--accent-3);
font-family: "Lekton", sans-serif;
text-align: center;
margin: 1rem 0;
}
}
.result-count {
padding: 0;
width: 80vw;
max-width: 900px;
margin: 1rem auto 0 auto;
h3 {
margin: 0;
color: var(--accent-3);
font-size: 1.6rem;
}
p {
margin: 0;
opacity: 0.6;
}
}
.results {
margin: 0 auto 2rem auto;
padding: 1rem;
width: calc(100% - 2rem);
max-width: 1100px;
columns: 6 300px;
column-gap: 1rem;
.category {
padding: 1rem;
box-shadow: 4px 4px 0 var(--foreground);
background: var(--accent-fg);
border: 2px solid var(--foreground);
border-radius: var(--curve-sm);
display: inline-flex;
flex-direction: column;
width: 85%;
margin: 1rem;
position: relative;
.category-title {
text-decoration: none;
color: var(--foreground);
z-index: 2;
position: relative;
h3 {
font-family: "Lekton", sans-serif;
font-weight: bold;
margin: 0;
font-size: 1.8rem;
}
}
.section-icon {
position: absolute;
right: 0.5rem;
top: 0.5rem;
width: fit-content;
:global(svg) {
width: 2rem;
height: 2rem;
opacity: 0.5;
text-shadow: 3px 3px 0 black;
color: var(--accent-3);
transition: all 0.2s ease-in-out;
}
}
.section-list {
list-style: none;
padding: 0;
margin: 0;
h4 {
margin: 1rem 0 0 0;
}
.service-list {
list-style: none;
padding: 0;
margin: 0;
}
}
}
}
</style>

View File

@ -0,0 +1,44 @@
---
import Layout from '@layouts/Layout.astro';
import { fetchData } from '@utils/fetch-data';
import Search from '@components/things/Search.svelte';
const categories = (await fetchData())?.categories;
---
<Layout title="Search | Awesome Privacy">
<section>
<h1>Search</h1>
<Search client:visible data={categories} />
</section>
</Layout>
<style lang="scss">
section {
display: flex;
flex-direction: column;
padding: 1rem;
box-shadow: 4px 4px 0 var(--foreground);
background: var(--accent-fg);
border: 2px solid var(--foreground);
border-radius: var(--curve-sm);
margin: 2rem auto;
padding: 1rem;
width: calc(100% - 5rem);
max-width: 1100px;
min-height: 80vh;
h1 {
font-size: 3rem;
color: var(--accent-3);
font-family: "Lekton", sans-serif;
text-align: center;
margin: 1rem 0;
}
}
</style>

View File

@ -0,0 +1,93 @@
/* Fallback to Google Fonts for specific weights and styles */
@import url('https://fonts.googleapis.com/css2?family=Lekton:wght@700&family=Libre+Franklin:wght@800&family=Rubik:ital,wght@0,400;0,500;0,600;1,400;1,500;1,600&display=swap');
/* Rubik Font Faces */
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 400;
src: local('Rubik'), url('/fonts/Rubik/Rubik-Regular.ttf') format('truetype');
}
@font-face {
font-family: 'Rubik';
font-style: italic;
font-weight: 400;
src: local('Rubik Italic'), url('/fonts/Rubik/Rubik-Italic.ttf') format('truetype');
}
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 500;
src: local('Rubik Medium'), url('/fonts/Rubik/Rubik-Medium.ttf') format('truetype');
}
@font-face {
font-family: 'Rubik';
font-style: italic;
font-weight: 500;
src: local('Rubik Medium Italic'), url('/fonts/Rubik/Rubik-MediumItalic.ttf') format('truetype');
}
@font-face {
font-family: 'Rubik';
font-style: normal;
font-weight: 600;
src: local('Rubik SemiBold'), url('/fonts/Rubik/Rubik-SemiBold.ttf') format('truetype');
}
@font-face {
font-family: 'Rubik';
font-style: italic;
font-weight: 600;
src: local('Rubik SemiBold Italic'), url('/fonts/Rubik/Rubik-SemiBoldItalic.ttf') format('truetype');
}
/* Libre Franklin Font Faces */
@font-face {
font-family: 'Libre Franklin';
font-style: normal;
font-weight: 500;
src: local('Libre Franklin Bold'), url('/fonts/Libre_Franklin/LibreFranklin-Bold.ttf') format('truetype');
}
/* Lekton Font Faces */
@font-face {
font-family: 'Lekton';
font-style: normal;
font-weight: 700;
src: local('Lekton Bold'), url('/fonts/Lekton/Lekton-Bold.ttf') format('truetype');
}
html {
font-family: system-ui, sans-serif;
}
code {
font-family:
Menlo,
Monaco,
Lucida Console,
Liberation Mono,
DejaVu Sans Mono,
Bitstream Vera Sans Mono,
Courier New,
monospace;
}
.heading, h1 {
font-family: "Libre Franklin", sans-serif;
font-optical-sizing: auto;
font-weight: 800;
font-style: normal;
}
.subtitle, h2, h3, h4, h5, h6 {
font-family: "Lekton", sans-serif;
font-weight: 700;
font-style: normal;
}
html, body, p, a, ul, ol, li, blockquote, pre, code, strong, i {
font-family: "Rubik", sans-serif;
}
a {
color: var(--accent);
}

17
web/src/styles/values.css Normal file
View File

@ -0,0 +1,17 @@
:root {
--accent: #f45397;
--accent-fg: #fff;
--accent-2: #ffdf60;
--accent-3: #5f53f4;
--accent-4: #28dffd;
--foreground: #13151a;
--curve-sm: 4px;
--curve-md: 6px;
--curve-lg: 12px;
--danger: #ff0048;
--success: #00ff64;
}

42
web/src/types/Service.ts Normal file
View File

@ -0,0 +1,42 @@
export interface ShortService {
name: string;
description: string;
url: string;
}
export interface Service {
name: string;
description: string;
url: string;
github?: string;
icon?: string;
followWith?: string;
securityAudited?: boolean;
openSource?: boolean;
acceptsCrypto?: boolean;
}
export interface Section {
name: string;
services: Service[];
intro?: string;
notableMentions?: ShortService[] | string;
furtherInfo?: string;
wordOfWarning?: string;
alternativeTo?: string[];
}
export interface Category {
name: string;
sections: Section[];
}
export interface AwesomePrivacy {
categories: Array<{
name: string;
sections: Array<Section>;
}>;
}

View File

@ -0,0 +1,58 @@
import type { Category } from '../types/Service';
export const prepareSearchItems = (categories: Category[]) => {
const items: any = [];
// Add each category
categories.forEach(category => {
items.push({
type: 'Category',
category: category.name,
itemCount: (category.sections || []).reduce((acc, section) => {
return acc + (section.services || []).length;
}, 0),
});
// Add section with category context
category.sections.forEach(section => {
items.push({
type: 'Section',
sectionName: section.name,
description: section.intro || '',
category: category.name,
itemCount: (section.services || []).length,
});
// Add service with section and category context
(section.services || []).forEach(service => {
items.push({
type: 'Service',
name: service.name,
description: service.description,
url: service.url,
github: service.github || '',
category: category.name,
sectionName: section.name,
logo: service.icon || '',
});
});
});
});
return items;
};
export const searchOptions = {
includeScore: true,
keys: [
{ name: 'name', weight: 0.9 },
{ name: 'sectionName', weight: 0.8 },
{ name: 'category', weight: 0.7 },
{ name: 'notableMentions', weight: 0.5 },
{ name: 'alternativeTo', weight: 0.5 },
{ name: 'github', weight: 0.4 },
{ name: 'url', weight: 0.2 },
{ name: 'description', weight: 0.1 },
{ name: 'intro', weight: 0.1 },
{ name: 'furtherInfo', weight: 0.1 },
{ name: 'wordOfWarning', weight: 0.1 },
],
};

View File

@ -0,0 +1,17 @@
import yaml from 'js-yaml';
import type { AwesomePrivacy } from '../types/Service';
const awesomePrivacyData = 'https://raw.githubusercontent.com/Lissy93/awesome-privacy/main/awesome-privacy.yml';
export const fetchData = async (): Promise<AwesomePrivacy> => {
return await fetch(awesomePrivacyData)
.then((res) => res.text())
.then((data) => yaml.load(data))
.catch((err) => console.error('ah crap', err)) as AwesomePrivacy;
}
export const slugify = (title: string) => {
return title.toLowerCase().replace(/\s/g, '-');z
};

View File

@ -0,0 +1,41 @@
import { marked } from 'marked';
export const parseMarkdown = (text: string | undefined): string => {
if (!text) return '';
// Custom renderer
const renderer = new marked.Renderer();
// Override function to handle headings
renderer.heading = (text, level) => {
const escapedText = text.toLowerCase().replace(/[^\w]+/g, '-');
return `<h${level} id="${escapedText}">${text}</h${level}>`;
};
// Override function to handle links
renderer.link = (href, title, text) => {
if (href.startsWith('/')) {
href = `https://github.com/Lissy93/personal-security-checklist/blob/old-version/${href}`;
}
title = title ? `title="${title}"` : '';
return `<a href="${href}" ${title} target="_blank" rel="noopener noreferrer">${text}</a>`;
};
// Sanitize the input to remove <script> tags
const sanitizeHtml = (html: string): string => {
return html.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
};
// Configure marked with the custom renderer
marked.use({ renderer });
// Parse the markdown, then sanitize the HTML to remove <script> tags
const rawHtml = marked.parse(text, { async: false}) as string;
const sanitizedHtml = sanitizeHtml(rawHtml);
return sanitizedHtml;
};
export const formatLink = (link: string) => {
return (link || '').replace(/^(https?:\/\/)?(www\.)?/, '').replace(/\/+$/, '');
};

5
web/svelte.config.js Normal file
View File

@ -0,0 +1,5 @@
import { vitePreprocess } from '@astrojs/svelte';
export default {
preprocess: vitePreprocess(),
};

11
web/tsconfig.json Normal file
View File

@ -0,0 +1,11 @@
{
"extends": "astro/tsconfigs/strict",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@components/*": ["src/components/*"],
"@layouts/*": ["src/layouts/*"],
"@utils/*": ["src/utils/*"]
}
}
}

4334
web/yarn.lock Normal file

File diff suppressed because it is too large Load Diff