use server sent events instead of polling
All checks were successful
Build Container / Build Container (push) Successful in 1m27s
All checks were successful
Build Container / Build Container (push) Successful in 1m27s
This commit is contained in:
parent
8bc4e7f28e
commit
8281848215
2 changed files with 90 additions and 46 deletions
|
|
@ -89,6 +89,23 @@ export interface paths {
|
||||||
patch?: never;
|
patch?: never;
|
||||||
trace?: never;
|
trace?: never;
|
||||||
};
|
};
|
||||||
|
"/api/locks/stream": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
/** Watch Locks */
|
||||||
|
get: operations["watch_locks_api_locks_stream_get"];
|
||||||
|
put?: never;
|
||||||
|
post?: never;
|
||||||
|
delete?: never;
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
"/api/locks/{lock_id}": {
|
"/api/locks/{lock_id}": {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
|
|
@ -135,7 +152,7 @@ export interface components {
|
||||||
* @description Statically known HTTP problem types using the [type URI scheme](https://datatracker.ietf.org/doc/rfc4151/)
|
* @description Statically known HTTP problem types using the [type URI scheme](https://datatracker.ietf.org/doc/rfc4151/)
|
||||||
* @enum {string}
|
* @enum {string}
|
||||||
*/
|
*/
|
||||||
HttpProblemType: "type:noc@hamburg.ccc.de,2026:UNAUTHORIZED" | "type:noc@hamburg.ccc.de,2026:LOCK_NOT_FOUND";
|
HttpProblemType: "type:noc@hamburg.ccc.de,2026:UNAUTHORIZED" | "type:noc@hamburg.ccc.de,2026,FORBIDDEN_TO_OPERATE" | "type:noc@hamburg.ccc.de,2026:LOCK_NOT_FOUND";
|
||||||
/** Lock */
|
/** Lock */
|
||||||
Lock: {
|
Lock: {
|
||||||
/** Name */
|
/** Name */
|
||||||
|
|
@ -178,14 +195,17 @@ export interface components {
|
||||||
};
|
};
|
||||||
/** UserStatus */
|
/** UserStatus */
|
||||||
UserStatus: {
|
UserStatus: {
|
||||||
/** Is Logged In */
|
|
||||||
is_logged_in: boolean;
|
|
||||||
/** Is Authorized */
|
/** Is Authorized */
|
||||||
is_authorized: boolean;
|
is_authorized: boolean;
|
||||||
/** Guaranteed Session Until */
|
/**
|
||||||
guaranteed_session_until: string | null;
|
* Guaranteed Session Until
|
||||||
|
* Format: date-time
|
||||||
|
*/
|
||||||
|
guaranteed_session_until: string;
|
||||||
/** Username */
|
/** Username */
|
||||||
username: string | null;
|
username: string;
|
||||||
|
/** Ccchh Roles */
|
||||||
|
ccchh_roles: string[];
|
||||||
};
|
};
|
||||||
/** ValidationError */
|
/** ValidationError */
|
||||||
ValidationError: {
|
ValidationError: {
|
||||||
|
|
@ -332,6 +352,35 @@ export interface operations {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
watch_locks_api_locks_stream_get: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"text/event-stream": unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Unauthorized */
|
||||||
|
401: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"text/event-stream": components["schemas"]["HttpProblemDetail"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
operate_lock_api_locks__lock_id__patch: {
|
operate_lock_api_locks__lock_id__patch: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
|
|
@ -365,6 +414,15 @@ export interface operations {
|
||||||
"application/json": components["schemas"]["HttpProblemDetail"];
|
"application/json": components["schemas"]["HttpProblemDetail"];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
/** @description Forbidden */
|
||||||
|
403: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HttpProblemDetail"];
|
||||||
|
};
|
||||||
|
};
|
||||||
/** @description Not Found */
|
/** @description Not Found */
|
||||||
404: {
|
404: {
|
||||||
headers: {
|
headers: {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import {Fetcher} from "openapi-typescript-fetch"
|
import {Fetcher} from "openapi-typescript-fetch"
|
||||||
import type {paths} from "../api/schema"
|
import type {components, paths} from "../api/schema"
|
||||||
import type {ui} from "../i18n/ui.ts"
|
import type {ui} from "../i18n/ui.ts"
|
||||||
|
|
||||||
const fetcher = Fetcher.for<paths>()
|
const fetcher = Fetcher.for<paths>()
|
||||||
|
|
@ -29,7 +29,7 @@ declare global {
|
||||||
lang: keyof typeof ui;
|
lang: keyof typeof ui;
|
||||||
doors: Array<DoorType>;
|
doors: Array<DoorType>;
|
||||||
auth: AuthType;
|
auth: AuthType;
|
||||||
doorAction: (action: 'unlock' | 'lock', doorId: string) => void;
|
doorAction: (action: "unlock" | "lock", doorId: string) => void;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,18 +105,18 @@ async function checkUser() {
|
||||||
if (e instanceof getUserInfo.Error) {
|
if (e instanceof getUserInfo.Error) {
|
||||||
const error = e.getActualType()
|
const error = e.getActualType()
|
||||||
|
|
||||||
if (error.status === 401) {
|
|
||||||
if (!auth.recentLogout)
|
if (error.status >= 500 && error.status < 600) {
|
||||||
auth.recentLogout = auth.authenticated // set recentLogout true, if user was logged in before
|
|
||||||
auth.authenticated = false
|
|
||||||
auth.authorized = false
|
|
||||||
auth.until = null
|
|
||||||
auth.username = ""
|
|
||||||
} else if (error.status >= 500 && error.status < 600) {
|
|
||||||
apiError.current = "serverError"
|
apiError.current = "serverError"
|
||||||
} else {
|
|
||||||
console.error("unknown error:", error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!auth.recentLogout)
|
||||||
|
auth.recentLogout = auth.authenticated // set recentLogout true, if user was logged in before
|
||||||
|
auth.authenticated = false
|
||||||
|
auth.authorized = false
|
||||||
|
auth.until = null
|
||||||
|
auth.username = ""
|
||||||
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
localStorage.setItem("auth", JSON.stringify(auth))
|
localStorage.setItem("auth", JSON.stringify(auth))
|
||||||
|
|
@ -125,15 +125,24 @@ async function checkUser() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchDoors() {
|
async function subscribeDoorEvents() {
|
||||||
if (doors.length === 0) {
|
if (doors.length === 0) {
|
||||||
loading.doors = true
|
loading.doors = true
|
||||||
}
|
}
|
||||||
refresh()
|
refresh()
|
||||||
const getDoors = fetcher.path("/api/locks/").method("get").create()
|
|
||||||
|
|
||||||
try {
|
const evtSource = new EventSource("/api/locks/stream")
|
||||||
const {data: doorInfo} = await getDoors({})
|
|
||||||
|
evtSource.onerror = () => {
|
||||||
|
if (!window.navigator.onLine) {
|
||||||
|
apiError.current = "networkError"
|
||||||
|
} else {
|
||||||
|
apiError.current = "serverError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
evtSource.onmessage = (event) => {
|
||||||
|
const doorInfo: Array<components["schemas"]["Lock"]> = JSON.parse(event.data)
|
||||||
|
|
||||||
apiError.current = null
|
apiError.current = null
|
||||||
while (doors.length) {
|
while (doors.length) {
|
||||||
|
|
@ -164,29 +173,6 @@ async function fetchDoors() {
|
||||||
|
|
||||||
loading.doors = false
|
loading.doors = false
|
||||||
refresh()
|
refresh()
|
||||||
} catch (e) {
|
|
||||||
// check which operation threw the exception
|
|
||||||
if (e instanceof getDoors.Error) {
|
|
||||||
const error = e.getActualType()
|
|
||||||
|
|
||||||
if (error.status === 401) {
|
|
||||||
console.log("unauthorized")
|
|
||||||
loading.doors = false
|
|
||||||
refresh()
|
|
||||||
} else if (error.status >= 500 && error.status < 600) {
|
|
||||||
apiError.current = "serverError"
|
|
||||||
clearInterval(doorsInterval)
|
|
||||||
} else {
|
|
||||||
console.error("unknown error:", error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e instanceof Error) {
|
|
||||||
switch (e.name) {
|
|
||||||
case "TypeError":
|
|
||||||
apiError.current = "networkError"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -295,7 +281,7 @@ function refresh() {
|
||||||
|
|
||||||
|
|
||||||
loadAuthFromLocalStorage()
|
loadAuthFromLocalStorage()
|
||||||
const doorsInterval = setInterval(fetchDoors, 250) // TODO: replace with SSE
|
subscribeDoorEvents()
|
||||||
checkUser()
|
checkUser()
|
||||||
|
|
||||||
document.addEventListener("loadeddata", () => {
|
document.addEventListener("loadeddata", () => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue