Self-hosted, federated location sharing app and server that prioritizes user privacy and security
end-to-end-encryption location-sharing privacy self-hosted federated

Updated the home page to match auth

Changed files
+218
app
+34
app/.gitignore
··· 1 + # dependencies (bun install) 2 + node_modules 3 + 4 + # output 5 + out 6 + dist 7 + *.tgz 8 + 9 + # code coverage 10 + coverage 11 + *.lcov 12 + 13 + # logs 14 + logs 15 + _.log 16 + report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 17 + 18 + # dotenv environment variable files 19 + .env 20 + .env.development.local 21 + .env.test.local 22 + .env.production.local 23 + .env.local 24 + 25 + # caches 26 + .eslintcache 27 + .cache 28 + *.tsbuildinfo 29 + 30 + # IntelliJ based IDEs 31 + .idea 32 + 33 + # Finder (MacOS) folder config 34 + .DS_Store
+15
app/README.md
··· 1 + # privacypin 2 + 3 + To install dependencies: 4 + 5 + ```bash 6 + bun install 7 + ``` 8 + 9 + To run: 10 + 11 + ```bash 12 + bun run 13 + ``` 14 + 15 + This project was created using `bun init` in bun v1.3.3. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
+116
app/src/settings-page/signup.css
··· 1 + body { 2 + font-family: system-ui, sans-serif; 3 + background: #f9fafb; 4 + display: flex; 5 + align-items: center; 6 + justify-content: center; 7 + height: 100vh; 8 + margin: 0; 9 + } 10 + 11 + .card { 12 + max-width: 90%; 13 + background: white; 14 + border: 1px solid #d1d5db; 15 + border-radius: 8px; 16 + padding: 1.5rem; 17 + box-sizing: border-box; 18 + } 19 + 20 + .header { 21 + text-align: center; 22 + margin-bottom: 1.5rem; 23 + } 24 + 25 + .icon-circle { 26 + width: 64px; 27 + height: 64px; 28 + background: #dbeafe; 29 + border-radius: 50%; 30 + display: flex; 31 + align-items: center; 32 + justify-content: center; 33 + margin: 0 auto 1rem; 34 + } 35 + 36 + .icon-circle img { 37 + width: 32px; 38 + height: 32px; 39 + } 40 + 41 + h1 { 42 + font-size: 1.5rem; 43 + } 44 + 45 + p { 46 + font-size: 0.9rem; 47 + color: #6b7280; 48 + } 49 + 50 + .actions { 51 + display: flex; 52 + flex-direction: column; 53 + gap: 1rem; 54 + } 55 + 56 + label { 57 + display: block; 58 + font-size: 0.85rem; 59 + font-weight: 600; 60 + margin-bottom: 0.25rem; 61 + } 62 + 63 + input { 64 + width: 100%; 65 + padding: 0.5rem 0.75rem; 66 + border: 1px solid #d1d5db; 67 + border-radius: 4px; 68 + font-size: 0.95rem; 69 + box-sizing: border-box; 70 + } 71 + 72 + input:focus { 73 + outline: none; 74 + border-color: #2563eb; 75 + } 76 + 77 + button { 78 + width: 100%; 79 + padding: 0.6rem; 80 + font-size: 0.95rem; 81 + border-radius: 4px; 82 + cursor: pointer; 83 + /*transition: background 0.2s ease;*/ 84 + display: flex; 85 + align-items: center; 86 + justify-content: center; 87 + } 88 + 89 + .btn-primary { 90 + background: #2563eb; 91 + color: white; 92 + border: none; 93 + } 94 + 95 + .btn-primary:hover { 96 + background: #1d4ed8; 97 + } 98 + 99 + .btn-qr { 100 + background: white; 101 + gap: 0.5rem; 102 + border: 1px solid #d1d5db; 103 + } 104 + 105 + .btn-qr:hover { 106 + background: #f3f4f6; 107 + } 108 + 109 + .btn-qr img { 110 + width: 16px; 111 + height: 16px; 112 + } 113 + 114 + .hint { 115 + font-size: 0.75rem; 116 + }
+42
app/src/settings-page/signup.html
··· 1 + <!doctype html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8" /> 5 + <script type="module" src="./signup.ts"></script> 6 + <link rel="stylesheet" href="./signup.css" /> 7 + </head> 8 + 9 + <body> 10 + <div class="card"> 11 + <div class="header"> 12 + <div class="icon-circle"> 13 + <img src="/src/assets/pin.svg" alt="Pin Icon" /> 14 + </div> 15 + <h1>PrivacyPin</h1> 16 + <p>Connect with a server to start sharing</p> 17 + </div> 18 + 19 + <!-- x-data connects this element to the signupPageState Alpine component, enabling its data (serverAddress and signupKey) and functions (signup and scanQR) to work within it :) --> 20 + <!-- TODO: make this a form instead? --> 21 + <div class="actions" x-data="signupPageState"> 22 + <div> 23 + <label for="server">Server Address</label> 24 + <input id="server" type="url" placeholder="https://your-server.com" x-model="serverAddress" required /> 25 + </div> 26 + 27 + <div> 28 + <label for="key">Signup Key</label> 29 + <input id="key" type="password" placeholder="Enter your signup key" x-model="signupKey" required /> 30 + </div> 31 + 32 + <p class="hint">Scan a QR code to automatically fill both server address and signup key</p> 33 + <button type="button" x-bind:disabled="isDoingStuff" class="btn-qr" @click="await scanQR()"> 34 + <img src="/src/assets/qr.svg" alt="QR Icon" /> 35 + Scan QR Code 36 + </button> 37 + 38 + <button class="btn-primary" x-bind:disabled="isDoingStuff" @click="await signup()"><span x-show="isDoingStuff">Connecting...</span> <span x-show="!isDoingStuff">Connect</span></button> 39 + </div> 40 + </div> 41 + </body> 42 + </html>
+11
app/src/settings-page/signup.ts
··· 1 + import Alpine from "alpinejs"; 2 + import { createAccount } from "../utils/api.ts"; 3 + import { Store } from "../utils/store.ts"; 4 + 5 + Alpine.data("signupPageState", () => ({ 6 + resetStore() { 7 + Store.reset(); 8 + }, 9 + })); 10 + 11 + Alpine.start();