From 282e3bc060068a9acefd87fad8c50b662470257d Mon Sep 17 00:00:00 2001 From: Alicia Sykes Date: Tue, 5 Mar 2024 23:15:33 +0000 Subject: [PATCH] Adds edit/remove capabilities, improves responsivness, makes service name hoverable, implements new screenshot service --- web/src/components/form/FontAwesome.svelte | 4 + .../things/GitHubDetailedInfo.astro | 35 ++++---- .../components/things/ItemGitHubMetrics.astro | 2 +- web/src/components/things/ServiceList.astro | 44 +++++++--- .../things/WebsiteDetailedInfo.astro | 6 +- web/src/pages/[...listing].astro | 86 ++++++++++++++++++- web/src/pages/about.astro | 66 +++++++++++++- web/src/styles/typography.css | 2 +- 8 files changed, 203 insertions(+), 42 deletions(-) diff --git a/web/src/components/form/FontAwesome.svelte b/web/src/components/form/FontAwesome.svelte index 3af7980..df7b230 100644 --- a/web/src/components/form/FontAwesome.svelte +++ b/web/src/components/form/FontAwesome.svelte @@ -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, diff --git a/web/src/components/things/GitHubDetailedInfo.astro b/web/src/components/things/GitHubDetailedInfo.astro index 9b39a4d..15cd97c 100644 --- a/web/src/components/things/GitHubDetailedInfo.astro +++ b/web/src/components/things/GitHubDetailedInfo.astro @@ -13,7 +13,6 @@ const { gitHubData, repo } = Astro.props; function generateChartData(languages: Record): string { - const colorMap: { [key: string]: string } = { 'TypeScript': '#2b7489', 'Objective-C++': '#6866fb', @@ -21,26 +20,26 @@ function generateChartData(languages: Record): 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 { '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

Author

- + {gitHubData.info.ownerUsername}

@@ -184,7 +185,9 @@ const languageChart = `https://quickchart.io/chart?c=${generateChartData(gitHubD {gitHubData.contributors.map((contributor) => (
  • - @{contributor.username} ({contributor.contributions}) + + @{contributor.username} + ({contributor.contributions})
  • ))} @@ -195,7 +198,9 @@ const languageChart = `https://quickchart.io/chart?c=${generateChartData(gitHubD {gitHubData.commits.map((commit) => (
  • - {commit.authorName} + + {commit.authorName} + ({formatDate(commit.authorDate)})

    {commit.message}

  • diff --git a/web/src/components/things/ItemGitHubMetrics.astro b/web/src/components/things/ItemGitHubMetrics.astro index 8763280..ff95855 100644 --- a/web/src/components/things/ItemGitHubMetrics.astro +++ b/web/src/components/things/ItemGitHubMetrics.astro @@ -90,7 +90,7 @@ const { Open Source --> - {github} + {github} { stats.statusCode === 200 && stats.data && ( diff --git a/web/src/components/things/ServiceList.astro b/web/src/components/things/ServiceList.astro index e91507e..d97ef59 100644 --- a/web/src/components/things/ServiceList.astro +++ b/web/src/components/things/ServiceList.astro @@ -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 {
      {services.map((service: Service) => (
    • -
      - -
      +
      } { service.github && noGitHubMetrics && ( - + {service.github} @@ -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; + } diff --git a/web/src/components/things/WebsiteDetailedInfo.astro b/web/src/components/things/WebsiteDetailedInfo.astro index e4d09cb..ec6fbb5 100644 --- a/web/src/components/things/WebsiteDetailedInfo.astro +++ b/web/src/components/things/WebsiteDetailedInfo.astro @@ -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}`; --- diff --git a/web/src/pages/[...listing].astro b/web/src/pages/[...listing].astro index 4a25234..8608bd2 100644 --- a/web/src/pages/[...listing].astro +++ b/web/src/pages/[...listing].astro @@ -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)}`; +} + --- @@ -171,7 +176,7 @@ const websiteData = (url && !ignoredSites.some(ignoredSite => url.includes(ignor )} { tosdrId && (
    • - ToS;DR: + Privacy: {`tosdr.org/en/service/${tosdrId}`}
    • )} @@ -247,6 +252,29 @@ const websiteData = (url && !ignoredSites.some(ignoredSite => url.includes(ignor sectionName={parentSection.name} /> +
      +

      About the Data: {name}

      + +

      API

      +

      + You can access {name}'s data programmatically via our API. + Simply make a GET request to: +

      +
      {getApiEndpoint()}
      +

      + The REST API is free, no-auth and CORS-enabled. + To learn more, view the Swagger Docs or read the API Usage Guide. +

      + +

      About the Data

      +

      + 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 About the Data section of our About page. +

      +
      { 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; diff --git a/web/src/pages/about.astro b/web/src/pages/about.astro index 60c4855..4723792 100644 --- a/web/src/pages/about.astro +++ b/web/src/pages/about.astro @@ -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; '


      +

      Our Data

      +

      +
      +

      Contributing

      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; diff --git a/web/src/styles/typography.css b/web/src/styles/typography.css index c662e83..e6d2b80 100644 --- a/web/src/styles/typography.css +++ b/web/src/styles/typography.css @@ -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; }