├── bun.lockb ├── static ├── css │ ├── input.css │ └── output.css └── js │ └── htpl.js ├── tailwind.config.js ├── package.json ├── README.md ├── jsconfig.json ├── index.jsx └── .gitignore /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Phillip-England/receipt-tracker/main/bun.lockb -------------------------------------------------------------------------------- /static/css/input.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: [ 4 | "./src/**.js", 5 | "./src/**.jsx", 6 | ], 7 | theme: { 8 | extend: {}, 9 | }, 10 | plugins: [], 11 | } 12 | 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "receipts", 3 | "version": "0.0.1", 4 | "author": "Phillip England", 5 | "module": "index.jsx", 6 | "devDependencies": { 7 | "@types/bun": "latest" 8 | }, 9 | "peerDependencies": { 10 | "typescript": "^5.0.0" 11 | }, 12 | "description": "tiny http server for bun", 13 | "scripts": { 14 | "dev": "bun --hot run index.jsx", 15 | "tw": "tailwindcss -i './static/css/input.css' -o './static/css/output.css' --watch" 16 | }, 17 | "type": "module", 18 | "dependencies": { 19 | "server": "github:react-dom/server", 20 | "xerus": "^0.0.18" 21 | } 22 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # receipts 2 | 3 | ## Env Vars 4 | 5 | ```bash 6 | USERNAME=username 7 | PASSWORD=password 8 | URL_SECRET_TOKEN=some_token 9 | ``` 10 | 11 | ## Todo 12 | 13 | 1. Update HTPL to enable whitelisting of approved file types for uploads 14 | 15 | 2. Update Xerus to better handle network timeouts, especially in regards to trying to access a null value in XerusContext which triggers a timeout when one has not occured. 16 | 17 | 3. Update HTPL to enable _active-link to allow a "flex" option where hrefs do not have to match exactly, but can be close enough. For example, if _active-links href has parameters within it, it may not indicate as active due to not exactly matching. So enable to ability for some flexibility here. -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // Enable latest features 4 | "lib": ["ESNext", "DOM"], 5 | "target": "ESNext", 6 | "module": "ESNext", 7 | "moduleDetection": "force", 8 | "jsx": "react-jsx", 9 | "allowJs": true, 10 | 11 | // Bundler mode 12 | "moduleResolution": "bundler", 13 | "allowImportingTsExtensions": true, 14 | "verbatimModuleSyntax": true, 15 | "noEmit": true, 16 | 17 | // Best practices 18 | "strict": true, 19 | "skipLibCheck": true, 20 | "noFallthroughCasesInSwitch": true, 21 | 22 | // Some stricter flags (disabled by default) 23 | "noUnusedLocals": false, 24 | "noUnusedParameters": false, 25 | "noPropertyAccessFromIndexSignature": false 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /index.jsx: -------------------------------------------------------------------------------- 1 | import { Xerus, logger, timeout } from "xerus"; 2 | 3 | const app = new Xerus(); 4 | 5 | app.use("*", logger, timeout); 6 | 7 | function Layout(props) { 8 | return ( 9 | 10 | 11 | 12 | {props.title} 13 | 14 | {props.children} 15 | 16 | ); 17 | } 18 | 19 | function LoginForm(props) { 20 | return ( 21 |
22 | 23 | 24 | 25 | 26 | 27 |
28 | ); 29 | } 30 | 31 | function Nav(props) { 32 | return ( 33 | , 41 | ); 42 | } 43 | 44 | app.get("/", async (c) => { 45 | c.jsx( 46 | 47 | 48 | , 49 | ); 50 | }); 51 | 52 | app.post("/form/login", async (c) => { 53 | let data = await c.form(); 54 | let username = data.get("username"); 55 | let password = data.get("password"); 56 | if (username == Bun.env.USERNAME && password == Bun.env.PASSWORD) { 57 | c.redirect("/app"); 58 | return; 59 | } 60 | c.redirect("/?loginErr=invalid credentials"); 61 | }); 62 | 63 | app.get("/logout", async (c) => { 64 | c.redirect("/"); 65 | }); 66 | 67 | app.get("/app", async (c) => { 68 | c.jsx( 69 | 70 |