mirror of
https://github.com/Lissy93/awesome-privacy.git
synced 2026-03-11 08:55:33 +00:00
Adds edit/remove capabilities, improves responsivness, makes service name hoverable, implements new screenshot service
This commit is contained in:
parent
043dbc4f27
commit
282e3bc060
8 changed files with 203 additions and 42 deletions
|
|
@ -23,6 +23,10 @@
|
|||
faceMeh: solidIcons.faMeh,
|
||||
faceBad: solidIcons.faFrown,
|
||||
|
||||
// Service actions
|
||||
delete: solidIcons.faTrash,
|
||||
edit: solidIcons.faPenToSquare,
|
||||
|
||||
// Website Detailed Stats
|
||||
blacklistFound: solidIcons.faDoNotEnter,
|
||||
blacklistNotFound: solidIcons.faShieldCheck,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ const { gitHubData, repo } = Astro.props;
|
|||
|
||||
|
||||
function generateChartData(languages: Record<string, number>): string {
|
||||
|
||||
const colorMap: { [key: string]: string } = {
|
||||
'TypeScript': '#2b7489',
|
||||
'Objective-C++': '#6866fb',
|
||||
|
|
@ -21,26 +20,26 @@ function generateChartData(languages: Record<string, number>): string {
|
|||
'Dart': '#00B4AB',
|
||||
'Shell': '#89e051',
|
||||
'Elixir': '#6e4a7e',
|
||||
'Kotlin': '#F18E33',
|
||||
'Kotlin': '#7F52FF',
|
||||
'Objective-C': '#438eff',
|
||||
'Ruby': '#701516',
|
||||
'Go': '#375eab',
|
||||
'Ruby': '#CC342D',
|
||||
'Go': '#00ADD8',
|
||||
'PHP': '#4F5D95',
|
||||
'Java': '#b07219',
|
||||
'Scala': '#DC322F',
|
||||
'Makefile': '#427819',
|
||||
'Perl': '#0298c3',
|
||||
'Lua': '#000080',
|
||||
'Vue': '#2c3e50',
|
||||
'HTML': '#e44b23',
|
||||
'Swift': '#ffac45',
|
||||
'C': '#555555',
|
||||
'Perl': '#39457E',
|
||||
'Lua': '#2C2D72',
|
||||
'Vue': '#4FC08D',
|
||||
'HTML': '#E34F26',
|
||||
'Swift': '#F05138',
|
||||
'C': '#A8B9CC',
|
||||
'C#': '#097c01',
|
||||
'Clojure': '#db5855',
|
||||
'Rust': '#dea584',
|
||||
'Assembly': '#6E4C13',
|
||||
'Arduino': '#bd79d1',
|
||||
'C Sharp': '#178600',
|
||||
'C Sharp': '#512BD4',
|
||||
'CSS': '#563d7c',
|
||||
'TeX': '#3D6117',
|
||||
'OCaml': '#3be133',
|
||||
|
|
@ -55,7 +54,9 @@ function generateChartData(languages: Record<string, number>): string {
|
|||
'Common Lisp': '#3fb68b',
|
||||
'Groovy': '#e69f56',
|
||||
'Julia': '#a270ba',
|
||||
'cpp': '#f34b7d',
|
||||
'cpp': '#00599C',
|
||||
'C++': '#00599C',
|
||||
'Dockerfile': '#2496ED',
|
||||
};
|
||||
|
||||
// Arrays to hold the data, labels, and colors
|
||||
|
|
@ -131,7 +132,7 @@ const languageChart = `https://quickchart.io/chart?c=${generateChartData(gitHubD
|
|||
<h4>Author</h4>
|
||||
<p>
|
||||
<img src={gitHubData.info.ownerAvatar} width="20" height="20" />
|
||||
<a href={`https://github.com/${gitHubData.info.ownerUsername}`}>
|
||||
<a target="_blank" href={`https://github.com/${gitHubData.info.ownerUsername}`}>
|
||||
{gitHubData.info.ownerUsername}
|
||||
</a>
|
||||
</p>
|
||||
|
|
@ -184,7 +185,9 @@ const languageChart = `https://quickchart.io/chart?c=${generateChartData(gitHubD
|
|||
{gitHubData.contributors.map((contributor) => (
|
||||
<li>
|
||||
<img src={contributor.avatar} width="20" height="20" />
|
||||
<a href={`https://github.com/${contributor.username}`}>@{contributor.username}</a> ({contributor.contributions})
|
||||
<a href={`https://github.com/${contributor.username}`} target="_blank">
|
||||
@{contributor.username}
|
||||
</a> ({contributor.contributions})
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
|
@ -195,7 +198,9 @@ const languageChart = `https://quickchart.io/chart?c=${generateChartData(gitHubD
|
|||
{gitHubData.commits.map((commit) => (
|
||||
<li>
|
||||
<img src={commit.authorAvatar} width="20" height="20" />
|
||||
<a href={`https://github.com/${commit.authorUsername}`}>{commit.authorName}</a>
|
||||
<a href={`https://github.com/${commit.authorUsername}`} target="_blank">
|
||||
{commit.authorName}
|
||||
</a>
|
||||
<span class="date" title={timeAgo(commit.authorDate)}>({formatDate(commit.authorDate)})</span>
|
||||
<p class="commit-msg">{commit.message}</p>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ const {
|
|||
<FontAwesome iconName="openSource" /> Open Source
|
||||
</span> -->
|
||||
<span title={`GitHub repository: ${github}`}>
|
||||
<a href={`https://github.com/${github}`}><FontAwesome iconName="github" /> {github}</a>
|
||||
<a href={`https://github.com/${github}`} target="_blank"><FontAwesome iconName="github" /> {github}</a>
|
||||
</span>
|
||||
{ stats.statusCode === 200 && stats.data && (
|
||||
<span title={`GitHub Star Count: ${stargazers_count}`}>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ 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 DeleteListing from '@components/things/DeleteListing.svelte';
|
||||
import { slugify } from '@utils/fetch-data';
|
||||
|
||||
import GitHubMetrics from '@components/things/ItemGitHubMetrics.astro';
|
||||
|
|
@ -52,9 +53,7 @@ const {
|
|||
<ul>
|
||||
{services.map((service: Service) => (
|
||||
<li id={slugify(service.name)}>
|
||||
<div class="actions">
|
||||
|
||||
</div>
|
||||
<DeleteListing client:load categoryName={categoryName} sectionName={sectionName} serviceName={service.name} />
|
||||
<div class="service-head">
|
||||
<img
|
||||
width="40"
|
||||
|
|
@ -106,7 +105,7 @@ const {
|
|||
{ 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}`}>
|
||||
<a href={`https://github.com/${service.github}`} target="_blank">
|
||||
<FontAwesome iconName="github" /> {service.github}
|
||||
</a>
|
||||
</span>
|
||||
|
|
@ -155,6 +154,7 @@ const {
|
|||
margin: 0 0 3rem 0;
|
||||
li {
|
||||
margin-bottom: 1rem;
|
||||
position: relative;
|
||||
.service-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -167,6 +167,24 @@ const {
|
|||
margin: 0;
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
position: relative;
|
||||
&:after {
|
||||
background: none repeat scroll 0 0 transparent;
|
||||
bottom: 0;
|
||||
content: "";
|
||||
display: block;
|
||||
height: 3px;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
background: var(--accent-3);
|
||||
transition: width 0.3s ease 0s, left 0.3s ease 0s;
|
||||
width: 0;
|
||||
}
|
||||
&:hover:after {
|
||||
width: 100%;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.service-icon {
|
||||
|
|
@ -275,14 +293,16 @@ const {
|
|||
}
|
||||
}
|
||||
|
||||
section :global(.view-all) {
|
||||
width: fit-content;
|
||||
position: absolute;
|
||||
right: 1rem;
|
||||
margin-top: -2.5rem;
|
||||
background: var(--accent-3);
|
||||
}
|
||||
|
||||
|
||||
section :global(.view-all) {
|
||||
width: fit-content;
|
||||
position: absolute;
|
||||
right: 1rem;
|
||||
margin-top: -2.5rem;
|
||||
background: var(--accent-3);
|
||||
}
|
||||
li:hover :global(.actions) {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -39,11 +39,7 @@ else if (risk < 50) riskText = 'risky';
|
|||
else if (risk < 75) riskText = 'dangerous';
|
||||
else if (risk >= 75) riskText = 'very dangerous';
|
||||
|
||||
|
||||
const clientSideKey = 'cd02a8520c90410bbc32440950bd3b7e';
|
||||
const screenshotOptions = 'format=png&width=960&height=540&ttl=2419200&response_type=image&no_cookie_banners=true&no_ads=true';
|
||||
const screenshotEndpoint = 'https://api.apiflash.com/v1';
|
||||
const screenshotUrl = `${screenshotEndpoint}/urltoimage?access_key=${clientSideKey}&url=${encodeURIComponent(url)}&${screenshotOptions}`;
|
||||
const screenshotUrl = `https://screenshot-cache.as93.workers.dev/${url}`;
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import Comments from '@components/things/Comments.svelte';
|
|||
import GitHubDetailedInfo from '@components/things/GitHubDetailedInfo.astro';
|
||||
import PrivacyPolicyDetails from '@components/things/PrivacyPolicyDetails.astro';
|
||||
import WebsiteDetailedInfo from '@components/things/WebsiteDetailedInfo.astro';
|
||||
import DataActions from '@components/things/DataActions.svelte';
|
||||
import type { AwesomePrivacy, Section, Service } from '../types/Service';
|
||||
import { fetchData, slugify } from '@utils/fetch-data';
|
||||
import { fetchGitHubStats } from '@utils/fetch-repo-info';
|
||||
|
|
@ -123,6 +124,10 @@ const githubData = github ? await fetchGitHubStats(github) : null;
|
|||
const privacyData = tosdrId ? await fetchTosdrPrivacy(tosdrId) : null;
|
||||
const websiteData = (url && !ignoredSites.some(ignoredSite => url.includes(ignoredSite))) ? await fetchWebsiteInfo(url) : null;
|
||||
|
||||
const getApiEndpoint = () => {
|
||||
return `https://api.awesome-privacy.xyz/${slugify(categoryName)}/${slugify(parentSection.name)}/${slugify(name)}`;
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
<Layout title={makePageTitle()} keywords={makeKeyWordTag()} description={makeDescriptionTag()} >
|
||||
|
|
@ -171,7 +176,7 @@ const websiteData = (url && !ignoredSites.some(ignoredSite => url.includes(ignor
|
|||
)}
|
||||
{ tosdrId && (
|
||||
<li>
|
||||
<b>ToS;DR:</b>
|
||||
<b>Privacy:</b>
|
||||
<a href={`https://tosdr.org/en/service/${tosdrId}`}>{`tosdr.org/en/service/${tosdrId}`}</a>
|
||||
</li>
|
||||
)}
|
||||
|
|
@ -247,6 +252,29 @@ const websiteData = (url && !ignoredSites.some(ignoredSite => url.includes(ignor
|
|||
sectionName={parentSection.name}
|
||||
/>
|
||||
</section>
|
||||
<section>
|
||||
<h3>About the Data: {name}</h3>
|
||||
<DataActions client:load categoryName={categoryName} sectionName={parentSection.name} serviceName={name} />
|
||||
<h4>API</h4>
|
||||
<p>
|
||||
You can access {name}'s data programmatically via our API.
|
||||
Simply make a <code>GET</code> request to:
|
||||
</p>
|
||||
<pre><a href={getApiEndpoint()}><code>{getApiEndpoint()}</code></a></pre>
|
||||
<p>
|
||||
The REST API is free, no-auth and CORS-enabled.
|
||||
To learn more, view the <a href="/api/docs">Swagger Docs</a> or read the <a href="/about#our-api">API Usage Guide</a>.
|
||||
</p>
|
||||
|
||||
<h4>About the Data</h4>
|
||||
<p>
|
||||
Beyond the user-submitted YAML you see above, we also augment each listing with
|
||||
additional data dynamically fetched from several sources.
|
||||
To learn more about where the rest of data included in this page comes from,
|
||||
and how it is computed,
|
||||
see the <a href="/about#our-data">About the Data</a> section of our About page.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<div class="pagination-navigation">
|
||||
{ previous ? (
|
||||
|
|
@ -276,6 +304,11 @@ main {
|
|||
width: 1000px;
|
||||
max-width: calc(100% - 5rem);
|
||||
position: relative;
|
||||
@media(max-width: 768px) {
|
||||
max-width: 95%;
|
||||
padding: 0.5rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
section {
|
||||
|
|
@ -289,6 +322,10 @@ section {
|
|||
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0.25rem 0 0.5rem 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 2rem;
|
||||
margin: -2rem 0 1rem -2rem;
|
||||
|
|
@ -297,12 +334,18 @@ h3 {
|
|||
color: var(--accent-fg);
|
||||
width: fit-content;
|
||||
padding: 0.25rem 0.5rem;
|
||||
@media(max-width: 768px) {
|
||||
margin: -2rem auto 2rem auto;
|
||||
}
|
||||
}
|
||||
|
||||
.service-head {
|
||||
h2 {
|
||||
font-size: 3rem;
|
||||
margin: 0;
|
||||
@media(max-width: 768px) {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
.url-wrap {
|
||||
display: flex;
|
||||
|
|
@ -315,6 +358,9 @@ h3 {
|
|||
font-style: italic;
|
||||
opacity: 0.6;
|
||||
}
|
||||
@media(max-width: 768px) {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -333,6 +379,14 @@ h3 {
|
|||
color: var(--accent);
|
||||
}
|
||||
}
|
||||
@media(max-width: 768px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.4rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.intro {
|
||||
|
|
@ -341,12 +395,15 @@ h3 {
|
|||
opacity: 0.9;
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
@media(max-width: 768px) {
|
||||
flex-direction: column;
|
||||
}
|
||||
.service-icon {
|
||||
border-radius: var(--curve-md);
|
||||
margin-top: 1rem;
|
||||
}
|
||||
h4 {
|
||||
margin-bottom: 0;
|
||||
@media(max-width: 768px) {
|
||||
margin: 1rem auto 0 auto;
|
||||
}
|
||||
}
|
||||
ul {
|
||||
padding-left: 1rem;
|
||||
|
|
@ -359,6 +416,10 @@ h3 {
|
|||
font-weight: 400;
|
||||
min-width: 85px;
|
||||
display: inline-block;
|
||||
@media(max-width: 768px) {
|
||||
width: auto;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -439,6 +500,23 @@ h3 {
|
|||
}
|
||||
}
|
||||
|
||||
pre {
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: var(--foreground);
|
||||
code {
|
||||
background: #acabb782;
|
||||
padding: 0.2rem 0.4rem;
|
||||
border-radius: var(--curve-sm);
|
||||
font-family: Courier New;
|
||||
font-size: 0.9rem;
|
||||
@media(max-width: 768px) {
|
||||
white-space: break-spaces;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.highlights {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,41 @@ const licenseContent = async () => {
|
|||
return await response.text();
|
||||
};
|
||||
|
||||
const aboutOurData = `
|
||||
All data is stored in
|
||||
[\`awesome-privacy.yml\`](https://github.com/lissy93/awesome-privacy/blob/main/awesome-privacy.yml).
|
||||
|
||||
This file is then pulled into the website at build-time, and also used to generate
|
||||
the repositories README file.
|
||||
We use this method in the interest of transparency and data integrity,
|
||||
as Git makes it possible to maintain a full log of what was changed, when, by who and why.
|
||||
|
||||
### Augmention
|
||||
The data is augmented with some extra info, to add additional context to each service.
|
||||
The aim of this is to give you a broader picture of each service, to help you make a more informed decision.
|
||||
Currently, this extra data is pulled from:
|
||||
|
||||
- **GitHub API** - To fetch info about each project's repository
|
||||
- Including author & contributors, creation & last updated date, languages, license and star count
|
||||
- **Web Check** - To fetch info about each project's website
|
||||
- Including SSL chain, WHOIS, server location, blacklist check and a screenshot
|
||||
- **ToS;DR** - To fetch a summerized version of each services Privacy Policy
|
||||
- Including policy and terms of service links, postives + negatives and privacy score
|
||||
- **User Reviews** - User-submitted comments + feedback on a given service
|
||||
- Implemented using self-hosted instance of Remark42
|
||||
- **Portainer Templates** - To fetch info about each project's Docker image (if it's self-hosted)
|
||||
- Including supported architectures, run command, pull count, last updated and config options
|
||||
|
||||
### API
|
||||
We also have a free, no-auth, CORS-enabled RESR API,
|
||||
which you can use to access Awesome Privacy's data programmatically,
|
||||
or to build your own apps on top of it.
|
||||
|
||||
The [Swagger Spec](#) outlines all endpoints, usage and examples.
|
||||
|
||||
The host is: \`https://api.awesome-privacy.xyz\`
|
||||
`;
|
||||
|
||||
const projectRequrements = `
|
||||
For software to be included in this list, it must meet the following requirements:
|
||||
|
||||
|
|
@ -43,13 +78,16 @@ For software to be included in this list, it must meet the following requirement
|
|||
- 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
|
||||
- Ideally hosted services should have been security audited, with the report published publicly
|
||||
- **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
|
||||
- Ideally the source should have been updated within the last 12 months
|
||||
- **Transparent**
|
||||
- It should be clear who is behind the project, what their motives are, and what (if any) the funding model is
|
||||
- For hosted solutions, the privacy policy should clearly state what data is collected, how it's used and how long it's stored
|
||||
- **Ethical**
|
||||
- Must not suppress free speech, discriminate or disregard any human rights
|
||||
- **Relevant**
|
||||
|
|
@ -176,6 +214,10 @@ const description = 'Privacy is a fundamental human right; '
|
|||
<div class="software-requirements"><p set:html={parseMarkdown(projectRequrements)}></p></div>
|
||||
<hr />
|
||||
|
||||
<h2 id="our-data">Our Data</h2>
|
||||
<div class="about-the-data"><p set:html={parseMarkdown(aboutOurData)}></p></div>
|
||||
<hr />
|
||||
|
||||
<h2 id="contributing">Contributing</h2>
|
||||
<p>
|
||||
Awesome Privacy (including all data and code) is fully open source,
|
||||
|
|
@ -305,7 +347,7 @@ h2 {
|
|||
margin: 2rem 0 1rem 0;
|
||||
}
|
||||
p {
|
||||
font-size: 1.4rem;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
hr {
|
||||
margin: 2rem 0;
|
||||
|
|
@ -323,14 +365,15 @@ h3 {
|
|||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.software-requirements {
|
||||
.software-requirements, .about-the-data {
|
||||
:global(p) {
|
||||
font-size: 1.4rem;
|
||||
font-size: 1.2rem;
|
||||
margin: 0 0 0.5rem 0;
|
||||
}
|
||||
:global(ul) {
|
||||
padding-left: 0.5rem;
|
||||
list-style: none;
|
||||
font-size: 1.1rem;
|
||||
font-size: 1.2rem;
|
||||
:global(li ul) {
|
||||
padding: 0 0 0.5rem 1rem;
|
||||
list-style: circle;
|
||||
|
|
@ -341,6 +384,20 @@ h3 {
|
|||
transition: color 0.2s ease-in-out;
|
||||
color: var(--accent-3);
|
||||
}
|
||||
:global(h3) {
|
||||
font-size: 1.6rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
:global(strong) {
|
||||
font-weight: 500;
|
||||
}
|
||||
:global(code) {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
background: #acabb782;
|
||||
padding: 0.2rem 0.4rem;
|
||||
border-radius: var(--curve-sm);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
}
|
||||
|
||||
.user-list {
|
||||
|
|
@ -401,6 +458,7 @@ h3 {
|
|||
max-width: 550px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 1rem;
|
||||
margin: 1rem 0;
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ code {
|
|||
font-style: normal;
|
||||
}
|
||||
|
||||
html, body, p, a, ul, ol, li, blockquote, pre, code, strong, i {
|
||||
html, body, p, a, ul, ol, li, blockquote, pre, strong, i {
|
||||
font-family: "Rubik", sans-serif;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue