Create a typescript client library for the server
This commit is contained in:
parent
79af050f53
commit
182020e136
2
visions/client/.gitignore
vendored
Normal file
2
visions/client/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
gen/
|
||||||
|
dist/
|
12
visions/client/Taskfile.yml
Normal file
12
visions/client/Taskfile.yml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
fmt:
|
||||||
|
cmds:
|
||||||
|
- npx prettier -w package.json src
|
||||||
|
|
||||||
|
build:
|
||||||
|
cmds:
|
||||||
|
- npm install typescript
|
||||||
|
- typeshare --lang typescript --output-file gen/types.ts ../server/src
|
||||||
|
- npx tsc
|
29
visions/client/eslint.config.js
Normal file
29
visions/client/eslint.config.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import js from "@eslint/js";
|
||||||
|
import globals from "globals";
|
||||||
|
import reactHooks from "eslint-plugin-react-hooks";
|
||||||
|
import reactRefresh from "eslint-plugin-react-refresh";
|
||||||
|
import tseslint from "typescript-eslint";
|
||||||
|
import eslintConfigPrettier from "eslint-config-prettier";
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
{ ignores: ["dist"] },
|
||||||
|
{
|
||||||
|
extends: [js.configs.recommended, ...tseslint.configs.recommended, eslintConfigPrettier],
|
||||||
|
files: ["**/*.{ts,tsx}"],
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
globals: globals.browser,
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
"react-hooks": reactHooks,
|
||||||
|
"react-refresh": reactRefresh,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
...reactHooks.configs.recommended.rules,
|
||||||
|
"react-refresh/only-export-components": [
|
||||||
|
"warn",
|
||||||
|
{ allowConstantExport: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
3813
visions/client/package-lock.json
generated
Normal file
3813
visions/client/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
21
visions/client/package.json
Normal file
21
visions/client/package.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "visions-client",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "Shared data types for Visions",
|
||||||
|
"main": "visions.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"prettier": {
|
||||||
|
"singleQuote": true,
|
||||||
|
"semi": false,
|
||||||
|
"tabWidth": 4
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"prettier": "^3.5.1",
|
||||||
|
"ts-jest": "^29.2.5",
|
||||||
|
"typescript": "^5.7.3"
|
||||||
|
}
|
||||||
|
}
|
42
visions/client/src/client.ts
Normal file
42
visions/client/src/client.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { VResponse, SessionId } from '../gen/types'
|
||||||
|
|
||||||
|
export interface Client {
|
||||||
|
auth: (
|
||||||
|
username: string,
|
||||||
|
password: string,
|
||||||
|
) => Promise<ClientResponse<SessionId>>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ClientResponse<A> =
|
||||||
|
| { status: 'ok'; content: VResponse<A> }
|
||||||
|
| { status: 'unauthorized' }
|
||||||
|
| { status: 'unexpected'; code: number }
|
||||||
|
|
||||||
|
export class Connection implements Client {
|
||||||
|
private base: URL
|
||||||
|
// private sessionId: string | undefined;
|
||||||
|
|
||||||
|
constructor(baseUrl: URL) {
|
||||||
|
this.base = baseUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
async auth(
|
||||||
|
username: string,
|
||||||
|
password: string,
|
||||||
|
): Promise<ClientResponse<SessionId>> {
|
||||||
|
const url = new URL(this.base)
|
||||||
|
url.pathname = `/api/v1/auth`
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: [['Content-Type', 'application/json']],
|
||||||
|
body: JSON.stringify({ username: username, password: password }),
|
||||||
|
})
|
||||||
|
if (response.ok) {
|
||||||
|
return await response.json()
|
||||||
|
} else if (response.status == 401) {
|
||||||
|
return { status: 'unauthorized' }
|
||||||
|
} else {
|
||||||
|
return { status: 'unexpected', code: response.status }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,5 +11,5 @@
|
|||||||
"strict": true,
|
"strict": true,
|
||||||
"skipLibCheck": true
|
"skipLibCheck": true
|
||||||
},
|
},
|
||||||
"include": ["./visions.ts"]
|
"include": ["gen", "src"]
|
||||||
}
|
}
|
15
visions/client/visions.ts
Normal file
15
visions/client/visions.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
Generated by typeshare 1.13.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type SessionId = string;
|
||||||
|
|
||||||
|
export interface AuthRequest {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AuthResponse =
|
||||||
|
| { type: "Success", content: SessionId }
|
||||||
|
| { type: "PasswordReset", content: SessionId };
|
||||||
|
|
@ -38,19 +38,20 @@ impl From<String> for SessionId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
|
#[serde(tag = "type", content = "content")]
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
enum AuthResponse {
|
enum VResponse<A> {
|
||||||
Success(SessionId),
|
Success(A),
|
||||||
PasswordReset(SessionId),
|
PasswordReset(A),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[axum::debug_handler]
|
#[axum::debug_handler]
|
||||||
async fn check_password(request: Json<AuthRequest>) -> (StatusCode, Json<Option<AuthResponse>>) {
|
async fn check_password(request: Json<AuthRequest>) -> (StatusCode, Json<Option<VResponse<SessionId>>>) {
|
||||||
let Json(request) = request;
|
let Json(request) = request;
|
||||||
if request.username == "vakarian" && request.password == "aoeu" {
|
if request.username == "vakarian" && request.password == "aoeu" {
|
||||||
(StatusCode::OK, Json(Some(AuthResponse::Success("vakarian-session-id".into()))))
|
(StatusCode::OK, Json(Some(VResponse::Success("vakarian-session-id".into()))))
|
||||||
} else if request.username == "shephard" && request.password == "aoeu" {
|
} else if request.username == "shephard" && request.password == "aoeu" {
|
||||||
(StatusCode::OK, Json(Some(AuthResponse::PasswordReset("shephard-session-id".into()))))
|
(StatusCode::OK, Json(Some(VResponse::PasswordReset("shephard-session-id".into()))))
|
||||||
} else {
|
} else {
|
||||||
(StatusCode::UNAUTHORIZED, Json(None))
|
(StatusCode::UNAUTHORIZED, Json(None))
|
||||||
}
|
}
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
build:
|
|
||||||
cmds:
|
|
||||||
- npm install typescript
|
|
||||||
- typeshare --lang typescript --output-file visions.ts ../server/src
|
|
||||||
- npx tsc
|
|
@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "visions-types",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"description": "Shared data types for Visions",
|
|
||||||
"main": "visions.js",
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
|
||||||
},
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"typescript": "^5.7.3"
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user