|
|
|
|
@@ -1,19 +1,28 @@
|
|
|
|
|
import { Schema as S } from "@effect/schema";
|
|
|
|
|
import { DateTime, Duration, Effect, Option as O, pipe } from "effect";
|
|
|
|
|
import { DateTime, Duration, Either as E, Effect, Option as O, pipe } from "effect";
|
|
|
|
|
import { RequestError } from "./RequestError";
|
|
|
|
|
import { Database } from "./services/database";
|
|
|
|
|
import { Request } from "./services/request";
|
|
|
|
|
import { brotliCompress } from "node:zlib";
|
|
|
|
|
import { Match } from "effect";
|
|
|
|
|
import { Api } from "make-api";
|
|
|
|
|
import { Login } from "common/api";
|
|
|
|
|
import { AccessLog } from "common/db";
|
|
|
|
|
import { LoginRequest, LoginResponse } from "common/api";
|
|
|
|
|
import { brotliCompress } from "node:zlib";
|
|
|
|
|
|
|
|
|
|
const match = (method: string, ...pattern: readonly string[]) => Effect.gen(function* () {
|
|
|
|
|
const match = (api: Api.Api.Any) => Effect.gen(function* () {
|
|
|
|
|
|
|
|
|
|
const req = yield* Request;
|
|
|
|
|
|
|
|
|
|
return req.method === method
|
|
|
|
|
&& req.path.length === pattern.length
|
|
|
|
|
&& pattern.every((x, i) => x === "*" || x === req.path[i]);
|
|
|
|
|
return req.method === api.method
|
|
|
|
|
&& req.path.length === api.props.route.length
|
|
|
|
|
&& api.props.route.every((token, i) => pipe(
|
|
|
|
|
Match.value(token),
|
|
|
|
|
Match.tags({
|
|
|
|
|
Literal: ({ literal }) => req.path[i] === literal,
|
|
|
|
|
Param: ({ schema }) => E.isRight(S.decodeUnknownEither(schema)(req.path[i])),
|
|
|
|
|
}),
|
|
|
|
|
Match.exhaustive,
|
|
|
|
|
));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const WEIGHTED_TOKEN_REGEX = /^([!#$%'*+.^_`|~a-z0-9-])\s*(?:;\s*q=(0(?:\.[0-9]{0,3})?|1(?:\.0{0,3})?))?$/i;
|
|
|
|
|
@@ -81,23 +90,23 @@ export const app = pipe(
|
|
|
|
|
|
|
|
|
|
console.log(JSON.stringify(accessLog));
|
|
|
|
|
|
|
|
|
|
if (yield* match("POST", "login")) {
|
|
|
|
|
if (yield* match(Login)) {
|
|
|
|
|
|
|
|
|
|
const body = yield* requestJson(LoginRequest);
|
|
|
|
|
const body = yield* requestJson(Login.props.request.schema);
|
|
|
|
|
|
|
|
|
|
const user = yield* pipe(
|
|
|
|
|
db.getUserByUsername(body.username),
|
|
|
|
|
Effect.catchTag("NoSuchElementException", () => new RequestError({ status: 404, body: "Invalid username or password" })),
|
|
|
|
|
Effect.catchTag("NoSuchElementException", () => new RequestError({ status: 401, body: "Invalid username or password" })),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const valid = yield* Effect.promise(() => Bun.password.verify(body.password, user.password));
|
|
|
|
|
if (!valid) {
|
|
|
|
|
return yield* new RequestError({ status: 404, body: "Invalid username or password" });
|
|
|
|
|
return yield* new RequestError({ status: 401, body: "Invalid username or password" });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const sessionId = yield* db.createSession(user.userId);
|
|
|
|
|
|
|
|
|
|
const responseData = LoginResponse.make({
|
|
|
|
|
const responseData = Login.props.response[200].schema.make({
|
|
|
|
|
userId: user.userId,
|
|
|
|
|
username: user.username,
|
|
|
|
|
admin: user.admin,
|
|
|
|
|
@@ -105,7 +114,7 @@ export const app = pipe(
|
|
|
|
|
|
|
|
|
|
const responseJson = yield* pipe(
|
|
|
|
|
responseData,
|
|
|
|
|
S.encode(LoginResponse),
|
|
|
|
|
S.encode(Login.props.response[200].schema),
|
|
|
|
|
Effect.map(JSON.stringify),
|
|
|
|
|
);
|
|
|
|
|
const responseArray = new TextEncoder().encode(responseJson);
|
|
|
|
|
|