refactoring everything before moving on
This commit is contained in:
5
deno.lock
generated
5
deno.lock
generated
@ -673,6 +673,9 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"redirects": {
|
||||
"https://deno.land/x/sleep/mod.ts": "https://deno.land/x/sleep@v1.3.0/mod.ts"
|
||||
},
|
||||
"remote": {
|
||||
"https://deno.land/std@0.203.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee",
|
||||
"https://deno.land/std@0.203.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56",
|
||||
@ -686,6 +689,8 @@
|
||||
"https://deno.land/std@0.203.0/async/pool.ts": "47c1841cfa9c036144943d11747ddd44064f5baf8cb7ece25473ba873c6aceb0",
|
||||
"https://deno.land/std@0.203.0/async/retry.ts": "296fb9c323e1325a69bee14ba947e7da7409a8dd9dd646d70cb51ea0d301f24e",
|
||||
"https://deno.land/std@0.203.0/async/tee.ts": "47e42d35f622650b02234d43803d0383a89eb4387e1b83b5a40106d18ae36757",
|
||||
"https://deno.land/x/sleep@v1.3.0/mod.ts": "e9955ecd3228a000e29d46726cd6ab14b65cf83904e9b365f3a8d64ec61c1af3",
|
||||
"https://deno.land/x/sleep@v1.3.0/sleep.ts": "b6abaca093b094b0c2bba94f287b19a60946a8d15764d168f83fcf555f5bb59e",
|
||||
"https://wilsonl.in/minify-html/deno/0.15.0/index.js": "8e7ee5067ca84fb5d5a1f33118cac4998de0b7d80b3f56cc5c6728b84e6bfb70"
|
||||
},
|
||||
"workspace": {
|
||||
|
||||
@ -5,7 +5,12 @@ const schema = {
|
||||
req: z.obj({
|
||||
password: z.string(),
|
||||
}),
|
||||
res: z.result(z.string(), z.any()),
|
||||
res: z.result(
|
||||
z.obj({
|
||||
isMatch: z.boolean(),
|
||||
}),
|
||||
z.any(),
|
||||
),
|
||||
};
|
||||
|
||||
export const loginApi = new Api("/login", "POST", schema);
|
||||
|
||||
@ -5,9 +5,8 @@ import rateLimitMiddleware from "@src/middleware/rateLimiter.ts";
|
||||
import authMiddleware from "@src/middleware/auth.ts";
|
||||
import loggerMiddleware from "@src/middleware/logger.ts";
|
||||
import { SchemaValidationError, z } from "@shared/utils/validator.ts";
|
||||
import { Api } from "@src/lib/apiValidator.ts";
|
||||
import { loginApi } from "./api.ts";
|
||||
import { err, getMessageFromError, ok } from "@shared/utils/result.ts";
|
||||
import { err, ok } from "@shared/utils/result.ts";
|
||||
import admin from "@src/lib/admin.ts";
|
||||
import {
|
||||
FailedToParseRequestAsJSON,
|
||||
@ -49,15 +48,17 @@ router
|
||||
return c.html(eta.render("./index.html", {}));
|
||||
})
|
||||
.get(["/login", "/login.html"], (c) => {
|
||||
return c.html(eta.render("./login.html", {}));
|
||||
const alreadyLoggedIn = c.cookies.get("token").map((token) =>
|
||||
admin.sessions.verifyToken(token)
|
||||
)
|
||||
.toBoolean();
|
||||
|
||||
console.log(alreadyLoggedIn);
|
||||
|
||||
return c.html(eta.render("./login.html", { alreadyLoggedIn }));
|
||||
});
|
||||
|
||||
const schema = {
|
||||
req: z.obj({
|
||||
password: z.string().max(1024),
|
||||
}),
|
||||
res: z.result(z.void(), z.string()),
|
||||
};
|
||||
admin.setPassword("Vermont5481");
|
||||
|
||||
router.api(loginApi, async (c) => {
|
||||
const r = await c
|
||||
@ -68,7 +69,7 @@ router.api(loginApi, async (c) => {
|
||||
|
||||
if (r.isErr()) {
|
||||
if (r.error.type === "AdminPasswordNotSetError") {
|
||||
return c.json(
|
||||
return c.json400(
|
||||
err({
|
||||
type: r.error.type,
|
||||
msg: r.error.message,
|
||||
@ -78,6 +79,9 @@ router.api(loginApi, async (c) => {
|
||||
return handleCommonErrors(c, r.error);
|
||||
}
|
||||
|
||||
const isMatch = r.value;
|
||||
|
||||
if (isMatch) {
|
||||
return admin.sessions.create()
|
||||
.map(({ value, expires }) => {
|
||||
c.cookies.set({
|
||||
@ -85,11 +89,16 @@ router.api(loginApi, async (c) => {
|
||||
value,
|
||||
expires,
|
||||
});
|
||||
return ok();
|
||||
return ok({ isMatch: true });
|
||||
}).match(
|
||||
() => c.json(ok()),
|
||||
(v) => c.json(v),
|
||||
(e) => handleCommonErrors(c, e),
|
||||
);
|
||||
} else {
|
||||
return c.json(ok({
|
||||
isMatch: false,
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
function handleCommonErrors(
|
||||
@ -106,11 +115,18 @@ function handleCommonErrors(
|
||||
{ status: 500 },
|
||||
);
|
||||
case "FailedToParseRequestAsJSON":
|
||||
case "SchemaValiationError":
|
||||
return c.json(
|
||||
err(error),
|
||||
{ status: 400 },
|
||||
);
|
||||
case "SchemaValiationError":
|
||||
return c.json(
|
||||
err({
|
||||
type: "ValidationError",
|
||||
msg: error.msg,
|
||||
}),
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
0
server/public/js/index.js
Normal file
0
server/public/js/index.js
Normal file
@ -1 +1 @@
|
||||
import{loginApi as o}from"./shared.bundle.js";const s=document.getElementById("loginForm"),m=document.getElementById("passwordInput");s.addEventListener("submit",async e=>{e.preventDefault();const t=m.value,n=await o.makeRequest({password:t},{});console.log(n)});
|
||||
import{loginApi as s}from"./shared.bundle.js";const o=document.getElementById("loginForm"),i=document.getElementById("passwordInput"),t=document.getElementById("errDiv");o.addEventListener("submit",async n=>{n.preventDefault();const r=i.value,e=(await s.makeRequest({password:r},{})).flatten();e.isErr()?e.error.type==="RequestValidationError"&&(t.innerText=e.error.msg):e.value.isMatch?window.location.href="/":t.innerText="invalid password"});
|
||||
|
||||
File diff suppressed because one or more lines are too long
1
server/src/js/index.ts
Normal file
1
server/src/js/index.ts
Normal file
@ -0,0 +1 @@
|
||||
|
||||
@ -6,13 +6,24 @@ const form = document.getElementById("loginForm") as HTMLFormElement;
|
||||
const passwordInput = document.getElementById(
|
||||
"passwordInput",
|
||||
) as HTMLInputElement;
|
||||
const errDiv = document.getElementById("errDiv") as HTMLDivElement;
|
||||
|
||||
form.addEventListener("submit", async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const password = passwordInput.value;
|
||||
|
||||
const res = await loginApi.makeRequest({ password }, {});
|
||||
const res = (await loginApi.makeRequest({ password }, {})).flatten();
|
||||
|
||||
console.log(res);
|
||||
if (res.isErr()) {
|
||||
if (res.error.type === "RequestValidationError") {
|
||||
errDiv.innerText = res.error.msg;
|
||||
}
|
||||
} else {
|
||||
if (!res.value.isMatch) {
|
||||
errDiv.innerText = "invalid password";
|
||||
} else {
|
||||
window.location.href = "/";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,15 +1,9 @@
|
||||
import { Result } from "@shared/utils/result.ts";
|
||||
import {
|
||||
InferSchemaType,
|
||||
ResultSchema,
|
||||
Schema,
|
||||
z,
|
||||
} from "@shared/utils/validator.ts";
|
||||
import { InferSchemaType, Schema } from "@shared/utils/validator.ts";
|
||||
import {
|
||||
RequestValidationError,
|
||||
ResponseValidationError,
|
||||
} from "@src/lib/errors.ts";
|
||||
import { errAsync, okAsync, ResultAsync } from "@shared/utils/resultasync.ts";
|
||||
import { ResultAsync } from "@shared/utils/resultasync.ts";
|
||||
|
||||
export type ExtractRouteParams<T extends string> = T extends string
|
||||
? T extends `${infer _Start}:${infer Param}/${infer Rest}`
|
||||
@ -85,4 +79,16 @@ export class Api<
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
public makeSafeRequest(
|
||||
reqBody: InferSchemaType<ReqSchema>,
|
||||
params: { [K in ExtractRouteParams<Path>]: string },
|
||||
): ResultAsync<InferSchemaType<ResSchema>, ResponseValidationError> {
|
||||
return this.makeRequest(reqBody, params).mapErr((e) => {
|
||||
if (e.type === "RequestValidationError") {
|
||||
throw "Failed to validate request";
|
||||
}
|
||||
return e;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,19 +3,13 @@ import { type ExtractRouteParams } from "@lib/router.ts";
|
||||
import { fromNullableVal, none, Option, some } from "@shared/utils/option.ts";
|
||||
import { deleteCookie, getCookies, setCookie } from "@std/http/cookie";
|
||||
import { type Cookie } from "@std/http/cookie";
|
||||
import {
|
||||
Err,
|
||||
getMessageFromError,
|
||||
Ok,
|
||||
type Result,
|
||||
ResultFromJSON,
|
||||
} from "@shared/utils/result.ts";
|
||||
import { getMessageFromError } from "@shared/utils/result.ts";
|
||||
import {
|
||||
InferSchemaType,
|
||||
Schema,
|
||||
SchemaValidationError,
|
||||
} from "@shared/utils/validator.ts";
|
||||
import { errAsync, ResultAsync } from "@shared/utils/resultasync.ts";
|
||||
import { ResultAsync } from "@shared/utils/resultasync.ts";
|
||||
import log from "@shared/utils/logger.ts";
|
||||
import { FailedToParseRequestAsJSON } from "@src/lib/errors.ts";
|
||||
|
||||
@ -141,7 +135,10 @@ export class Context<
|
||||
return none;
|
||||
}
|
||||
|
||||
public json(body?: object | string, init: ResponseInit = {}): Response {
|
||||
public json(
|
||||
body?: ResSchema extends Schema<infer T> ? T : object | string,
|
||||
init: ResponseInit = {},
|
||||
): Response {
|
||||
const headers = mergeHeaders(
|
||||
SECURITY_HEADERS,
|
||||
this._responseHeaders,
|
||||
|
||||
83
server/src/lib/devices.ts
Normal file
83
server/src/lib/devices.ts
Normal file
@ -0,0 +1,83 @@
|
||||
import usbip from "@src/lib/usbip.ts";
|
||||
import {
|
||||
type CommandExecutionError,
|
||||
DeviceDetailed,
|
||||
type UsbipUnknownError,
|
||||
} from "@shared/utils/usbip.ts";
|
||||
import { none } from "@shared/utils/option.ts";
|
||||
import { InferSchemaType, z } from "@shared/utils/validator.ts";
|
||||
import log from "@shared/utils/logger.ts";
|
||||
import { errAsync, ResultAsync } from "@shared/utils/resultasync.ts";
|
||||
|
||||
class Devices {
|
||||
private readonly devices: Map<string, Device> = new Map();
|
||||
|
||||
updateDevices(): ResultAsync<
|
||||
void,
|
||||
CommandExecutionError | UsbipUnknownError
|
||||
> {
|
||||
return usbip.getDevicesDetailed().mapErr((e) => {
|
||||
log.error("Failed to update devices!");
|
||||
return e;
|
||||
}).map((d) => d.unwrapOr([])).map(
|
||||
(devices) => {
|
||||
const current = new Set(devices.map((d) => d.busid));
|
||||
const old = new Set(this.devices.keys());
|
||||
|
||||
const connected = current.difference(old);
|
||||
const disconnected = old.difference(current);
|
||||
|
||||
for (const device of devices) {
|
||||
if (connected.has(device.busid)) {
|
||||
this.devices.set(
|
||||
device.busid,
|
||||
this.deviceFromDetailed(device),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (const device of disconnected) {
|
||||
this.devices.delete(device);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
deviceFromDetailed(d: DeviceDetailed): Device {
|
||||
return {
|
||||
busid: d.busid,
|
||||
usbid: d.usbid,
|
||||
vendor: d.vendor,
|
||||
name: d.name,
|
||||
displayName: none,
|
||||
description: none,
|
||||
connectedAt: new Date(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const deviceSchema = z.obj({
|
||||
busid: z.string(),
|
||||
usbid: z.option(z.string()),
|
||||
vendor: z.option(z.string()),
|
||||
name: z.option(z.string()),
|
||||
displayName: z.option(z.string()),
|
||||
description: z.option(z.string()),
|
||||
connectedAt: z.date(),
|
||||
});
|
||||
|
||||
const test = new Devices();
|
||||
|
||||
await test.updateDevices();
|
||||
|
||||
console.log(test);
|
||||
|
||||
import { sleep } from "https://deno.land/x/sleep/mod.ts";
|
||||
|
||||
await sleep(5);
|
||||
|
||||
await test.updateDevices();
|
||||
|
||||
console.log(test);
|
||||
|
||||
export type Device = InferSchemaType<typeof deviceSchema>;
|
||||
@ -1,9 +1,16 @@
|
||||
import { fromNullableVal, none, Option, some } from "@shared/utils/option.ts";
|
||||
|
||||
const DEFAULT_WILDCARD_SYMBOL = "*";
|
||||
const DEFAULT_WILDCARD = "*";
|
||||
const DEFAULT_PARAM_PREFIX = ":";
|
||||
const DEFAULT_PATH_SEPARATOR = "/";
|
||||
|
||||
export type Params = Record<string, string>;
|
||||
|
||||
interface RouteMatch<T> {
|
||||
value: T;
|
||||
params: Params;
|
||||
}
|
||||
|
||||
interface Node<T> {
|
||||
handler: Option<T>;
|
||||
paramNames: string[];
|
||||
@ -29,52 +36,52 @@ class StaticNode<T> implements Node<T> {
|
||||
this.handler = fromNullableVal(handler);
|
||||
}
|
||||
|
||||
addStaticChild(segment: string, handler?: T): StaticNode<T> {
|
||||
private addStaticChild(segment: string, handler?: T): StaticNode<T> {
|
||||
const child = new StaticNode(handler);
|
||||
this.staticChildren.set(segment, child);
|
||||
return child;
|
||||
}
|
||||
|
||||
setDynamicChild(handler?: T): DynamicNode<T> {
|
||||
private createDynamicChild(handler?: T): DynamicNode<T> {
|
||||
const child = new DynamicNode(handler);
|
||||
this.dynamicChild = some(child);
|
||||
return child;
|
||||
}
|
||||
|
||||
setWildcardNode(handler?: T): WildcardNode<T> {
|
||||
private createWildcardNode(handler?: T): WildcardNode<T> {
|
||||
const child = new WildcardNode(handler);
|
||||
this.wildcardChild = some(child);
|
||||
return child;
|
||||
}
|
||||
|
||||
addChild(
|
||||
public addChild(
|
||||
segment: string,
|
||||
wildcardSymbol: string,
|
||||
paramPrefixSymbol: string,
|
||||
handler?: T,
|
||||
): Node<T> {
|
||||
if (segment === wildcardSymbol) {
|
||||
return this.setWildcardNode(handler);
|
||||
return this.createWildcardNode(handler);
|
||||
}
|
||||
if (segment.startsWith(paramPrefixSymbol)) {
|
||||
return this.setDynamicChild(handler);
|
||||
return this.createDynamicChild(handler);
|
||||
}
|
||||
return this.addStaticChild(segment, handler);
|
||||
}
|
||||
|
||||
getStaticChild(segment: string): Option<StaticNode<T>> {
|
||||
private getStaticChild(segment: string): Option<StaticNode<T>> {
|
||||
return fromNullableVal(this.staticChildren.get(segment));
|
||||
}
|
||||
|
||||
getDynamicChild(): Option<DynamicNode<T>> {
|
||||
public getDynamicChild(): Option<DynamicNode<T>> {
|
||||
return this.dynamicChild;
|
||||
}
|
||||
|
||||
getWildcardChild(): Option<WildcardNode<T>> {
|
||||
public getWildcardChild(): Option<WildcardNode<T>> {
|
||||
return this.wildcardChild;
|
||||
}
|
||||
|
||||
getChild(segment: string): Option<Node<T>> {
|
||||
public getChild(segment: string): Option<Node<T>> {
|
||||
return this.getStaticChild(segment)
|
||||
.orElse(() => this.getWildcardChild())
|
||||
.orElse(() => this.getDynamicChild());
|
||||
@ -89,7 +96,6 @@ class StaticNode<T> implements Node<T> {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: get rid of fixed param name
|
||||
class DynamicNode<T> extends StaticNode<T> implements Node<T> {
|
||||
constructor(
|
||||
handler?: T,
|
||||
@ -112,7 +118,7 @@ class WildcardNode<T> implements Node<T> {
|
||||
|
||||
// Override to prevent adding children to a wildcard node
|
||||
public addChild(): Node<T> {
|
||||
throw new Error("Cannot add child to a WildcardNode.");
|
||||
throw new Error("Cannot add child to a wildcard (catch-all) node.");
|
||||
}
|
||||
|
||||
public getChild(): Option<Node<T>> {
|
||||
@ -128,16 +134,13 @@ class WildcardNode<T> implements Node<T> {
|
||||
}
|
||||
}
|
||||
|
||||
// Using Node<T> as the unified type for tree nodes.
|
||||
type TreeNode<T> = Node<T>;
|
||||
|
||||
export class RouterTree<T> {
|
||||
public readonly root: StaticNode<T>;
|
||||
|
||||
constructor(
|
||||
handler?: T,
|
||||
private readonly wildcardSymbol: string = DEFAULT_WILDCARD_SYMBOL,
|
||||
private readonly paramPrefixSymbol: string = DEFAULT_PARAM_PREFIX,
|
||||
private readonly wildcardSymbol: string = DEFAULT_WILDCARD,
|
||||
private readonly paramPrefix: string = DEFAULT_PARAM_PREFIX,
|
||||
private readonly pathSeparator: string = DEFAULT_PATH_SEPARATOR,
|
||||
) {
|
||||
this.root = new StaticNode(handler);
|
||||
@ -146,7 +149,7 @@ export class RouterTree<T> {
|
||||
public add(path: string, handler: T): void {
|
||||
const segments = this.splitPath(path);
|
||||
const paramNames: string[] = this.extractParams(segments);
|
||||
let current: TreeNode<T> = this.root;
|
||||
let current: Node<T> = this.root;
|
||||
|
||||
for (const segment of segments) {
|
||||
current = current
|
||||
@ -155,7 +158,7 @@ export class RouterTree<T> {
|
||||
current.addChild(
|
||||
segment,
|
||||
this.wildcardSymbol,
|
||||
this.paramPrefixSymbol,
|
||||
this.paramPrefix,
|
||||
)
|
||||
);
|
||||
|
||||
@ -174,7 +177,7 @@ export class RouterTree<T> {
|
||||
public find(path: string): Option<RouteMatch<T>> {
|
||||
const segments = this.splitPath(path);
|
||||
const paramValues: string[] = [];
|
||||
let current: TreeNode<T> = this.root;
|
||||
let current: Node<T> = this.root;
|
||||
let i = 0;
|
||||
|
||||
for (; i < segments.length; i++) {
|
||||
@ -209,7 +212,7 @@ export class RouterTree<T> {
|
||||
|
||||
public getHandler(path: string): Option<T> {
|
||||
const segments = this.splitPath(path);
|
||||
let current: TreeNode<T> = this.root;
|
||||
let current: Node<T> = this.root;
|
||||
|
||||
for (const segment of segments) {
|
||||
if (current.isWildcardNode()) break;
|
||||
@ -224,6 +227,16 @@ export class RouterTree<T> {
|
||||
return current.handler;
|
||||
}
|
||||
|
||||
private traverseOrCreate(segments: string[]): Node<T> {
|
||||
let node: Node<T> = this.root;
|
||||
for (const segment of segments) {
|
||||
if (node.isWildcardNode()) break;
|
||||
node = node.getChild(segment).unwrapOrElse(() =>
|
||||
node.addChild(segment, this.wildcardSymbol, this.paramPrefix)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private splitPath(path: string): string[] {
|
||||
const trimmed = path.trim().replace(/^\/+/, "").replace(/\/+$/, "");
|
||||
return trimmed ? trimmed.split(this.pathSeparator) : [];
|
||||
@ -231,18 +244,11 @@ export class RouterTree<T> {
|
||||
|
||||
public extractParams(segments: string[]): string[] {
|
||||
return segments.filter((segment) =>
|
||||
segment.startsWith(this.paramPrefixSymbol)
|
||||
segment.startsWith(this.paramPrefix)
|
||||
).map((segment) => this.stripParamPrefix(segment));
|
||||
}
|
||||
|
||||
public stripParamPrefix(segment: string): string {
|
||||
return segment.slice(this.paramPrefixSymbol.length);
|
||||
return segment.slice(this.paramPrefix.length);
|
||||
}
|
||||
}
|
||||
|
||||
export type Params = Record<string, string>;
|
||||
|
||||
interface RouteMatch<T> {
|
||||
value: T;
|
||||
params: Params;
|
||||
}
|
||||
|
||||
@ -17,10 +17,6 @@ const authMiddleware: Middleware = async (c, next) => {
|
||||
return c.redirect("/login");
|
||||
}
|
||||
|
||||
if (path === LOGIN_PATH && isValid) {
|
||||
return c.redirect("");
|
||||
}
|
||||
|
||||
await next();
|
||||
}
|
||||
};
|
||||
|
||||
@ -4,7 +4,6 @@ const loggerMiddleware: Middleware = async (c, next) => {
|
||||
console.log("", c.req.method, c.path);
|
||||
await next();
|
||||
console.log("", c.res.status, "\n");
|
||||
console.log(await c.res.json());
|
||||
};
|
||||
|
||||
export default loggerMiddleware;
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
<% layout("./layouts/layout.html") %>
|
||||
|
||||
this is an index.html
|
||||
<script src="/public/js/index.js" defer></script>
|
||||
|
||||
@ -1,7 +1,17 @@
|
||||
<% layout("./layouts/basic.html") %>
|
||||
<% if (!it.alreadyLoggedIn) { %>
|
||||
<main>
|
||||
<form id=loginForm method=POST>
|
||||
<p>password</p><input id=passwordInput name=password type=password><input value="sign in" type=submit>
|
||||
<div id="errDiv"></div>
|
||||
</form>
|
||||
</main>
|
||||
<script defer src=/public/js/login.js type=module></script>
|
||||
<% } else { %>
|
||||
<main>
|
||||
You are already logged in!
|
||||
</main>
|
||||
<script>
|
||||
setTimeout(() => {window.location.href = "/"}, 1500)
|
||||
</script>
|
||||
<% } %>
|
||||
|
||||
BIN
server/test.db
BIN
server/test.db
Binary file not shown.
@ -1 +1,6 @@
|
||||
<% layout("./layouts/layout.html") %> this is an index.html
|
||||
<% layout("./layouts/layout.html") %>
|
||||
|
||||
devices:
|
||||
<div id="Devices"></div>
|
||||
|
||||
<script defer src=/public/js/index.js></script>
|
||||
|
||||
@ -1 +1 @@
|
||||
<% layout("./layouts/basic.html") %> <main><form id=loginForm method=POST><p>password</p><input id=passwordInput name=password type=password><input value="sign in" type=submit></form></main><script defer src=/public/js/login.js type=module></script>
|
||||
<% layout("./layouts/basic.html") %> <% if (!it.alreadyLoggedIn) { %> <main><form id=loginForm method=POST><p>password</p><input id=passwordInput name=password type=password><input value="sign in" type=submit><div id=errDiv></div></form></main><script defer src=/public/js/login.js type=module></script> <% } else { %> <main>You are already logged in!</main><script>setTimeout(() => {window.location.href = "/"}, 1500)</script> <% } %>
|
||||
@ -371,7 +371,7 @@ export function flattenResult<R extends Result<any, any>>(
|
||||
currentResult = currentResult.value;
|
||||
}
|
||||
|
||||
return currentResult as FlattenResult<R>;
|
||||
return ok(currentResult) as FlattenResult<R>;
|
||||
}
|
||||
|
||||
export type UnwrapOption<T> = T extends Option<infer V> ? V : T;
|
||||
|
||||
@ -254,13 +254,12 @@ export function errAsync<E, T = never>(err: E): ResultAsync<T, E> {
|
||||
return new ResultAsync(Promise.resolve(new Err<T, E>(err)));
|
||||
}
|
||||
|
||||
export type FlattenResultAsync<R> = R extends ResultAsync<infer T, infer E>
|
||||
? T extends ResultAsync<any, any>
|
||||
? FlattenResultAsync<T> extends ResultAsync<infer V, infer innerE>
|
||||
? ResultAsync<V, E | innerE>
|
||||
: never
|
||||
type FlattenResultAsync<R> = R extends
|
||||
ResultAsync<infer Inner, infer OuterError>
|
||||
? Inner extends ResultAsync<infer T, infer InnerError>
|
||||
? ResultAsync<T, OuterError | InnerError>
|
||||
: R
|
||||
: never;
|
||||
: R;
|
||||
|
||||
type UnwrapPromise<Pr extends Promise<unknown>> = Pr extends Promise<infer U>
|
||||
? U
|
||||
|
||||
@ -9,42 +9,42 @@ import {
|
||||
some,
|
||||
} from "@shared/utils/option.ts";
|
||||
|
||||
class CommandExecutionError extends Error {
|
||||
code = "CommandExecutionError";
|
||||
export class CommandExecutionError extends Error {
|
||||
type = "CommandExecutionError";
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
class DeviceDoesNotExistError extends Error {
|
||||
code = "DeviceDoesNotExist";
|
||||
export class DeviceDoesNotExistError extends Error {
|
||||
type = "DeviceDoesNotExist";
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
class DeviceAlreadyBoundError extends Error {
|
||||
code = "DeviceAlreadyBound";
|
||||
export class DeviceAlreadyBoundError extends Error {
|
||||
type = "DeviceAlreadyBound";
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
class DeviceNotBound extends Error {
|
||||
code = "DeviceNotBound";
|
||||
export class DeviceNotBound extends Error {
|
||||
type = "DeviceNotBound";
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
class UsbipUknownError extends Error {
|
||||
code = "UsbipUknownError";
|
||||
export class UsbipUnknownError extends Error {
|
||||
type = "UsbipUknownError";
|
||||
constructor(msg: string) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
type UsbipCommonError = DeviceDoesNotExistError | UsbipUknownError;
|
||||
type UsbipCommonError = DeviceDoesNotExistError | UsbipUnknownError;
|
||||
|
||||
class UsbipManager {
|
||||
private readonly listDeatiledCmd = new Deno.Command("usbip", {
|
||||
@ -84,7 +84,7 @@ class UsbipManager {
|
||||
return new DeviceDoesNotExistError(stderr);
|
||||
}
|
||||
|
||||
return new UsbipUknownError(stderr);
|
||||
return new UsbipUnknownError(stderr);
|
||||
}
|
||||
|
||||
private parseDetailedList(stdout: string): Option<DeviceDetailed[]> {
|
||||
@ -140,7 +140,7 @@ class UsbipManager {
|
||||
|
||||
public getDevicesDetailed(): ResultAsync<
|
||||
Option<DeviceDetailed[]>,
|
||||
CommandExecutionError | UsbipUknownError
|
||||
CommandExecutionError | UsbipUnknownError
|
||||
> {
|
||||
return this.executeCommand(this.listDeatiledCmd).andThen(
|
||||
({ stdout, stderr, success }) => {
|
||||
@ -153,7 +153,7 @@ class UsbipManager {
|
||||
return ok(this.parseDetailedList(stdout));
|
||||
}
|
||||
|
||||
return err(new UsbipUknownError(stderr));
|
||||
return err(new UsbipUnknownError(stderr));
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -193,7 +193,7 @@ class UsbipManager {
|
||||
|
||||
public getDevices(): ResultAsync<
|
||||
Option<Device[]>,
|
||||
CommandExecutionError | UsbipUknownError
|
||||
CommandExecutionError | UsbipUnknownError
|
||||
> {
|
||||
return this.executeCommand(this.listParsableCmd).andThenAsync(
|
||||
({ stdout, stderr, success }) => {
|
||||
@ -205,7 +205,7 @@ class UsbipManager {
|
||||
}
|
||||
return okAsync(this.parseParsableList(stdout));
|
||||
}
|
||||
return errAsync(new UsbipUknownError(stderr));
|
||||
return errAsync(new UsbipUnknownError(stderr));
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -268,7 +268,7 @@ class CommandOutput {
|
||||
}
|
||||
}
|
||||
|
||||
interface DeviceDetailed {
|
||||
export interface DeviceDetailed {
|
||||
busid: string;
|
||||
usbid: Option<string>;
|
||||
vendor: Option<string>;
|
||||
|
||||
2
vendor/deno.land/x/sleep@v1.3.0/mod.ts
vendored
Normal file
2
vendor/deno.land/x/sleep@v1.3.0/mod.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
export * from "./sleep.ts"
|
||||
|
||||
11
vendor/deno.land/x/sleep@v1.3.0/sleep.ts
vendored
Normal file
11
vendor/deno.land/x/sleep@v1.3.0/sleep.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
// I buy and sell https://FreedomCash.org
|
||||
export function sleep(seconds: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, seconds * 1000))
|
||||
}
|
||||
export function sleepRandomAmountOfSeconds(minimumSeconds: number, maximumSeconds: number) {
|
||||
const secondsOfSleep = getRandomArbitrary(minimumSeconds, maximumSeconds)
|
||||
return new Promise((resolve) => setTimeout(resolve, secondsOfSleep * 1000))
|
||||
}
|
||||
function getRandomArbitrary(min: number, max: number) {
|
||||
return Math.random() * (max - min) + min
|
||||
}
|
||||
6
vendor/manifest.json
vendored
6
vendor/manifest.json
vendored
@ -1,5 +1,11 @@
|
||||
{
|
||||
"modules": {
|
||||
"https://deno.land/x/sleep/mod.ts": {
|
||||
"headers": {
|
||||
"location": "/x/sleep@v1.3.0/mod.ts",
|
||||
"x-deno-warning": "Implicitly using latest version (v1.3.0) for https://deno.land/x/sleep/mod.ts"
|
||||
}
|
||||
},
|
||||
"https://jsr.io/@std/crypto/1.0.3/_wasm/lib/deno_std_wasm_crypto.generated.d.mts": {},
|
||||
"https://jsr.io/@std/crypto/1.0.3/_wasm/lib/deno_std_wasm_crypto.generated.mjs": {},
|
||||
"https://jsr.io/@std/net/1.0.4/unstable_get_network_address.ts": {}
|
||||
|
||||
Reference in New Issue
Block a user