├── Dockerfile ├── lib ├── api.py ├── api-spec.yml ├── validate.py ├── requirements.txt ├── schema.json └── generate.py ├── web ├── public │ ├── robots.txt │ ├── personal-security-checklist.yml │ ├── banner.png │ ├── favicon.png │ ├── fonts │ │ ├── poppins-400.woff2 │ │ ├── poppins-500.woff2 │ │ └── poppins-700.woff2 │ └── manifest.json ├── src │ ├── types │ │ ├── progressbar.d.ts │ │ ├── vite-plugin-copy.d.ts │ │ └── PSC.ts │ ├── styles │ │ ├── tailwind.css │ │ └── global.css │ ├── media │ │ └── thunder.png │ ├── routes │ │ ├── article │ │ │ ├── [slug] │ │ │ │ ├── article.module.css │ │ │ │ └── index.tsx │ │ │ └── index.tsx │ │ ├── _404.tsx │ │ ├── service-worker.ts │ │ ├── index.tsx │ │ ├── layout.tsx │ │ ├── checklist │ │ │ ├── [title] │ │ │ │ └── index.tsx │ │ │ └── index.tsx │ │ └── about │ │ │ ├── about-content.ts │ │ │ └── index.tsx │ ├── components │ │ ├── furniture │ │ │ ├── header.tsx │ │ │ ├── footer.tsx │ │ │ ├── hero.tsx │ │ │ └── nav.tsx │ │ ├── psc │ │ │ ├── psc.module.css │ │ │ ├── section-link-grid.tsx │ │ │ ├── checklist-table.tsx │ │ │ └── progress.tsx │ │ ├── router-head │ │ │ └── router-head.tsx │ │ ├── starter │ │ │ └── icons │ │ │ │ └── qwik.tsx │ │ └── core │ │ │ └── icon.tsx │ ├── store │ │ ├── checklist-context.ts │ │ ├── theme-store.ts │ │ └── local-checklist-store.ts │ ├── entry.dev.tsx │ ├── entry.preview.tsx │ ├── entry.vercel-edge.tsx │ ├── entry.ssr.tsx │ ├── hooks │ │ └── useLocalStorage.ts │ ├── root.tsx │ └── data │ │ └── articles.ts ├── postcss.config.js ├── vercel.json ├── README.txt ├── adapters │ ├── static │ │ └── vite.config.mts │ └── vercel-edge │ │ └── vite.config.mts ├── .prettierignore ├── .eslintignore ├── .gitignore ├── tsconfig.json ├── .vscode │ ├── launch.json │ ├── qwik-city.code-snippets │ └── qwik.code-snippets ├── vite.config.mts ├── .eslintrc.cjs ├── package.json └── tailwind.config.js ├── articles ├── 5_Privacy_Respecting_Software.md ├── Secure-Messaging.md ├── 0_Why_It_Matters.md └── 2_TLDR_Short_List.md ├── .github ├── workflows │ ├── sync-mirror.yml │ ├── maintain-gh-pages.yml │ └── insert-checklist.yml ├── PULL_REQUEST_TEMPLATE.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── addition.yml │ ├── removal.yml │ └── amendment.yml ├── CODE_OF_CONDUCT.md └── README.md └── LICENSE /Dockerfile: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/api.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/api-spec.yml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/validate.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/public/robots.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/requirements.txt: -------------------------------------------------------------------------------- 1 | PyYAML==6.0.1 2 | requests==2.31.0 3 | -------------------------------------------------------------------------------- /web/src/types/progressbar.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'progressbar.js'; 2 | -------------------------------------------------------------------------------- /web/src/types/vite-plugin-copy.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'vite-plugin-copy'; 2 | -------------------------------------------------------------------------------- /web/public/personal-security-checklist.yml: -------------------------------------------------------------------------------- 1 | ../../personal-security-checklist.yml -------------------------------------------------------------------------------- /web/src/styles/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | -------------------------------------------------------------------------------- /web/public/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lissy93/personal-security-checklist/HEAD/web/public/banner.png -------------------------------------------------------------------------------- /web/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lissy93/personal-security-checklist/HEAD/web/public/favicon.png -------------------------------------------------------------------------------- /web/src/media/thunder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lissy93/personal-security-checklist/HEAD/web/src/media/thunder.png -------------------------------------------------------------------------------- /web/public/fonts/poppins-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lissy93/personal-security-checklist/HEAD/web/public/fonts/poppins-400.woff2 -------------------------------------------------------------------------------- /web/public/fonts/poppins-500.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lissy93/personal-security-checklist/HEAD/web/public/fonts/poppins-500.woff2 -------------------------------------------------------------------------------- /web/public/fonts/poppins-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lissy93/personal-security-checklist/HEAD/web/public/fonts/poppins-700.woff2 -------------------------------------------------------------------------------- /articles/5_Privacy_Respecting_Software.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | | ➡️ This list page has now been moved to [awesome-privacy](https://github.com/Lissy93/awesome-privacy) | 4 | | --- | 5 | -------------------------------------------------------------------------------- /web/src/routes/article/[slug]/article.module.css: -------------------------------------------------------------------------------- 1 | .psc_article { 2 | img { 3 | display: inline; 4 | margin: 0 auto; 5 | border-radius: 4px; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /web/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | 'postcss-import': {}, 4 | 'tailwindcss/nesting': {}, 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /web/src/components/furniture/header.tsx: -------------------------------------------------------------------------------- 1 | import { component$ } from "@builder.io/qwik"; 2 | 3 | export default component$(() => { 4 | return ( 5 |
6 |
7 | ); 8 | }); 9 | -------------------------------------------------------------------------------- /web/src/styles/global.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Catamaran:wght@900&family=Poppins&display=swap'); 2 | 3 | h1, h2, h3, h4, h5, h6 { 4 | font-family: 'Catamaran', sans-serif; 5 | } 6 | 7 | p, a, li, span { 8 | font-family: 'Poppins', sans-serif; 9 | } 10 | -------------------------------------------------------------------------------- /web/src/components/psc/psc.module.css: -------------------------------------------------------------------------------- 1 | 2 | .container { 3 | /* I couldn't figure out how to do this with Tailwind.... */ 4 | grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); 5 | } 6 | 7 | .checklistItemDescription { 8 | a { 9 | text-decoration: underline; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /web/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "headers": [ 3 | { 4 | "source": "/build/(.*)", 5 | "headers": [ 6 | { 7 | "key": "Cache-Control", 8 | "value": "public, max-age=31536000, s-maxage=31536000, immutable" 9 | } 10 | ] 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /web/src/store/checklist-context.ts: -------------------------------------------------------------------------------- 1 | import { type Signal } from '@builder.io/qwik'; 2 | import { createContextId } from '@builder.io/qwik'; 3 | 4 | import type { Sections } from '../types/PSC'; 5 | 6 | export const ChecklistContext = createContextId>( 7 | 'psc.ChecklistContext' 8 | ); 9 | -------------------------------------------------------------------------------- /web/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/web-manifest-combined.json", 3 | "name": "digital-defense", 4 | "short_name": "Digital Defense", 5 | "start_url": ".", 6 | "display": "standalone", 7 | "background_color": "#5616c6", 8 | "description": "The ultimate personal security checklist" 9 | } 10 | -------------------------------------------------------------------------------- /web/src/routes/_404.tsx: -------------------------------------------------------------------------------- 1 | // src/routes/_404.tsx 2 | import { component$ } from '@builder.io/qwik'; 3 | 4 | export default component$(() => { 5 | return ( 6 |
7 |

404 Not Found

8 |

The page you're looking for doesn't exist.

9 | Go back to the homepage 10 |
11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /web/README.txt: -------------------------------------------------------------------------------- 1 | 2 | This is the source for the https://digital-defense.io website, which displays the checklist data interactively 3 | 4 | For build, development and deploy instructions, see the main README 5 | 6 | If you wish to make content changes, the only file you need to edit is `personal-security-checklist.yml`, in the repo's root 7 | 8 | All code here is licensed under MIT 9 | 10 | 11 | -------------------------------------------------------------------------------- /web/adapters/static/vite.config.mts: -------------------------------------------------------------------------------- 1 | import { staticAdapter } from "@builder.io/qwik-city/adapters/static/vite"; 2 | import { extendConfig } from "@builder.io/qwik-city/vite"; 3 | import baseConfig from "../../vite.config.mts"; 4 | 5 | export default extendConfig(baseConfig, () => { 6 | return { 7 | build: { 8 | ssr: true, 9 | rollupOptions: { 10 | input: ["@qwik-city-plan"], 11 | }, 12 | }, 13 | plugins: [ 14 | staticAdapter({ 15 | origin: "https://yoursite.qwik.dev", 16 | }), 17 | ], 18 | }; 19 | }); 20 | -------------------------------------------------------------------------------- /web/.prettierignore: -------------------------------------------------------------------------------- 1 | **/*.log 2 | **/.DS_Store 3 | *. 4 | .vscode/settings.json 5 | .history 6 | .yarn 7 | bazel-* 8 | bazel-bin 9 | bazel-out 10 | bazel-qwik 11 | bazel-testlogs 12 | dist 13 | dist-dev 14 | lib 15 | lib-types 16 | etc 17 | external 18 | node_modules 19 | temp 20 | tsc-out 21 | tsdoc-metadata.json 22 | target 23 | output 24 | rollup.config.js 25 | build 26 | .cache 27 | .vscode 28 | .rollup.cache 29 | tsconfig.tsbuildinfo 30 | vite.config.ts 31 | *.spec.tsx 32 | *.spec.ts 33 | .netlify 34 | pnpm-lock.yaml 35 | package-lock.json 36 | yarn.lock 37 | server 38 | -------------------------------------------------------------------------------- /.github/workflows/sync-mirror.yml: -------------------------------------------------------------------------------- 1 | # Pushes the contents of the repo to the Codeberg mirror 2 | name: 🪞 Mirror to Codeberg 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: '30 0 * * 0' 7 | jobs: 8 | codeberg: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | with: { fetch-depth: 0 } 13 | - uses: pixta-dev/repository-mirroring-action@v1 14 | with: 15 | target_repo_url: git@codeberg.org:alicia/personal-security-checklist.git 16 | ssh_private_key: ${{ secrets.CODEBERG_SSH }} 17 | 18 | -------------------------------------------------------------------------------- /web/.eslintignore: -------------------------------------------------------------------------------- 1 | **/*.log 2 | **/.DS_Store 3 | *. 4 | .vscode/settings.json 5 | .history 6 | .yarn 7 | bazel-* 8 | bazel-bin 9 | bazel-out 10 | bazel-qwik 11 | bazel-testlogs 12 | dist 13 | dist-dev 14 | lib 15 | lib-types 16 | etc 17 | external 18 | node_modules 19 | temp 20 | tsc-out 21 | tsdoc-metadata.json 22 | target 23 | output 24 | rollup.config.js 25 | build 26 | .cache 27 | .vscode 28 | .rollup.cache 29 | dist 30 | tsconfig.tsbuildinfo 31 | vite.config.ts 32 | *.spec.tsx 33 | *.spec.ts 34 | .netlify 35 | pnpm-lock.yaml 36 | package-lock.json 37 | yarn.lock 38 | server 39 | -------------------------------------------------------------------------------- /web/adapters/vercel-edge/vite.config.mts: -------------------------------------------------------------------------------- 1 | import { vercelEdgeAdapter } from "@builder.io/qwik-city/adapters/vercel-edge/vite"; 2 | import { extendConfig } from "@builder.io/qwik-city/vite"; 3 | import baseConfig from "../../vite.config.mts"; 4 | 5 | export default extendConfig(baseConfig, () => { 6 | return { 7 | build: { 8 | ssr: true, 9 | rollupOptions: { 10 | input: ["src/entry.vercel-edge.tsx", "@qwik-city-plan"], 11 | }, 12 | outDir: ".vercel/output/functions/_qwik-city.func", 13 | }, 14 | plugins: [vercelEdgeAdapter()], 15 | }; 16 | }); 17 | -------------------------------------------------------------------------------- /web/.gitignore: -------------------------------------------------------------------------------- 1 | # Build 2 | /dist 3 | /lib 4 | /lib-types 5 | /server 6 | 7 | # Development 8 | node_modules 9 | *.local 10 | 11 | # Cache 12 | .cache 13 | .mf 14 | .rollup.cache 15 | tsconfig.tsbuildinfo 16 | 17 | # Logs 18 | logs 19 | *.log 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | pnpm-debug.log* 24 | lerna-debug.log* 25 | 26 | # Editor 27 | .vscode/* 28 | !.vscode/launch.json 29 | !.vscode/*.code-snippets 30 | 31 | .idea 32 | .DS_Store 33 | *.suo 34 | *.ntvs* 35 | *.njsproj 36 | *.sln 37 | *.sw? 38 | 39 | # Yarn 40 | .yarn/* 41 | !.yarn/releases 42 | 43 | # Vercel 44 | .vercel 45 | -------------------------------------------------------------------------------- /web/src/entry.dev.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * WHAT IS THIS FILE? 3 | * 4 | * Development entry point using only client-side modules: 5 | * - Do not use this mode in production! 6 | * - No SSR 7 | * - No portion of the application is pre-rendered on the server. 8 | * - All of the application is running eagerly in the browser. 9 | * - More code is transferred to the browser than in SSR mode. 10 | * - Optimizer/Serialization/Deserialization code is not exercised! 11 | */ 12 | import { render, type RenderOptions } from "@builder.io/qwik"; 13 | import Root from "./root"; 14 | 15 | export default function (opts: RenderOptions) { 16 | return render(document, , opts); 17 | } 18 | -------------------------------------------------------------------------------- /web/src/entry.preview.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * WHAT IS THIS FILE? 3 | * 4 | * It's the bundle entry point for `npm run preview`. 5 | * That is, serving your app built in production mode. 6 | * 7 | * Feel free to modify this file, but don't remove it! 8 | * 9 | * Learn more about Vite's preview command: 10 | * - https://vitejs.dev/config/preview-options.html#preview-options 11 | * 12 | */ 13 | import { createQwikCity } from "@builder.io/qwik-city/middleware/node"; 14 | import qwikCityPlan from "@qwik-city-plan"; 15 | import render from "./entry.ssr"; 16 | 17 | /** 18 | * The default export is the QwikCity adapter used by Vite preview. 19 | */ 20 | export default createQwikCity({ render, qwikCityPlan }); 21 | -------------------------------------------------------------------------------- /web/src/entry.vercel-edge.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * WHAT IS THIS FILE? 3 | * 4 | * It's the entry point for Vercel Edge when building for production. 5 | * 6 | * Learn more about the Vercel Edge integration here: 7 | * - https://qwik.builder.io/docs/deployments/vercel-edge/ 8 | * 9 | */ 10 | import { 11 | createQwikCity, 12 | type PlatformVercel, 13 | } from "@builder.io/qwik-city/middleware/vercel-edge"; 14 | import qwikCityPlan from "@qwik-city-plan"; 15 | import { manifest } from "@qwik-client-manifest"; 16 | import render from "./entry.ssr"; 17 | 18 | declare global { 19 | interface QwikCityPlatform extends PlatformVercel {} 20 | } 21 | 22 | export default createQwikCity({ render, qwikCityPlan, manifest }); 23 | -------------------------------------------------------------------------------- /web/src/routes/service-worker.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * WHAT IS THIS FILE? 3 | * 4 | * The service-worker.ts file is used to have state of the art prefetching. 5 | * https://qwik.builder.io/qwikcity/prefetching/overview/ 6 | * 7 | * Qwik uses a service worker to speed up your site and reduce latency, ie, not used in the traditional way of offline. 8 | * You can also use this file to add more functionality that runs in the service worker. 9 | */ 10 | import { setupServiceWorker } from "@builder.io/qwik-city/service-worker"; 11 | 12 | setupServiceWorker(); 13 | 14 | addEventListener("install", () => self.skipWaiting()); 15 | 16 | addEventListener("activate", () => self.clients.claim()); 17 | 18 | declare const self: ServiceWorkerGlobalScope; 19 | -------------------------------------------------------------------------------- /web/src/types/PSC.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface PersonalSecurityChecklist { 3 | sections: Section[], 4 | } 5 | 6 | export type Sections = Section[]; 7 | 8 | export interface Section { 9 | title: string, 10 | slug: string, 11 | description: string, 12 | intro: string, 13 | icon: string, 14 | color: string, 15 | checklist: Checklist[], 16 | softwareLinks?: Link[], 17 | helpfulTools?: Link[], 18 | furtherResources?: Link[], 19 | } 20 | 21 | export type Priority = 'essential' | 'optional' | 'advanced'; 22 | 23 | export interface Checklist { 24 | point: string, 25 | priority: Priority, 26 | details: string, 27 | } 28 | 29 | export interface Link { 30 | title: string, 31 | url: string, 32 | description?: string, 33 | } 34 | -------------------------------------------------------------------------------- /web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "target": "ES2017", 5 | "module": "ES2022", 6 | "lib": ["es2022", "DOM", "WebWorker", "DOM.Iterable"], 7 | "jsx": "react-jsx", 8 | "jsxImportSource": "@builder.io/qwik", 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "resolveJsonModule": true, 12 | "moduleResolution": "node", 13 | "esModuleInterop": true, 14 | "skipLibCheck": true, 15 | "incremental": true, 16 | "isolatedModules": true, 17 | "outDir": "tmp", 18 | "noEmit": true, 19 | "types": ["node", "vite/client"], 20 | "paths": { 21 | "~/*": ["./src/*"], 22 | }, 23 | }, 24 | "files": ["./.eslintrc.cjs"], 25 | "include": ["src", "./*.d.ts", "./*.config.ts"], 26 | } 27 | -------------------------------------------------------------------------------- /web/src/components/furniture/footer.tsx: -------------------------------------------------------------------------------- 1 | import { component$ } from "@builder.io/qwik"; 2 | 3 | export default component$(() => { 4 | 5 | const ghLink = 'https://github.com/Lissy93/personal-security-checklist/'; 6 | const licenseLink = 'https://github.com/Lissy93/personal-security-checklist/blob/master/LICENSE'; 7 | const authorLink = 'https://aliciasykes.com'; 8 | 9 | return ( 10 | 17 | ); 18 | }); 19 | -------------------------------------------------------------------------------- /web/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch Chrome", 9 | "request": "launch", 10 | "type": "chrome", 11 | "url": "http://localhost:5173", 12 | "webRoot": "${workspaceFolder}" 13 | }, 14 | { 15 | "type": "node", 16 | "name": "dev.debug", 17 | "request": "launch", 18 | "skipFiles": ["/**"], 19 | "cwd": "${workspaceFolder}", 20 | "program": "${workspaceFolder}/node_modules/vite/bin/vite.js", 21 | "args": ["--mode", "ssr", "--force"] 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /web/src/entry.ssr.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * WHAT IS THIS FILE? 3 | * 4 | * SSR entry point, in all cases the application is rendered outside the browser, this 5 | * entry point will be the common one. 6 | * 7 | * - Server (express, cloudflare...) 8 | * - npm run start 9 | * - npm run preview 10 | * - npm run build 11 | * 12 | */ 13 | import { 14 | renderToStream, 15 | type RenderToStreamOptions, 16 | } from "@builder.io/qwik/server"; 17 | import { manifest } from "@qwik-client-manifest"; 18 | import Root from "./root"; 19 | 20 | export default function (opts: RenderToStreamOptions) { 21 | return renderToStream(, { 22 | manifest, 23 | ...opts, 24 | // Use container attributes to set attributes on the html tag. 25 | containerAttributes: { 26 | lang: "en-us", 27 | ...opts.containerAttributes, 28 | }, 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /web/src/store/theme-store.ts: -------------------------------------------------------------------------------- 1 | import { useStore, useOnWindow, $ } from '@builder.io/qwik'; 2 | 3 | import { useLocalStorage } from '~/hooks/useLocalStorage'; 4 | 5 | const STORAGE_KEY = 'PSC_THEME'; 6 | const defaultTheme = 'dark'; 7 | 8 | export const useTheme = () => { 9 | const [theme, saveTheme] = useLocalStorage(STORAGE_KEY, defaultTheme); 10 | const state = useStore({ theme: theme.value }); 11 | 12 | useOnWindow('load', $(() => { 13 | const storedTheme = theme.value || defaultTheme; 14 | state.theme = storedTheme; 15 | document.getElementsByTagName('body')[0].setAttribute('data-theme', storedTheme); 16 | })); 17 | 18 | const setTheme = $((newTheme: string) => { 19 | saveTheme(newTheme); 20 | state.theme = newTheme; 21 | document.getElementsByTagName('body')[0].setAttribute('data-theme', newTheme); 22 | }); 23 | 24 | return { theme: state, setTheme }; 25 | }; 26 | 27 | -------------------------------------------------------------------------------- /web/src/store/local-checklist-store.ts: -------------------------------------------------------------------------------- 1 | 2 | import { $, useStore, useOnWindow } from '@builder.io/qwik'; 3 | import jsyaml from 'js-yaml'; 4 | import type { Sections } from '~/types/PSC'; 5 | 6 | export const useChecklist = () => { 7 | const state = useStore<{ checklist: Sections | null }>({ checklist: null }); 8 | 9 | const fetchChecklist = $(async () => { 10 | const localUrl = '/personal-security-checklist.yml'; 11 | return fetch(localUrl) 12 | .then((res) => res.text()) 13 | .then((yamlText) => { 14 | return jsyaml.load(yamlText); 15 | }); 16 | }); 17 | 18 | useOnWindow('load', $(() => { 19 | fetchChecklist().then((checklist) => { 20 | state.checklist = checklist as Sections; 21 | }); 22 | })); 23 | 24 | const setChecklist = $((newChecklist: Sections) => { 25 | state.checklist = newChecklist; 26 | }); 27 | 28 | return { checklist: state, setChecklist }; 29 | }; 30 | -------------------------------------------------------------------------------- /web/vite.config.mts: -------------------------------------------------------------------------------- 1 | import { defineConfig, type UserConfig } from "vite"; 2 | import { qwikVite } from "@builder.io/qwik/optimizer"; 3 | import { qwikCity } from "@builder.io/qwik-city/vite"; 4 | import tsconfigPaths from "vite-tsconfig-paths"; 5 | import { viteStaticCopy } from 'vite-plugin-static-copy' 6 | 7 | export default defineConfig((): UserConfig => { 8 | return { 9 | plugins: [ 10 | qwikCity(), 11 | qwikVite(), 12 | tsconfigPaths(), 13 | viteStaticCopy({ 14 | targets: [ 15 | { 16 | src: '../personal-security-checklist.yml', 17 | dest: 'public' 18 | } 19 | ] 20 | }) 21 | ], 22 | server: { 23 | headers: { 24 | "Cache-Control": "public, max-age=0", 25 | }, 26 | }, 27 | preview: { 28 | headers: { 29 | "Cache-Control": "public, max-age=600", 30 | }, 31 | }, 32 | }; 33 | }); 34 | -------------------------------------------------------------------------------- /web/src/routes/article/index.tsx: -------------------------------------------------------------------------------- 1 | // src/routes/articles/index.tsx 2 | import { component$ } from '@builder.io/qwik'; 3 | import articles from '~/data/articles'; 4 | 5 | export default component$(() => { 6 | 7 | return ( 8 |
9 |
10 | 11 |

Articles

12 | 25 | 26 |
27 |
28 | ); 29 | }); 30 | -------------------------------------------------------------------------------- /web/src/hooks/useLocalStorage.ts: -------------------------------------------------------------------------------- 1 | import { $, type QRL, useOnWindow, useStore } from "@builder.io/qwik"; 2 | 3 | export function useLocalStorage(key: string, initialState: any): [any, QRL<(value: any) => void>] { 4 | const store = useStore({ value: initialState }); 5 | 6 | useOnWindow('load', $(() => { 7 | try { 8 | const item = window.localStorage.getItem(key); 9 | if (!item) { 10 | window.localStorage.setItem(key, JSON.stringify(initialState)); 11 | } 12 | store.value = item ? JSON.parse(item) : initialState; 13 | } catch (error) { 14 | console.log(error); 15 | store.value = initialState; 16 | } 17 | })); 18 | 19 | const setValue$ = $((value: any) => { 20 | try { 21 | store.value = value; 22 | if (typeof window !== "undefined") { 23 | window.localStorage.setItem(key, JSON.stringify(value)); 24 | } 25 | } catch (error) { 26 | console.log(error); 27 | } 28 | }); 29 | 30 | return [store, setValue$]; 31 | } 32 | -------------------------------------------------------------------------------- /web/src/components/furniture/hero.tsx: -------------------------------------------------------------------------------- 1 | import { component$ } from "@builder.io/qwik"; 2 | 3 | import Icon from "~/components/core/icon"; 4 | 5 | export default component$(() => { 6 | return ( 7 |
8 |
9 |
10 |

The Ultimate

11 |

Personal Security Checklist

12 |

Your guide to securing your digital life and protecting your privacy

13 | 14 | 15 | 19 | 20 |
21 |
22 |
23 | ); 24 | }); 25 | -------------------------------------------------------------------------------- /web/src/routes/index.tsx: -------------------------------------------------------------------------------- 1 | import { component$, useContext } from '@builder.io/qwik'; 2 | import { type DocumentHead } from "@builder.io/qwik-city"; 3 | 4 | import Hero from "~/components/furniture/hero"; 5 | import SectionLinkGrid from "~/components/psc/section-link-grid"; 6 | import Progress from "~/components/psc/progress"; 7 | 8 | import { ChecklistContext } from '~/store/checklist-context'; 9 | 10 | import { useChecklist } from '~/store/local-checklist-store'; 11 | 12 | export default component$(() => { 13 | const checklists = useContext(ChecklistContext); 14 | 15 | const localChecklist = useChecklist(); 16 | 17 | return ( 18 | <> 19 | 20 | 21 | 22 | 23 | ); 24 | }); 25 | 26 | export const head: DocumentHead = { 27 | title: "Digital Defense", 28 | meta: [ 29 | { 30 | name: "description", 31 | content: "The ultimate personal security checklist, for securing your digital life.", 32 | }, 33 | ], 34 | }; 35 | -------------------------------------------------------------------------------- /web/src/root.tsx: -------------------------------------------------------------------------------- 1 | import { component$, useStyles$ } from "@builder.io/qwik"; 2 | import { 3 | QwikCityProvider, 4 | RouterOutlet, 5 | ServiceWorkerRegister, 6 | } from "@builder.io/qwik-city"; 7 | import { RouterHead } from "./components/router-head/router-head"; 8 | 9 | import tailwind from './styles/tailwind.css?inline'; 10 | 11 | import "./styles/global.css"; 12 | 13 | export default component$(() => { 14 | 15 | useStyles$(tailwind); 16 | /** 17 | * The root of a QwikCity site always start with the component, 18 | * immediately followed by the document's and . 19 | * 20 | * Don't remove the `` and `` elements. 21 | */ 22 | 23 | return ( 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | ); 36 | }); 37 | -------------------------------------------------------------------------------- /web/.vscode/qwik-city.code-snippets: -------------------------------------------------------------------------------- 1 | { 2 | "onRequest": { 3 | "scope": "javascriptreact,typescriptreact", 4 | "prefix": "qonRequest", 5 | "description": "onRequest function for a route index", 6 | "body": [ 7 | "export const onRequest: RequestHandler = (request) => {", 8 | " $0", 9 | "};", 10 | ], 11 | }, 12 | "loader$": { 13 | "scope": "javascriptreact,typescriptreact", 14 | "prefix": "qloader$", 15 | "description": "loader$()", 16 | "body": ["export const $1 = routeLoader$(() => {", " $0", "});"], 17 | }, 18 | "action$": { 19 | "scope": "javascriptreact,typescriptreact", 20 | "prefix": "qaction$", 21 | "description": "action$()", 22 | "body": ["export const $1 = routeAction$((data) => {", " $0", "});"], 23 | }, 24 | "Full Page": { 25 | "scope": "javascriptreact,typescriptreact", 26 | "prefix": "qpage", 27 | "description": "Simple page component", 28 | "body": [ 29 | "import { component$ } from '@builder.io/qwik';", 30 | "", 31 | "export default component$(() => {", 32 | " $0", 33 | "});", 34 | ], 35 | }, 36 | } 37 | -------------------------------------------------------------------------------- /web/src/routes/layout.tsx: -------------------------------------------------------------------------------- 1 | import { component$, useContextProvider, Slot } from "@builder.io/qwik"; 2 | import { routeLoader$, type RequestHandler } from "@builder.io/qwik-city"; 3 | import jsyaml from "js-yaml"; 4 | 5 | import Navbar from "~/components/furniture/nav"; 6 | import Footer from "~/components/furniture/footer"; 7 | import { ChecklistContext } from "~/store/checklist-context"; 8 | import type { Sections } from "~/types/PSC"; 9 | 10 | export const useChecklists = routeLoader$(async () => { 11 | const remoteUrl = 'https://raw.githubusercontent.com/Lissy93/personal-security-checklist/HEAD/personal-security-checklist.yml'; 12 | return fetch(remoteUrl) 13 | .then((res) => res.text()) 14 | .then((res) => jsyaml.load(res) as Sections) 15 | .catch(() => []); 16 | }); 17 | 18 | export const onGet: RequestHandler = async ({ cacheControl }) => { 19 | cacheControl({ 20 | staleWhileRevalidate: 60 * 60 * 24 * 7, 21 | maxAge: 5, 22 | }); 23 | }; 24 | 25 | export default component$(() => { 26 | const checklists = useChecklists(); 27 | useContextProvider(ChecklistContext, checklists); 28 | 29 | return ( 30 | <> 31 | 32 |
33 | 34 |
35 |