Compare commits

...

No commits in common. "master-fresh" and "master" have entirely different histories.

42 changed files with 602 additions and 622 deletions

12
.gitignore vendored
View file

@ -1,12 +0,0 @@
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# Fresh build directory
_fresh/
# npm dependencies
node_modules/
/local

View file

@ -1,5 +0,0 @@
{
"recommendations": [
"denoland.vscode-deno"
]
}

18
.vscode/settings.json vendored
View file

@ -1,18 +1,8 @@
{
"deno.enablePaths": [
"./"
],
"deno.enable": true,
"deno.lint": true,
"deno.unstable": true,
"editor.defaultFormatter": "denoland.vscode-deno",
"[typescriptreact]": {
"editor.defaultFormatter": "denoland.vscode-deno"
},
"[typescript]": {
"editor.defaultFormatter": "denoland.vscode-deno"
},
"[javascriptreact]": {
"editor.defaultFormatter": "denoland.vscode-deno"
},
"[javascript]": {
"editor.defaultFormatter": "denoland.vscode-deno"
}
"editor.inlayHints.enabled": "off"
}

View file

@ -1,16 +1,7 @@
# Fresh project
# Twifeur
Your new Fresh project is ready to go. You can follow the Fresh "Getting
Started" guide here: https://fresh.deno.dev/docs/getting-started
Feur
### Usage
## Feur
Make sure to install Deno: https://deno.land/manual/getting_started/installation
Then start the project:
```
deno task start
```
This will watch the project directory and restart as necessary.
Feur feur.

16
api/login.ts Normal file
View file

@ -0,0 +1,16 @@
import { Context } from "https://deno.land/x/hono@v4.3.10/mod.ts";
import { BlankInput } from "https://deno.land/x/hono@v4.3.10/types.ts";
import { FeurEnv } from "../main.ts";
import { login, set_user } from "../lib/auth.ts";
export async function login_route(context: Context<FeurEnv, string, BlankInput>) {
const data = await context.req.formData();
let username = data.get("login"), pass = data.get("password");
if (username === null || pass === null) return context.redirect("/login");
username = username.toString(), pass = pass.toString();
const logged = await login(username, pass);
if (logged === null) return context.redirect("/login");
console.log("Logged in", { username });
set_user(context, logged);
return context.redirect("/user");
}

9
api/logout.ts Normal file
View file

@ -0,0 +1,9 @@
import { Context } from "https://deno.land/x/hono@v4.3.10/mod.ts";
import { BlankInput } from "https://deno.land/x/hono@v4.3.10/types.ts";
import { FeurEnv } from "../main.ts";
import { set_user } from "../lib/auth.ts";
export function logout_route(context: Context<FeurEnv, string, BlankInput>) {
set_user(context, null);
return context.redirect("/");
}

View file

@ -1,72 +0,0 @@
import { assert } from "$std/assert/assert.ts";
import { User } from "../lib/store.ts";
import { generate } from "https://deno.land/std@0.224.0/uuid/v1.ts";
class Authentificator {
tokens;
user_storage;
constructor(user_storage: UserStorage) {
this.tokens = new TokenSet();
this.user_storage = user_storage;
}
}
interface UserStorage {
get_user(login: string): User | null;
}
export class Token {
raw;
user_id;
constructor(raw: string, user_id: string) {
this.raw = raw;
this.user_id = user_id;
}
}
class TokenSet {
tokens;
constructor() {
this.tokens = new Map<string, Token>();
}
get(token: string) {
return this.tokens.get(token) ?? null;
}
create(user_id: string) {
const raw = generate();
assert(typeof raw == "string");
const token = new Token(raw, user_id);
this.tokens.set(raw, token);
}
}
class StaticUserStorage implements UserStorage {
users;
constructor() {
this.users = new Map<string, User>();
}
get_user(user_id: string) {
return this.users.get(user_id) ?? null;
}
with(user: User) {
this.users.set(user.id, user);
return this;
}
}
export const auth = new Authentificator(
new StaticUserStorage().with({
id: "pleinplein",
email: "feur@feur.feur",
like_set: new Set(),
name: "Feur",
password: "feurfeur",
pfp_url: "https://feur.feur.feur/feur.feur",
}),
);

View file

@ -1,12 +0,0 @@
import { JSX } from "preact";
import { IS_BROWSER } from "$fresh/runtime.ts";
export function Button(props: JSX.HTMLAttributes<HTMLButtonElement>) {
return (
<button
{...props}
disabled={!IS_BROWSER || props.disabled}
class="px-2 py-1 border-gray-500 border-2 rounded bg-white hover:bg-gray-200 transition-colors"
/>
);
}

View file

@ -1,39 +1,6 @@
{
"lock": false,
"tasks": {
"check": "deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx",
"cli": "echo \"import '\\$fresh/src/dev/cli.ts'\" | deno run --unstable -A -",
"manifest": "deno task cli manifest $(pwd)",
"start": "deno run -A --unstable --watch=static/,routes/ dev.ts",
"build": "deno run -A dev.ts build",
"preview": "deno run -A main.ts",
"update": "deno run -A -r https://fresh.deno.dev/update ."
},
"lint": {
"rules": {
"tags": [
"fresh",
"recommended"
]
}
},
"exclude": [
"**/_fresh/*"
],
"imports": {
"$fresh/": "https://deno.land/x/fresh@1.6.8/",
"preact": "https://esm.sh/preact@10.19.6",
"preact/": "https://esm.sh/preact@10.19.6/",
"@preact/signals": "https://esm.sh/*@preact/signals@1.2.2",
"@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.5.1",
"$std/": "https://deno.land/std@0.216.0/"
},
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "preact"
},
"fmt": {
"useTabs": true,
"lineWidth": 120
}
"fmt": {
"lineWidth": 120,
"useTabs": true
}
}

223
deno.lock generated Normal file
View file

@ -0,0 +1,223 @@
{
"version": "3",
"packages": {
"specifiers": {
"npm:@types/node": "npm:@types/node@18.16.19",
"npm:hono@^4.0.0": "npm:hono@4.4.0",
"npm:iron-webcrypto@0.10.1": "npm:iron-webcrypto@0.10.1"
},
"npm": {
"@types/node@18.16.19": {
"integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA==",
"dependencies": {}
},
"hono@4.4.0": {
"integrity": "sha512-Bb2GHk8jmlLIuxc3U+7UBGOoA5lByJTAFnRdH2N2fqEVy9TEQzJ9saIJUQ/ZqBvEvgEFe7UjPFNSFi8cyeU+3Q==",
"dependencies": {}
},
"iron-webcrypto@0.10.1": {
"integrity": "sha512-QGOS8MRMnj/UiOa+aMIgfyHcvkhqNUsUxb1XzskENvbo+rEfp6TOwqd1KPuDzXC4OnGHcMSVxDGRoilqB8ViqA==",
"dependencies": {}
}
}
},
"redirects": {
"https://deno.land/x/deno_dom/deno-dom-wasm.ts": "https://deno.land/x/deno_dom@v0.1.45/deno-dom-wasm.ts",
"https://deno.land/x/hono/middleware.ts": "https://deno.land/x/hono@v4.3.10/middleware.ts",
"https://deno.land/x/hono_sessions/mod.ts": "https://deno.land/x/hono_sessions@v0.5.8/mod.ts",
"https://esm.sh/v135/@types/css@latest/index.d.ts": "https://esm.sh/v135/@types/css@0.0.37/index.d.ts"
},
"remote": {
"https://deno.land/std@0.224.0/path/_common/assert_path.ts": "dbdd757a465b690b2cc72fc5fb7698c51507dec6bfafce4ca500c46b76ff7bd8",
"https://deno.land/std@0.224.0/path/_common/constants.ts": "dc5f8057159f4b48cd304eb3027e42f1148cf4df1fb4240774d3492b5d12ac0c",
"https://deno.land/std@0.224.0/path/_common/dirname.ts": "684df4aa71a04bbcc346c692c8485594fc8a90b9408dfbc26ff32cf3e0c98cc8",
"https://deno.land/std@0.224.0/path/_common/strip_trailing_separators.ts": "7024a93447efcdcfeaa9339a98fa63ef9d53de363f1fbe9858970f1bba02655a",
"https://deno.land/std@0.224.0/path/_os.ts": "8fb9b90fb6b753bd8c77cfd8a33c2ff6c5f5bc185f50de8ca4ac6a05710b2c15",
"https://deno.land/std@0.224.0/path/dirname.ts": "85bd955bf31d62c9aafdd7ff561c4b5fb587d11a9a5a45e2b01aedffa4238a7c",
"https://deno.land/std@0.224.0/path/posix/_util.ts": "1e3937da30f080bfc99fe45d7ed23c47dd8585c5e473b2d771380d3a6937cf9d",
"https://deno.land/std@0.224.0/path/posix/dirname.ts": "76cd348ffe92345711409f88d4d8561d8645353ac215c8e9c80140069bf42f00",
"https://deno.land/std@0.224.0/path/windows/_util.ts": "d5f47363e5293fced22c984550d5e70e98e266cc3f31769e1710511803d04808",
"https://deno.land/std@0.224.0/path/windows/dirname.ts": "33e421be5a5558a1346a48e74c330b8e560be7424ed7684ea03c12c21b627bc9",
"https://deno.land/x/deno_dom@v0.1.45/build/deno-wasm/deno-wasm.js": "d6841a06342eb6a2798ef28de79ad69c0f2fa349fa04d3ca45e5fcfbf50a9340",
"https://deno.land/x/deno_dom@v0.1.45/deno-dom-wasm.ts": "a33d160421bbb6e3104285ea5ebf33352b7ad50d82ea8765e3cf65f972b25119",
"https://deno.land/x/deno_dom@v0.1.45/src/api.ts": "0ff5790f0a3eeecb4e00b7d8fbfa319b165962cf6d0182a65ba90f158d74f7d7",
"https://deno.land/x/deno_dom@v0.1.45/src/constructor-lock.ts": "59714df7e0571ec7bd338903b1f396202771a6d4d7f55a452936bd0de9deb186",
"https://deno.land/x/deno_dom@v0.1.45/src/deserialize.ts": "1cf4096678d8afed8ed28dbad690504c4d2c28149ba768b26eacd1416873425b",
"https://deno.land/x/deno_dom@v0.1.45/src/dom/document-fragment.ts": "1c7352a3c816587ed7fad574b42636198f680f17abc3836fcfe7799b31e7718f",
"https://deno.land/x/deno_dom@v0.1.45/src/dom/document.ts": "a182727dd9179e5712e31be66f4f72b766a5b714c765a75950babe6dd756b4ee",
"https://deno.land/x/deno_dom@v0.1.45/src/dom/dom-parser.ts": "609097b426f8c2358f3e5d2bca55ed026cf26cdf86562e94130dfdb0f2537f92",
"https://deno.land/x/deno_dom@v0.1.45/src/dom/element.ts": "d5371cd83ff2128353c1975465c368ef83d7441568626b386557deba51315111",
"https://deno.land/x/deno_dom@v0.1.45/src/dom/elements/html-template-element.ts": "740b97a5378c9a14cccf3429299846eda240b613013e2d2d7f20b393897453c2",
"https://deno.land/x/deno_dom@v0.1.45/src/dom/html-collection.ts": "829a965f419f8286d5f43a12e27886d10836d519ca2d5e74cb3f2e1d35f35746",
"https://deno.land/x/deno_dom@v0.1.45/src/dom/node-list.ts": "9008303fe236e40e74f9f93e398bd173d2e9b09065932a0153dd0142c759397b",
"https://deno.land/x/deno_dom@v0.1.45/src/dom/node.ts": "3069e6fc93ac4111a136ed68199d76673339842b9751610ba06f111ba7dc10a7",
"https://deno.land/x/deno_dom@v0.1.45/src/dom/selectors/custom-api.ts": "852696bd58e534bc41bd3be9e2250b60b67cd95fd28ed16b1deff1d548531a71",
"https://deno.land/x/deno_dom@v0.1.45/src/dom/selectors/nwsapi-types.ts": "c43b36c36acc5d32caabaa54fda8c9d239b2b0fcbce9a28efb93c84aa1021698",
"https://deno.land/x/deno_dom@v0.1.45/src/dom/selectors/nwsapi.js": "985d7d8fc1eabbb88946b47a1c44c1b2d4aa79ff23c21424219f1528fa27a2ff",
"https://deno.land/x/deno_dom@v0.1.45/src/dom/selectors/selectors.ts": "83eab57be2290fb48e3130533448c93c6c61239f2a2f3b85f1917f80ca0fdc75",
"https://deno.land/x/deno_dom@v0.1.45/src/dom/selectors/sizzle-types.ts": "78149e2502409989ce861ed636b813b059e16bc267bb543e7c2b26ef43e4798b",
"https://deno.land/x/deno_dom@v0.1.45/src/dom/selectors/sizzle.js": "c3aed60c1045a106d8e546ac2f85cc82e65f62d9af2f8f515210b9212286682a",
"https://deno.land/x/deno_dom@v0.1.45/src/dom/utils-types.ts": "96db30e3e4a75b194201bb9fa30988215da7f91b380fca6a5143e51ece2a8436",
"https://deno.land/x/deno_dom@v0.1.45/src/dom/utils.ts": "4c6206516fb8f61f37a209c829e812c4f5a183e46d082934dd14c91bde939263",
"https://deno.land/x/deno_dom@v0.1.45/src/parser.ts": "e06b2300d693e6ae7564e53dfa5c9a9e97fdb8c044c39c52c8b93b5d60860be3",
"https://deno.land/x/hono@v4.3.10/adapter/deno/serve-static.ts": "db226d30f08f1a8bb77653ead42a911357b2f8710d653e43c01eccebb424b295",
"https://deno.land/x/hono@v4.3.10/client/client.ts": "dcda3887257fa3164db7b32c56665c6e757f0ef047a14f3f9599ef41725c1525",
"https://deno.land/x/hono@v4.3.10/client/index.ts": "30def535310a37bede261f1b23d11a9758983b8e9d60a6c56309cee5f6746ab2",
"https://deno.land/x/hono@v4.3.10/client/types.ts": "73fbec704cf968ca5c5bba10aa1131758c52ab31a78301451c79cfc47368d43c",
"https://deno.land/x/hono@v4.3.10/client/utils.ts": "8be84b49c5c7952666875a8e901fde3044c85c853ea6ba3a7e2c0468478459c0",
"https://deno.land/x/hono@v4.3.10/compose.ts": "37d6e33b7db80e4c43a0629b12fd3a1e3406e7d9e62a4bfad4b30426ea7ae4f1",
"https://deno.land/x/hono@v4.3.10/context.ts": "facfd749d823a645039571d66d9d228f5ae6836818b65d3b6c4c6891adfe071e",
"https://deno.land/x/hono@v4.3.10/helper/adapter/index.ts": "ff7e11eb1ca1fbd74ca3c46cd1d24014582f91491ef6d3846d66ed1cede18ec4",
"https://deno.land/x/hono@v4.3.10/helper/cookie/index.ts": "689c84eae410f0444a4598f136a4f859b9122ec6f790dff74412d34405883db8",
"https://deno.land/x/hono@v4.3.10/helper/html/index.ts": "48a0ddc576c10452db6c3cab03dd4ee6986ab61ebdc667335b40a81fa0487f69",
"https://deno.land/x/hono@v4.3.10/hono-base.ts": "fd7e9c1bba1e13119e95158270011784da3a7c3014c149ba0700e700f840ae0d",
"https://deno.land/x/hono@v4.3.10/hono.ts": "23edd0140bf0bd5a68c14ae96e5856a5cec6b844277e853b91025e91ea74f416",
"https://deno.land/x/hono@v4.3.10/http-exception.ts": "f5dd375e61aa4b764eb9b99dd45a7160f8317fd36d3f79ae22585b9a5e8ad7c5",
"https://deno.land/x/hono@v4.3.10/jsx/base.ts": "33f1c302c8f72ae948abd9c3ef85f4b3be6525251a13b95fd18fe2910b7d4a0d",
"https://deno.land/x/hono@v4.3.10/jsx/children.ts": "26ead0f151faba5307883614b5b064299558f06798c695c432f32acbb1127d56",
"https://deno.land/x/hono@v4.3.10/jsx/components.ts": "f79ab215f59388f01a69e2d6ec0b841fd3b42ba38e0ee7c93a525cdf06e159f9",
"https://deno.land/x/hono@v4.3.10/jsx/constants.ts": "984e0797194be1fbc935cb688c8d0a60c112b21bc59301be5354c02232f18820",
"https://deno.land/x/hono@v4.3.10/jsx/context.ts": "2b7a86e6b35da171fab27aa05f09748bb3eba64b26c037ea1da655c07e8f6bc1",
"https://deno.land/x/hono@v4.3.10/jsx/dom/components.ts": "733da654edb3d4c178a4479649fac2c64e79069e37e848add0c3a49f90e7f2d7",
"https://deno.land/x/hono@v4.3.10/jsx/dom/context.ts": "06209d14553398750c69252cc826082018cefa277f5c82cbe58d7261c8a2d81e",
"https://deno.land/x/hono@v4.3.10/jsx/dom/jsx-dev-runtime.ts": "ba87562d14b77dd5f2a3cc30d41b1eb5edb0800e5f4a7337b5b87b2e66f8a099",
"https://deno.land/x/hono@v4.3.10/jsx/dom/jsx-runtime.ts": "6a50a65306771a9000030f494d92a5fdeeb055112e0126234b2fd9179de1d4f5",
"https://deno.land/x/hono@v4.3.10/jsx/dom/render.ts": "7db816d40de58c60e1cbdab64ac3f170b1e30696ed61ad449bbb823f60b46146",
"https://deno.land/x/hono@v4.3.10/jsx/dom/utils.ts": "5d3e8c14996902db9c1223041fb21480fa0e921a4ccdc59f8c7571c08b7810f2",
"https://deno.land/x/hono@v4.3.10/jsx/hooks/index.ts": "b7e0f0a754f31a1e1fbe0ac636b38b031603eb0ae195c32a30769a11d79fb871",
"https://deno.land/x/hono@v4.3.10/jsx/index.ts": "fe3e582c2a4e24e5f8b6027925bddccaae0283747d8f0161eb6f5a34616edd11",
"https://deno.land/x/hono@v4.3.10/jsx/intrinsic-elements.ts": "21c3a8f6ba07f0d7d7c0ec7293c79c26b9b62df2894e26cb6c17b6c7ec381264",
"https://deno.land/x/hono@v4.3.10/jsx/streaming.ts": "5e5dde9a546041353b9a3860fc9020471f762813f10e1290009ab6bd40e7bdcf",
"https://deno.land/x/hono@v4.3.10/jsx/types.ts": "51c2bdbb373860e2570ad403546a7fdbbb1cf00a47ce7ed10b2aece922031ac4",
"https://deno.land/x/hono@v4.3.10/jsx/utils.ts": "4b8299d402ba5395472c552d1fe3297ee60112bfc32e0ef86cfe8e40086f7d54",
"https://deno.land/x/hono@v4.3.10/middleware.ts": "2e7c6062e36b0e5f84b44a62e7b0e1cef33a9827c19937c648be4b63e1b7d7c6",
"https://deno.land/x/hono@v4.3.10/middleware/basic-auth/index.ts": "2c8cb563f3b89df1a7a2232be37377c3df6194af38613dc0a823c6595816fc66",
"https://deno.land/x/hono@v4.3.10/middleware/bearer-auth/index.ts": "b3b7469bc0eb9543c6c47f3ff67de879210dd73063307a61536042ff30e8720e",
"https://deno.land/x/hono@v4.3.10/middleware/body-limit/index.ts": "3fefeaf7e6e576aa1b33f2694072d2eaab692842acd29cb360d98e20eebfe5aa",
"https://deno.land/x/hono@v4.3.10/middleware/cache/index.ts": "5e6273e5c9ea73ef387b25923ab23274c220b29d7c981b62ac0be26d6a1aa3d8",
"https://deno.land/x/hono@v4.3.10/middleware/compress/index.ts": "98c403a5fe7e9c5f5d776350b422b0a125fb34696851b8b14f825b9b7b06f2ac",
"https://deno.land/x/hono@v4.3.10/middleware/cors/index.ts": "976eb9ce8cefc214b403a2939503a13177cec76223274609a07ca554e0dc623b",
"https://deno.land/x/hono@v4.3.10/middleware/csrf/index.ts": "077bb0ce299d79d0d232cb9e462aaa4eaa901164f1310f74a7630f7e6cfe74e8",
"https://deno.land/x/hono@v4.3.10/middleware/etag/index.ts": "95e0270ea349cf00537ee6e58985a4cc7dba44091ca8e2dc42b6d8b2f01bcfe7",
"https://deno.land/x/hono@v4.3.10/middleware/jsx-renderer/index.ts": "229322c66ebc7f426cd2d71f282438025b4ee7ce8cb8e97e87c7efbc94530c19",
"https://deno.land/x/hono@v4.3.10/middleware/jwt/index.ts": "4cb997d3d7a09d0b0c0e273841d29729e13e35dfc00021089aebaad868a7f8c6",
"https://deno.land/x/hono@v4.3.10/middleware/logger/index.ts": "52a2e968890ada2c11ce89a7a783692c5767b8ed7fb23ccf6b559d255d13ccbc",
"https://deno.land/x/hono@v4.3.10/middleware/method-override/index.ts": "bc13bdcf70c777b72b1300a5cca1b51a8bd126e0d922b991d89e96fe7c694b5b",
"https://deno.land/x/hono@v4.3.10/middleware/powered-by/index.ts": "6faba0cf042278d60b317b690640bb0b58747690cf280fa09024424c5174e66d",
"https://deno.land/x/hono@v4.3.10/middleware/pretty-json/index.ts": "2216ce4c9910be009fecac63367c3626b46137d4cf7cb9a82913e501104b4a88",
"https://deno.land/x/hono@v4.3.10/middleware/secure-headers/index.ts": "f2e4c3858d26ff47bc6909513607e6a3c31184aabe78fb272ed08e1d62a750f0",
"https://deno.land/x/hono@v4.3.10/middleware/serve-static/index.ts": "14b760bbbc4478cc3a7fb9728730bc6300581c890365b7101b80c16e70e4b21e",
"https://deno.land/x/hono@v4.3.10/middleware/timing/index.ts": "6fddbb3e47ae875c16907cf23b9bb503ec2ad858406418b5f38f1e7fbca8c6f6",
"https://deno.land/x/hono@v4.3.10/middleware/trailing-slash/index.ts": "419cf0af99a137f591b72cc71c053e524fe3574393ce81e0e9dbce84a4046e24",
"https://deno.land/x/hono@v4.3.10/mod.ts": "35fd2a2e14b52365e0ad66f168b067363fd0a60d75cbcb1b01685b04de97d60e",
"https://deno.land/x/hono@v4.3.10/request.ts": "7b08602858e642d1626c3106c0bedc2aa8d97e30691a079351d9acef7c5955e6",
"https://deno.land/x/hono@v4.3.10/router.ts": "880316f561918fc197481755aac2165fdbe2f530b925c5357a9f98d6e2cc85c7",
"https://deno.land/x/hono@v4.3.10/router/linear-router/index.ts": "8a2a7144c50b1f4a92d9ee99c2c396716af144c868e10608255f969695efccd0",
"https://deno.land/x/hono@v4.3.10/router/linear-router/router.ts": "928d29894e4b45b047a4f453c7f1745c8b1869cd68447e1cb710c7bbf99a4e29",
"https://deno.land/x/hono@v4.3.10/router/pattern-router/index.ts": "304a66c50e243872037ed41c7dd79ed0c89d815e17e172e7ad7cdc4bc07d3383",
"https://deno.land/x/hono@v4.3.10/router/pattern-router/router.ts": "1b5f68e6af942579d3a40ee834294fea3d1f05fd5f70514e46ae301dd0107e46",
"https://deno.land/x/hono@v4.3.10/router/reg-exp-router/index.ts": "52755829213941756159b7a963097bafde5cc4fc22b13b1c7c9184dc0512d1db",
"https://deno.land/x/hono@v4.3.10/router/reg-exp-router/node.ts": "7efaa6f4301efc2aad0519c84973061be8555da02e5868409293a1fd98536aaf",
"https://deno.land/x/hono@v4.3.10/router/reg-exp-router/router.ts": "632f2fa426b3e45a66aeed03f7205dad6d13e8081bed6f8d1d987b6cad8fb455",
"https://deno.land/x/hono@v4.3.10/router/reg-exp-router/trie.ts": "852ce7207e6701e47fa30889a0d2b8bfcd56d8862c97e7bc9831e0a64bd8835f",
"https://deno.land/x/hono@v4.3.10/router/smart-router/index.ts": "74f9b4fe15ea535900f2b9b048581915f12fe94e531dd2b0032f5610e61c3bef",
"https://deno.land/x/hono@v4.3.10/router/smart-router/router.ts": "dc22a8505a0f345476f07dca3054c0c50a64d7b81c9af5a904476490dfd5cbb4",
"https://deno.land/x/hono@v4.3.10/router/trie-router/index.ts": "3eb75e7f71ba81801631b30de6b1f5cefb2c7239c03797e2b2cbab5085911b41",
"https://deno.land/x/hono@v4.3.10/router/trie-router/node.ts": "d3e00e8f1ba7fb26896459d5bba882356891a07793387c4655d1864c519a91de",
"https://deno.land/x/hono@v4.3.10/router/trie-router/router.ts": "54ced78d35676302c8fcdda4204f7bdf5a7cc907fbf9967c75674b1e394f830d",
"https://deno.land/x/hono@v4.3.10/types.ts": "b561c3ee846121b33c2d81331246cdedf7781636ed72dad7406677105b4275de",
"https://deno.land/x/hono@v4.3.10/utils/body.ts": "774cb319dfbe886a9d39f12c43dea15a39f9d01e45de0323167cdd5d0aad14d4",
"https://deno.land/x/hono@v4.3.10/utils/buffer.ts": "2fae689954b427b51fb84ad02bed11a72eae96692c2973802b3b4c1e39cd5b9c",
"https://deno.land/x/hono@v4.3.10/utils/color.ts": "10575c221f48bc806887710da8285f859f51daf9e6878bbdf99cb406b8494457",
"https://deno.land/x/hono@v4.3.10/utils/cookie.ts": "662529d55703d2c0bad8736cb1274eb97524c0ef7882d99254fc7c8fa925b46c",
"https://deno.land/x/hono@v4.3.10/utils/crypto.ts": "bda0e141bbe46d3a4a20f8fbcb6380d473b617123d9fdfa93e4499410b537acc",
"https://deno.land/x/hono@v4.3.10/utils/encode.ts": "311dfdfae7eb0b6345e9680f7ebbb3a692e872ed964e2029aca38567af8d1f33",
"https://deno.land/x/hono@v4.3.10/utils/filepath.ts": "a83e5fe87396bb291a6c5c28e13356fcbea0b5547bad2c3ba9660100ff964000",
"https://deno.land/x/hono@v4.3.10/utils/html.ts": "6ea4f6bf41587a51607dff7a6d2865ef4d5001e4203b07e5c8a45b63a098e871",
"https://deno.land/x/hono@v4.3.10/utils/http-status.ts": "f5b820f2793e45209f34deddf147b23e3133a89eb4c57dc643759a504706636b",
"https://deno.land/x/hono@v4.3.10/utils/jwt/index.ts": "3b66f48cdd3fcc2caed5e908ca31776e11b1c30391008931276da3035e6ba1e9",
"https://deno.land/x/hono@v4.3.10/utils/jwt/jwa.ts": "6874cacd8b6dde386636b81b5ea2754f8e4c61757802fa908dd1ce54b91a52fa",
"https://deno.land/x/hono@v4.3.10/utils/jwt/jws.ts": "878fa7d1966b0db20ae231cfee279ba2bb198943e949049cab3f5845cd5ee2d1",
"https://deno.land/x/hono@v4.3.10/utils/jwt/jwt.ts": "80452edc3498c6670a211fdcd33cfc4d5c00dfac79aa9f403b0623dedc039554",
"https://deno.land/x/hono@v4.3.10/utils/jwt/types.ts": "b6659ac85e7f8fcdd8cdfc7d51f5d1a91107ad8dfb647a8e4ea9c80f0f02afee",
"https://deno.land/x/hono@v4.3.10/utils/jwt/utf8.ts": "17c507f68f23ccb82503ea6183e54b5f748a6fe621eb60994adfb4a8c2a3f561",
"https://deno.land/x/hono@v4.3.10/utils/mime.ts": "d1fc2c047191ccb01d736c6acf90df731324536298181dba0ecc2259e5f7d661",
"https://deno.land/x/hono@v4.3.10/utils/types.ts": "050bfa9dc6d0cc4b7c5069944a8bd60066c2f9f95ee69833623ad104f11f92bf",
"https://deno.land/x/hono@v4.3.10/utils/url.ts": "855169632c61d03703bd08cafb27664ba3fdb352892f01687d5cce8fd49e3cb1",
"https://deno.land/x/hono@v4.3.10/validator/index.ts": "6c986e8b91dcf857ecc8164a506ae8eea8665792a4ff7215471df669c632ae7c",
"https://deno.land/x/hono@v4.3.10/validator/validator.ts": "53f3d2ad442e22f0bc2d85b7d8d90320d4e5ecf5fdd58882f906055d33a18e13",
"https://deno.land/x/hono_sessions@v0.5.8/deps.ts": "a86ee9f6d96c0dcc80380eaa3d1cef13ff972a56003d8e1d7f461cc32554fa34",
"https://deno.land/x/hono_sessions@v0.5.8/mod.ts": "67e89aebb3c0e87e7e6065aefb56606d2f1017dd8ed030ef742f5bb73398abda",
"https://deno.land/x/hono_sessions@v0.5.8/src/Crypto.ts": "dcbf1dc60d5c67426f3a25bcd56a38012336c2418ab1291550f2fa1f76b66e25",
"https://deno.land/x/hono_sessions@v0.5.8/src/Middleware.ts": "05879181e4f649098c05800a47c12c21649dde41f8d21aceefc7b2dbb91b261b",
"https://deno.land/x/hono_sessions@v0.5.8/src/Session.ts": "9de1c3661a9b713a4eded0457a60432a96118be59741cb32e43c39a437da33cd",
"https://deno.land/x/hono_sessions@v0.5.8/src/store/CookieStore.ts": "9175f9da3e555c0e07928db03bb26ad40b6b0c1f661bfccd9aeddad0a615ccc2",
"https://deno.land/x/hono_sessions@v0.5.8/src/store/MemoryStore.ts": "f702d81a948a775207f047b60e2dd6c903a4b25b041c827309486055be3103fd",
"https://deno.land/x/hono_sessions@v0.5.8/src/store/Store.ts": "8f26b96fa22c330846ccb318cb90d3d1135aec9078c0faa7b5dc39ceb49a0da4",
"https://deno.land/x/zod@v3.22.4/ZodError.ts": "4de18ff525e75a0315f2c12066b77b5c2ae18c7c15ef7df7e165d63536fdf2ea",
"https://deno.land/x/zod@v3.22.4/errors.ts": "5285922d2be9700cc0c70c95e4858952b07ae193aa0224be3cbd5cd5567eabef",
"https://deno.land/x/zod@v3.22.4/external.ts": "a6cfbd61e9e097d5f42f8a7ed6f92f93f51ff927d29c9fbaec04f03cbce130fe",
"https://deno.land/x/zod@v3.22.4/helpers/enumUtil.ts": "54efc393cc9860e687d8b81ff52e980def00fa67377ad0bf8b3104f8a5bf698c",
"https://deno.land/x/zod@v3.22.4/helpers/errorUtil.ts": "7a77328240be7b847af6de9189963bd9f79cab32bbc61502a9db4fe6683e2ea7",
"https://deno.land/x/zod@v3.22.4/helpers/parseUtil.ts": "f791e6e65a0340d85ad37d26cd7a3ba67126cd9957eac2b7163162155283abb1",
"https://deno.land/x/zod@v3.22.4/helpers/partialUtil.ts": "998c2fe79795257d4d1cf10361e74492f3b7d852f61057c7c08ac0a46488b7e7",
"https://deno.land/x/zod@v3.22.4/helpers/typeAliases.ts": "0fda31a063c6736fc3cf9090dd94865c811dfff4f3cb8707b932bf937c6f2c3e",
"https://deno.land/x/zod@v3.22.4/helpers/util.ts": "8baf19b19b2fca8424380367b90364b32503b6b71780269a6e3e67700bb02774",
"https://deno.land/x/zod@v3.22.4/index.ts": "d27aabd973613985574bc31f39e45cb5d856aa122ef094a9f38a463b8ef1a268",
"https://deno.land/x/zod@v3.22.4/locales/en.ts": "a7a25cd23563ccb5e0eed214d9b31846305ddbcdb9c5c8f508b108943366ab4c",
"https://deno.land/x/zod@v3.22.4/mod.ts": "64e55237cb4410e17d968cd08975566059f27638ebb0b86048031b987ba251c4",
"https://deno.land/x/zod@v3.22.4/types.ts": "724185522fafe43ee56a52333958764c8c8cd6ad4effa27b42651df873fc151e",
"https://esm.sh/jsdom@22.1.0": "2efb54a90536dc0f4bb00f442c32fcedb2224af5aad5c87db87660f15c013170",
"https://esm.sh/v128/@tootallnate/once@2.0.0/denonext/once.mjs": "2c69d53dd1de1d7e5f9a78d46041f5d869f7d058f22d72e7f25821e61dddd147",
"https://esm.sh/v128/abab@2.0.6/denonext/abab.mjs": "7101c07a728882d686222e5f5c5a6fa956a558e4f406c521f1c0b61bc60cd07d",
"https://esm.sh/v128/agent-base@6.0.2/denonext/agent-base.mjs": "73a5fb40647fa22367f61cb5573a0404c21fed9d86445d21ece399ff47e097ad",
"https://esm.sh/v128/bufferutil@4.0.7/denonext/bufferutil.mjs": "f08b440283b2993a9e360b0e092c1714bc689230323961d60d7aad805b79c026",
"https://esm.sh/v128/canvas@2.11.2/denonext/canvas.mjs": "4245b1d01d91b5e807b85e40e98efe28c93634260bd8cb5ac0da71c42098a1a4",
"https://esm.sh/v128/cssstyle@3.0.0/denonext/cssstyle.mjs": "4fe8a9be3507541807575ed9254ca9e74c3ad950777a4478876ed52ac9c2e6da",
"https://esm.sh/v128/data-urls@4.0.0/denonext/data-urls.mjs": "fa125b728922f32cac094291ac3b8c0abcfac2c9963ee2c6280769f0e972519d",
"https://esm.sh/v128/debug@4.3.4/denonext/debug.mjs": "b837e7a39e434a676c751cd5ebd29351d8ad48d083006582edcf40361cadeccc",
"https://esm.sh/v128/decimal.js@10.4.3/denonext/decimal.mjs": "ece864b80732112b324aed7fd89ec03128627206e1e4b30057651f495b594dee",
"https://esm.sh/v128/domexception@4.0.0/denonext/webidl2js-wrapper.js": "d5c3a0961bc0fe877f335ade79e191d3e0848785a6b4521f67c54303f0c5a39d",
"https://esm.sh/v128/entities@4.5.0/denonext/lib/decode.js": "7fea6d8bd725edbbf7ea05031d2ea1bbbc1166dc11e3345d541198dd2dc16f1e",
"https://esm.sh/v128/entities@4.5.0/denonext/lib/escape.js": "7ebdc622bf3618bab25db40da4a49e2b9d03f044745f125f0bc3359f2d060def",
"https://esm.sh/v128/form-data@4.0.0/denonext/form-data.mjs": "48e84ac3b590bc364839367938d7e48ca37615a0c66e56dcc7356c3172ec7790",
"https://esm.sh/v128/html-encoding-sniffer@3.0.0/denonext/html-encoding-sniffer.mjs": "e5ca81202625bbf8802002b420cad9ddd1facb5ebd834c3a23bbac0a2362bbbc",
"https://esm.sh/v128/http-proxy-agent@5.0.0/denonext/http-proxy-agent.mjs": "b17dce342ba465c56d371c0ce5c0ee4ab56182bf878e465695c24380acebf29f",
"https://esm.sh/v128/https-proxy-agent@5.0.1/denonext/https-proxy-agent.mjs": "e250f991702f9f98f94e631d4d1a4a90020ddc667686c86390af1c50fa585f30",
"https://esm.sh/v128/iconv-lite@0.6.3/denonext/iconv-lite.mjs": "5840545a9ac1cea4684c800dfafca7200d8b0b7ea0f561b85c8445c12fc004d8",
"https://esm.sh/v128/is-potential-custom-element-name@1.0.1/denonext/is-potential-custom-element-name.mjs": "de2781ef99795b662f43c0840c3dcfdc303f9e60a75e66924370f902133469ed",
"https://esm.sh/v128/jsdom@22.1.0/denonext/jsdom.mjs": "de374334080b3d18ac730f117aceab674e50642285a3f94f3b7db5de04c6798b",
"https://esm.sh/v128/ms@2.1.2/denonext/ms.mjs": "aa4dc45ba72554c5011168f8910cc646c37af53cfff1a15a4decced838b8eb14",
"https://esm.sh/v128/node-gyp-build@4.6.0/denonext/node-gyp-build.mjs": "0de7d4d97a896a89efb9b3a31a0746f89f5c0e86d7f92badd0fddc3cd22ca40b",
"https://esm.sh/v128/nwsapi@2.2.6/denonext/nwsapi.mjs": "def5ad23c81fba69b806c0daf07c6b932a8069715e41404d27fe443cd46d75bd",
"https://esm.sh/v128/parse5@7.1.2/denonext/parse5.mjs": "9885dc571f470bdb484d8f0e1dbab42af18f406200ed5bcf7ebb9c183faa32ae",
"https://esm.sh/v128/psl@1.9.0/denonext/psl.mjs": "f40e9bcba5f6602eeb677f85f8e2b5c24b706bade58ced5b272044ebea609d9b",
"https://esm.sh/v128/querystringify@2.2.0/denonext/querystringify.mjs": "4f0f639f99ec4a7ddaffb886bd7e6f3fe4b088e1fcd60336dd10d447d2093ef6",
"https://esm.sh/v128/requires-port@1.0.0/denonext/requires-port.mjs": "c4f20b71539d08fc2662d75dfd79881fce985a0e4592268f18ac13bf53679efa",
"https://esm.sh/v128/rrweb-cssom@0.6.0/denonext/rrweb-cssom.mjs": "e2a14692d801a24edadd3fd4ecdf017382494258064134e33390a6bf1146950d",
"https://esm.sh/v128/safer-buffer@2.1.2/denonext/safer-buffer.mjs": "ce0e787812c668ba082ad5b75958490c714b6e05836bd5b6013d9f75727c006f",
"https://esm.sh/v128/saxes@6.0.0/denonext/saxes.mjs": "51f5349277fb67f314089490b16b9732fc85e85eae0c2ae6c5efcb965a57260f",
"https://esm.sh/v128/symbol-tree@3.2.4/denonext/symbol-tree.mjs": "67199d1e47bd6e5b7d2715dd04d25658061c95fc4464f7d200b6aab9e439b5f4",
"https://esm.sh/v128/tough-cookie@4.1.3/denonext/tough-cookie.mjs": "f9f24f23ada31799b530ed23b265f7cf5e17b70b8141687ee0c3e613ef55d766",
"https://esm.sh/v128/tr46@4.1.1/denonext/tr46.mjs": "2042307b546151186c6912a27265e444331e3d0bd6820c92a750aa67b3e5fdfc",
"https://esm.sh/v128/universalify@0.2.0/denonext/universalify.mjs": "6ebb2a9b372d4ae89f494d9b0044942dc76a5b5fb366ab3018e2bf0aeb8eae94",
"https://esm.sh/v128/url-parse@1.5.10/denonext/url-parse.mjs": "f04a3a5b4b960a62bab9c4dc9a4f8cfe3094ce24cc870c979b6cf9c3890c9a19",
"https://esm.sh/v128/utf-8-validate@6.0.3/denonext/utf-8-validate.mjs": "5f09fc9a7a6d2ed3fedf17fbfee3d906fb3acecfe18e9351de026b857e4b1d86",
"https://esm.sh/v128/w3c-xmlserializer@4.0.0/denonext/w3c-xmlserializer.mjs": "e23495a59a8c55bf3c1202a2e7ee4b1c8e25d1c6be14f5872d51ef8e50f6382f",
"https://esm.sh/v128/webidl-conversions@7.0.0/denonext/webidl-conversions.mjs": "04e3e6917179380727c6f65cd16a5a89836fb5a104fe5524c10a0a697f88d552",
"https://esm.sh/v128/whatwg-encoding@2.0.0/denonext/whatwg-encoding.mjs": "3483d367364a2aaf503a4fc0e56e6163c88c50d1aede2117c9ec264ea558e0a6",
"https://esm.sh/v128/whatwg-mimetype@3.0.0/denonext/whatwg-mimetype.mjs": "59446370c8333e5956416535e9773585645c22e7ea3420c8df5d78d1e9127c59",
"https://esm.sh/v128/whatwg-url@12.0.1/denonext/webidl2js-wrapper.js": "d9a6264ca46bf11a22eb24577dd549f2c5778fb0ec4783e69ff9a705a8aa3baa",
"https://esm.sh/v128/whatwg-url@12.0.1/denonext/whatwg-url.mjs": "efecc5954d62474ceb6a528a81e8b6503ab2473f0913fa2341ddcf381651bbf9",
"https://esm.sh/v128/ws@8.13.0/denonext/ws.mjs": "9891167e91ecedb8aa3e01398b564429aff5538ba58a9f27c680552b7ccab835",
"https://esm.sh/v128/xml-name-validator@4.0.0/denonext/xml-name-validator.mjs": "69af66c891312f304a8be720961ad5cce5c49c85a3bba03275b56a70dec7a21e",
"https://esm.sh/v128/xmlchars@2.2.0/denonext/xml/1.0/ed5.js": "60f8f018eb1d79d69a41324155b7d9f52f1058b37060b28acc1dfc49446e549d",
"https://esm.sh/v128/xmlchars@2.2.0/denonext/xml/1.1/ed2.js": "ba7d1fe5694f62469c4b293a1fadad332c637cbcfbc74147a296475c2ff8ad3d",
"https://esm.sh/v128/xmlchars@2.2.0/denonext/xmlns/1.0/ed3.js": "929d15ffc72d56c8909f87e7df8288f060bda0256622e8e95c24f0decb06adc7",
"https://git.barnulf.net/mb/debilus/raw/commit/fc701bec680dd73be29c72164f47ee87fac540c7/mod.ts": "bc9436bcf45f3204ebcd0b2559f53e5d38162195fc48ac6d435cf8ca38c77ae3",
"https://git.barnulf.net/mb/debilus/raw/commit/fc701bec680dd73be29c72164f47ee87fac540c7/src/entry.ts": "243a53b27e494c01e944331e2b71dfbe96ff906921aca8870603eb19bbce8235",
"https://git.barnulf.net/mb/debilus/raw/commit/fc701bec680dd73be29c72164f47ee87fac540c7/src/lib.ts": "76973ce240e2bcadaf13e43115d050dee70b92dc5cd7cd726cdd5d9adf4c1ad6",
"https://git.barnulf.net/mb/debilus/raw/commit/fc701bec680dd73be29c72164f47ee87fac540c7/src/store.ts": "ff6cdb0c045818d8e4ab6eb2bb304db8ac387ef05514af09e6c731bb65815a10",
"https://git.barnulf.net/mb/debilus/raw/commit/fc701bec680dd73be29c72164f47ee87fac540c7/src/typing.ts": "cb0d315461aab883ddd2396f5a5ce2a88615257b402d5a5cd06c10dc44ffa453"
}
}

8
dev.ts
View file

@ -1,8 +0,0 @@
#!/usr/bin/env -S deno run -A --watch=static/,routes/
import dev from "$fresh/dev.ts";
import config from "./fresh.config.ts";
import "$std/dotenv/load.ts";
await dev(import.meta.url, "./main.ts", config);

View file

@ -1,46 +0,0 @@
## Views
- Home
- Landing
- Feed (logged)
- Post
- new
- view
- likes
- add (logged)
- comment section
- new (logged)
- Account
- Login
- Register
## Models
- User
- name
- email
- password
- pfp_url
- like_set
* posts
* comments
- Post
- title
- content
- date
- like_count
+ author
* comments
- Comment
- date
+ post
+ author
---
- trend algo ?
- notifications ?

View file

@ -1,3 +0,0 @@
import { defineConfig } from "$fresh/server.ts";
export default defineConfig({});

View file

@ -1,31 +0,0 @@
// DO NOT EDIT. This file is generated by Fresh.
// This file SHOULD be checked into source version control.
// This file is automatically updated during development when running `dev.ts`.
import * as $_404 from "./routes/_404.tsx";
import * as $_app from "./routes/_app.tsx";
import * as $_middleware from "./routes/_middleware.ts";
import * as $api_joke from "./routes/api/joke.ts";
import * as $feur from "./routes/feur.tsx";
import * as $greet_name_ from "./routes/greet/[name].tsx";
import * as $index from "./routes/index.tsx";
import * as $Counter from "./islands/Counter.tsx";
import { type Manifest } from "$fresh/server.ts";
const manifest = {
routes: {
"./routes/_404.tsx": $_404,
"./routes/_app.tsx": $_app,
"./routes/_middleware.ts": $_middleware,
"./routes/api/joke.ts": $api_joke,
"./routes/feur.tsx": $feur,
"./routes/greet/[name].tsx": $greet_name_,
"./routes/index.tsx": $index,
},
islands: {
"./islands/Counter.tsx": $Counter,
},
baseUrl: import.meta.url,
} satisfies Manifest;
export default manifest;

View file

@ -1,16 +0,0 @@
import type { Signal } from "@preact/signals";
import { Button } from "../components/Button.tsx";
interface CounterProps {
count: Signal<number>;
}
export default function Counter(props: CounterProps) {
return (
<div class="flex gap-8 py-6">
<Button onClick={() => props.count.value -= 1}>-1</Button>
<p class="text-3xl tabular-nums">{props.count}</p>
<Button onClick={() => props.count.value += 1}>+1</Button>
</div>
);
}

24
lib/auth.ts Normal file
View file

@ -0,0 +1,24 @@
import { Context } from "https://deno.land/x/hono@v4.3.10/mod.ts";
import { db, User } from "./storage.ts";
import { FeurEnv } from "../main.ts";
import { BlankInput } from "https://deno.land/x/hono@v4.3.10/types.ts";
export async function login(login: string, password: string) {
for await (const user of db.all("user")) {
if (await user.get("username") !== login) continue;
if (await user.get("password") === password) return user;
return null;
}
return null;
}
export async function get_user(context: Context<FeurEnv, string, BlankInput>) {
const id = context.get("session").get("user");
if (typeof id !== "string") return null;
return await db.get("user", id);
}
export function set_user(context: Context<FeurEnv, string, BlankInput>, user: User | null) {
if (user === null) context.get("session").set("user", "");
else context.get("session").set("user", user.id);
}

View file

@ -1,9 +0,0 @@
import { z } from "https://deno.land/x/zod@v3.23.8/mod.ts";
export const UserModel = z.object({
id: z.string().uuid().describe("primary"),
name: z.string(),
email: z.string(),
password: z.string(),
pfp_url: z.string(),
});

36
lib/storage.ts Normal file
View file

@ -0,0 +1,36 @@
import {
EntryFor,
Schema,
Store,
} from "https://git.barnulf.net/mb/debilus/raw/commit/fc701bec680dd73be29c72164f47ee87fac540c7/mod.ts";
import { project_root } from "./utils.ts";
export const db = await Store.open(
new Schema({
user: {
username: "string",
password: "string",
upvoted_posts: ["many", "post"],
upvoted_comments: ["many", "comment"],
posts: ["many", "post"],
},
post: {
title: "string",
content: "string",
upvotes: "number",
author: ["one", "user"],
comments: ["many", "comment"],
},
comment: {
content: "string",
upvotes: "number",
author: ["one", "user"],
post: ["one", "post"],
},
}),
`${project_root()}/local/db.kv`,
);
export type User = EntryFor<typeof db, "user">;
export type Post = EntryFor<typeof db, "post">;
export type Comment = EntryFor<typeof db, "comment">;

View file

@ -1,57 +0,0 @@
import { createPentagon } from "https://deno.land/x/pentagon@v0.1.5/mod.ts";
import { project_root_dir } from "../utils.ts";
import { z } from "https://deno.land/x/zod@v3.21.4/mod.ts";
const kv = await Deno.openKv(project_root_dir() + "local/kv");
export type User = z.infer<typeof user_model>;
export const user_model = z.object({
id: z.string().uuid().describe("primary"),
name: z.string(),
email: z.string(),
password: z.string(),
pfp_url: z.string(),
like_set: z.set(z.string().uuid()),
});
export type Post = z.infer<typeof post_model>;
export const post_model = z.object({
id: z.string().uuid().describe("primary"),
title: z.string(),
content: z.string(),
date: z.number(),
like_count: z.number(),
author_id: z.string().uuid(),
});
export type Comment = z.infer<typeof comment_model>;
export const comment_model = z.object({
id: z.string().uuid().describe("primary"),
date: z.number(),
post_id: z.string().uuid(),
author_id: z.string().uuid(),
});
export const db = createPentagon(kv, {
users: {
schema: user_model,
relations: {
posts: ["posts", [post_model], "id", "author_id"],
comments: ["comments", [comment_model], "id", "author_id"],
},
},
posts: {
schema: post_model,
relations: {
author: ["users", user_model, "author_id", "id"],
comments: ["comments", [comment_model], "id", "post_id"],
},
},
comments: {
schema: comment_model,
relations: {
author: ["users", user_model, "author_id", "id"],
post: ["post", post_model, "post_id", "id"],
},
},
});

14
lib/utils.ts Normal file
View file

@ -0,0 +1,14 @@
import { dirname } from "https://deno.land/std@0.224.0/path/dirname.ts";
import { CSSProperties } from "https://deno.land/std@0.40.0/types/react.d.ts";
export function _css(prop: CSSProperties) {
return prop;
}
export function project_root() {
const this_url = new URL(import.meta.url);
const this_abs_path = Deno.realPathSync(this_url.pathname);
const lib_path = dirname(this_abs_path);
return dirname(lib_path);
}

2
local/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*
!/.gitignore

41
main.ts Normal file → Executable file
View file

@ -1,14 +1,33 @@
/// <reference no-default-lib="true" />
/// <reference lib="dom" />
/// <reference lib="dom.iterable" />
/// <reference lib="dom.asynciterable" />
/// <reference lib="deno.ns" />
/// <reference lib="deno.unstable" />
#!/bin/env -S deno run --allow-net --unstable-kv --watch --allow-read=./pages,./local,./lib,. --allow-write=./local
import "$std/dotenv/load.ts";
import { Hono } from "https://deno.land/x/hono@v4.3.10/mod.ts";
import { serveStatic } from "https://deno.land/x/hono@v4.3.10/middleware.ts";
import { CookieStore, Session, sessionMiddleware } from "https://deno.land/x/hono_sessions@v0.5.8/mod.ts";
import { start } from "$fresh/server.ts";
import manifest from "./fresh.gen.ts";
import config from "./fresh.config.ts";
export type FeurEnv = { Variables: { session: Session } };
const app = new Hono<FeurEnv>();
await start(manifest, config);
app.use("/static/*", serveStatic({ root: "./pages/" }));
const store = new CookieStore();
const encryptionKey = "FeurFeurFeurFeurFeurFeurFeurFeurFeurFeurFeurFeurFeurFeurFeurFeurFeurFeurFeurFeur";
app.use("*", sessionMiddleware({ store, encryptionKey }));
app.get("/", (c) => c.redirect("/home"));
import HomePage from "./pages/home.tsx";
app.get("/home", (c) => c.html(HomePage(c)));
import LoginPage from "./pages/login.tsx";
app.get("/login", (c) => c.html(LoginPage()));
import { login_route } from "./api/login.ts";
app.post("/api/login", (c) => login_route(c));
import { logout_route } from "./api/logout.ts";
app.get("/api/logout", (c) => logout_route(c));
import UserPage from "./pages/user.tsx";
app.get("/user", async (c) => await UserPage(c));
Deno.serve(app.fetch);

22
pages/components/base.tsx Normal file
View file

@ -0,0 +1,22 @@
/** @jsx jsx */
import { PropsWithChildren, jsx } from "https://deno.land/x/hono@v4.3.10/middleware.ts"
import { _css } from "../../lib/utils.ts";
export function BasePage({ name, children }: PropsWithChildren<{ name: string }>) {
return (
<html lang="en">
<head>
<meta charSet="UTF-8"/>
<title>{name} - Twifeur</title>
<link rel="stylesheet" href="/static/style.css" />
</head>
<body style={_css({
width: "100%",
minHeight: "100%",
})}>
{children}
</body>
</html>
)
}

View file

@ -0,0 +1,53 @@
/** @jsx jsx */
import { PropsWithChildren, jsx } from "https://deno.land/x/hono@v4.3.10/middleware.ts"
import { _css } from "../../lib/utils.ts";
import { User } from "../../lib/storage.ts";
//
export default function Heading({ username }: PropsWithChildren<{username: string | null}>) {
return (
<header style={_css({
width: "100vw",
display: "grid",
placeContent: "center",
backgroundColor: "#202020",
borderBottom: "1px solid #444",
boxShadow: "0px 0px 10px #0008",
position: "fixed",
})}>
<div style={_css({
width: "100vw",
maxWidth: "900px",
display: "flex",
})}>
<div style={_css({
borderLeft: '1px solid #444',
borderRight: '1px solid #444',
})}>
<h1 style={_css({
margin: "0.3rem",
marginLeft: "3rem",
marginRight: "3rem",
})}>TwiFeur</h1>
</div>
<MenuItem name="Content" location="/home"/>
<MenuItem name="About" location="/about"/>
<MenuItem name="User" location="/user"/>
{(username !== null) && <MenuItem name={`Logout (${username})`} location="/api/logout" />}
</div>
</header>
)
}
function MenuItem({ name, location }: { name: string, location: string }) {
return (
<a href={location} style={_css({
borderRight: '1px solid #444',
textDecoration: 'none',
color: 'unset'
})}>
<h3 style={_css({ margin: "0.7rem" })}>{name}</h3>
</a>
)
}

View file

@ -0,0 +1,13 @@
/** @jsx jsx */
import { jsx } from "https://deno.land/x/hono@v4.3.10/middleware.ts"
export default function Login() {
return (
<form action="/api/login" method="post">
<input type="text" name="login"/>
<input type="password" name="password"/>
<input type="submit" value="login" />
</form>
)
}

51
pages/components/main.tsx Normal file
View file

@ -0,0 +1,51 @@
/** @jsx jsx */
import { PropsWithChildren, jsx, Fragment } from "https://deno.land/x/hono@v4.3.10/middleware.ts"
import { _css } from "../../lib/utils.ts";
export default function Main({ children }: PropsWithChildren) {
return (
<Fragment>
<main style={_css({
width: "100vw",
display: "grid",
placeContent: "center",
})}>
<div style={_css({
width: "100vw",
maxWidth: "1000px",
backgroundColor: "#202020",
borderLeft: "1px solid #444",
borderRight: "1px solid #444",
boxShadow: "0px 0px 10px #0008",
paddingTop: "2rem"
})}>
<div style={_css({
minHeight: "100vh",
padding: "2rem"
})}>
{children}
</div>
</div>
</main>
<footer style={_css({
width: "100vw",
display: "grid",
placeContent: "center",
backgroundColor: "#202020",
borderTop: "1px solid #444",
boxShadow: "0px 0px 10px #0008",
position: "relative",
})}>
<div style={_css({
width: "100vw",
maxWidth: "900px",
display: "flex",
padding: "1rem"
})}>
Footer
</div>
</footer>
</Fragment>
)
}

12
pages/components/post.tsx Normal file
View file

@ -0,0 +1,12 @@
/** @jsx jsx */
import { PropsWithChildren, jsx } from "https://deno.land/x/hono@v4.3.10/middleware.ts"
import { _css } from "../../lib/utils.ts";
export function Post({ children, name }: PropsWithChildren<{ name: string }>) {
return (
<div>
</div>
)
}

20
pages/home.tsx Normal file
View file

@ -0,0 +1,20 @@
/** @jsx jsx */
import { jsx } from "https://deno.land/x/hono@v4.3.10/middleware.ts"
import { BasePage } from "./components/base.tsx"
import Main from "./components/main.tsx";
import Heading from "./components/heading.tsx";
import { get_user } from "../lib/auth.ts";
import { Context } from "https://deno.land/x/hono@v4.3.10/mod.ts";
import { FeurEnv } from "../main.ts";
import { BlankInput } from "https://deno.land/x/hono@v4.3.10/types.ts";
export default async function HomePage(context: Context<FeurEnv, string, BlankInput>) {
const user = await get_user(context);
return (
<BasePage name="Home">
<Heading username={await user?.get("username") ?? null}></Heading>
<Main>Main</Main>
</BasePage>
)
}

18
pages/login.tsx Normal file
View file

@ -0,0 +1,18 @@
/** @jsx jsx */
import { jsx } from "https://deno.land/x/hono@v4.3.10/middleware.ts"
import { BasePage } from "./components/base.tsx";
import Heading from "./components/heading.tsx";
import Main from "./components/main.tsx";
import Login from "./components/login.tsx";
export default function LoginPage() {
return (
<BasePage name="Login">
<Main>
<h1>Login</h1>
<Login/>
</Main>
</BasePage>
)
}

23
pages/static/style.css Normal file
View file

@ -0,0 +1,23 @@
html,
body,
header,
main,
footer {
margin: 0;
padding: 0;
}
h1 {
font-size: 1.5rem;
}
h3 {
font-size: 1rem;
}
body {
background-color: #181818;
color: #eee;
font-family: "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
overflow-x: hidden
}

24
pages/user.tsx Normal file
View file

@ -0,0 +1,24 @@
/** @jsx jsx */
import { jsx } from "https://deno.land/x/hono@v4.3.10/middleware.ts"
import { BasePage } from "./components/base.tsx";
import Heading from "./components/heading.tsx";
import Main from "./components/main.tsx";
import Login from "./components/login.tsx";
import { User } from "../lib/storage.ts";
import { Context } from "https://deno.land/x/hono@v4.3.10/mod.ts";
import { FeurEnv } from "../main.ts";
import { BlankInput } from "https://deno.land/x/hono@v4.3.10/types.ts";
import { get_user } from "../lib/auth.ts";
export default async function UserPage(context: Context<FeurEnv, string, BlankInput>) {
const user = await get_user(context);
if (user === null) return context.text("Must be logged.", 401);
return context.html(
<BasePage name="Login">
<Main>
<h1>Logged as {await user.get("username")}</h1>
</Main>
</BasePage>
)
}

View file

@ -1,27 +0,0 @@
import { Head } from "$fresh/runtime.ts";
export default function Error404() {
return (
<>
<Head>
<title>404 - Page not found</title>
</Head>
<div class="px-4 py-8 mx-auto bg-[#86efac]">
<div class="max-w-screen-md mx-auto flex flex-col items-center justify-center">
<img
class="my-6"
src="/logo.svg"
width="128"
height="128"
alt="the Fresh logo: a sliced lemon dripping with juice"
/>
<h1 class="text-4xl font-bold">404 - Page not found</h1>
<p class="my-4">
The page you were looking for doesn't exist.
</p>
<a href="/" class="underline">Go back home</a>
</div>
</div>
</>
);
}

View file

@ -1,16 +0,0 @@
import { type PageProps } from "$fresh/server.ts";
export default function App({ Component }: PageProps) {
return (
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>twifeur</title>
<link rel="stylesheet" href="/styles.css" />
</head>
<body>
<Component />
</body>
</html>
);
}

View file

@ -1,40 +0,0 @@
import { FreshContext } from "$fresh/server.ts";
import { deleteCookie, getCookies, setCookie } from "$std/http/cookie.ts";
import { auth, Token } from "../auth/auth.ts";
type State = {
user_id: string | null;
};
export async function handler(
req: Request,
ctx: FreshContext<State>,
) {
const cookies = getCookies(req.headers);
const token = get_session_token(cookies);
ctx.state.user_id = token?.user_id ?? null;
const resp = await ctx.next();
set_session_token(resp.headers, token);
return resp;
}
function get_session_token(cookies: Record<string, string>) {
const stored = cookies["auth_token"];
if (stored === undefined) return null;
const token = auth.tokens.get(stored);
if (token === null) return null;
return token;
}
function set_session_token(headers: Headers, token: Token | null) {
if (token === null) {
deleteCookie(headers, "auth_token");
return;
}
setCookie(headers, {
name: "auth_token",
value: token.raw,
});
}

View file

@ -1,21 +0,0 @@
import { FreshContext } from "$fresh/server.ts";
// Jokes courtesy of https://punsandoneliners.com/randomness/programmer-jokes/
const JOKES = [
"Why do Java developers often wear glasses? They can't C#.",
"A SQL query walks into a bar, goes up to two tables and says “can I join you?”",
"Wasn't hard to crack Forrest Gump's password. 1forrest1.",
"I love pressing the F5 key. It's refreshing.",
"Called IT support and a chap from Australia came to fix my network connection. I asked “Do you come from a LAN down under?”",
"There are 10 types of people in the world. Those who understand binary and those who don't.",
"Why are assembly programmers often wet? They work below C level.",
"My favourite computer based band is the Black IPs.",
"What programme do you use to predict the music tastes of former US presidential candidates? An Al Gore Rhythm.",
"An SEO expert walked into a bar, pub, inn, tavern, hostelry, public house.",
];
export const handler = (_req: Request, _ctx: FreshContext): Response => {
const randomIndex = Math.floor(Math.random() * JOKES.length);
const body = JOKES[randomIndex];
return new Response(body);
};

View file

@ -1,5 +0,0 @@
import { PageProps } from "$fresh/server.ts";
export default function Greet(props: PageProps) {
return <div>Hello {props.params.name}</div>;
}

View file

@ -1,25 +0,0 @@
import { useSignal } from "@preact/signals";
import Counter from "../islands/Counter.tsx";
export default function Home() {
const count = useSignal(3);
return (
<div class="px-4 py-8 mx-auto bg-[#86efac]">
<div class="max-w-screen-md mx-auto flex flex-col items-center justify-center">
<img
class="my-6"
src="/logo.svg"
width="128"
height="128"
alt="the Fresh logo: a sliced lemon dripping with juice"
/>
<h1 class="text-4xl font-bold">Welcome to Fresh</h1>
<p class="my-4">
Try updating this message in the
<code class="mx-2">./routes/index.tsx</code> file, and refresh.
</p>
<Counter count={count} />
</div>
</div>
);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

View file

@ -1,6 +0,0 @@
<svg width="40" height="40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M34.092 8.845C38.929 20.652 34.092 27 30 30.5c1 3.5-2.986 4.222-4.5 2.5-4.457 1.537-13.512 1.487-20-5C2 24.5 4.73 16.714 14 11.5c8-4.5 16-7 20.092-2.655Z" fill="#FFDB1E"/>
<path d="M14 11.5c6.848-4.497 15.025-6.38 18.368-3.47C37.5 12.5 21.5 22.612 15.5 25c-6.5 2.587-3 8.5-6.5 8.5-3 0-2.5-4-5.183-7.75C2.232 23.535 6.16 16.648 14 11.5Z" fill="#fff" stroke="#FFDB1E"/>
<path d="M28.535 8.772c4.645 1.25-.365 5.695-4.303 8.536-3.732 2.692-6.606 4.21-7.923 4.83-.366.173-1.617-2.252-1.617-1 0 .417-.7 2.238-.934 2.326-1.365.512-4.223 1.29-5.835 1.29-3.491 0-1.923-4.754 3.014-9.122.892-.789 1.478-.645 2.283-.645-.537-.773-.534-.917.403-1.546C17.79 10.64 23 8.77 25.212 8.42c.366.014.82.35.82.629.41-.14 2.095-.388 2.503-.278Z" fill="#FFE600"/>
<path d="M14.297 16.49c.985-.747 1.644-1.01 2.099-2.526.566.121.841-.08 1.29-.701.324.466 1.657.608 2.453.701-.715.451-1.057.852-1.452 2.106-1.464-.611-3.167-.302-4.39.42Z" fill="#fff"/>
</svg>

Before

Width:  |  Height:  |  Size: 1 KiB

View file

@ -1,129 +0,0 @@
*,
*::before,
*::after {
box-sizing: border-box;
}
* {
margin: 0;
}
button {
color: inherit;
}
button, [role="button"] {
cursor: pointer;
}
code {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
"Liberation Mono", "Courier New", monospace;
font-size: 1em;
}
img,
svg {
display: block;
}
img,
video {
max-width: 100%;
height: auto;
}
html {
line-height: 1.5;
-webkit-text-size-adjust: 100%;
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
"Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}
.transition-colors {
transition-property: background-color, border-color, color, fill, stroke;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.my-6 {
margin-bottom: 1.5rem;
margin-top: 1.5rem;
}
.text-4xl {
font-size: 2.25rem;
line-height: 2.5rem;
}
.mx-2 {
margin-left: 0.5rem;
margin-right: 0.5rem;
}
.my-4 {
margin-bottom: 1rem;
margin-top: 1rem;
}
.mx-auto {
margin-left: auto;
margin-right: auto;
}
.px-4 {
padding-left: 1rem;
padding-right: 1rem;
}
.py-8 {
padding-bottom: 2rem;
padding-top: 2rem;
}
.bg-\[\#86efac\] {
background-color: #86efac;
}
.text-3xl {
font-size: 1.875rem;
line-height: 2.25rem;
}
.py-6 {
padding-bottom: 1.5rem;
padding-top: 1.5rem;
}
.px-2 {
padding-left: 0.5rem;
padding-right: 0.5rem;
}
.py-1 {
padding-bottom: 0.25rem;
padding-top: 0.25rem;
}
.border-gray-500 {
border-color: #6b7280;
}
.bg-white {
background-color: #fff;
}
.flex {
display: flex;
}
.gap-8 {
grid-gap: 2rem;
gap: 2rem;
}
.font-bold {
font-weight: 700;
}
.max-w-screen-md {
max-width: 768px;
}
.flex-col {
flex-direction: column;
}
.items-center {
align-items: center;
}
.justify-center {
justify-content: center;
}
.border-2 {
border-width: 2px;
}
.rounded {
border-radius: 0.25rem;
}
.hover\:bg-gray-200:hover {
background-color: #e5e7eb;
}
.tabular-nums {
font-variant-numeric: tabular-nums;
}

View file

@ -1,7 +0,0 @@
import { dirname } from "$std/path/dirname.ts";
export function project_root_dir() {
const this_url = new URL(import.meta.url);
const this_dir = dirname(this_url.pathname);
return this_dir + "/";
}