Magnetar stream status for notification view
ci/woodpecker/push/ociImagePush Pipeline failed
Details
ci/woodpecker/push/ociImagePush Pipeline failed
Details
This commit is contained in:
parent
d12b65fff7
commit
ff3f0927fb
|
@ -28,11 +28,13 @@ async-trait = "0.1"
|
|||
async-stream = "0.3"
|
||||
axum = "0.7"
|
||||
axum-extra = "0.9"
|
||||
base64 = "0.22"
|
||||
cached = "0.47"
|
||||
cfg-if = "1"
|
||||
chrono = "0.4"
|
||||
compact_str = "0.7"
|
||||
dotenvy = "0.15"
|
||||
ed25519-dalek = "2.1"
|
||||
either = "1.9"
|
||||
emojis = "0.6"
|
||||
futures = "0.3"
|
||||
|
@ -42,6 +44,7 @@ headers = "0.4"
|
|||
http = "1.0"
|
||||
hyper = "1.1"
|
||||
idna = "0.5"
|
||||
indexmap = "2.2"
|
||||
itertools = "0.12"
|
||||
lru = "0.12"
|
||||
miette = "5.9"
|
||||
|
@ -51,12 +54,14 @@ percent-encoding = "2.2"
|
|||
quick-xml = "0.31"
|
||||
redis = "0.24"
|
||||
regex = "1.9"
|
||||
reqwest = "0.11"
|
||||
rsa = "0.9"
|
||||
reqwest = "0.12"
|
||||
sea-orm = "0.12"
|
||||
sea-orm-migration = "0.12"
|
||||
serde = "1"
|
||||
serde_json = "1"
|
||||
serde_urlencoded = "0.7"
|
||||
sha2 = "0.10"
|
||||
strum = "0.25"
|
||||
tera = { version = "1", default-features = false }
|
||||
thiserror = "1"
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
<MkError v-else-if="error" @retry="init()" />
|
||||
|
||||
<div v-else-if="empty" key="_empty_" class="empty">
|
||||
<MagStreamStatus @reconnect="reload()" />
|
||||
|
||||
<slot name="empty">
|
||||
<div class="_fullinfo">
|
||||
<img
|
||||
|
@ -18,6 +20,8 @@
|
|||
</div>
|
||||
|
||||
<div v-else ref="rootEl" class="list">
|
||||
<MagStreamStatus @reconnect="reload()" />
|
||||
|
||||
<XList
|
||||
v-slot="{ item: notificationGroup }"
|
||||
class="elsfgstc"
|
||||
|
@ -89,6 +93,7 @@ import XNotification from "@/components/MagNotification.vue";
|
|||
import XNotificationGroup from "@/components/MagNotificationGroup.vue";
|
||||
import XList from "@/components/MkDateSeparatedList.vue";
|
||||
import XNote from "@/components/MagNote.vue";
|
||||
import MagStreamStatus from "@/components/MagStreamStatus.vue";
|
||||
import { magStream, stream } from "@/stream";
|
||||
import { $i } from "@/account";
|
||||
import { i18n } from "@/i18n";
|
||||
|
@ -153,6 +158,7 @@ const onNotification = (notification: packed.PackNotification) => {
|
|||
};
|
||||
|
||||
let notifStream: ((e: ChannelEvent) => void) | undefined;
|
||||
|
||||
let connection: Connection<Channels["main"]>;
|
||||
|
||||
onMounted(() => {
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
<template>
|
||||
<div
|
||||
class="mag-connection-status"
|
||||
v-if="state !== 'connected' || showConnected"
|
||||
:class="state"
|
||||
>
|
||||
<span v-if="state === 'connected'">
|
||||
{{ i18n.t("connectionStatus.connected") }}
|
||||
</span>
|
||||
<span v-if="state === 'exponentialBackoff' || state === 'initial'">
|
||||
{{ i18n.t("connectionStatus.reconnecting") }}
|
||||
<MkEllipsis />
|
||||
</span>
|
||||
<span v-else-if="state === 'failed' || state === 'closed'">
|
||||
{{ i18n.t("connectionStatus.disconnected") }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { i18n } from "@/i18n";
|
||||
import { magStream } from "@/stream";
|
||||
import { MagChannelState } from "magnetar-common";
|
||||
import { onMounted, onUnmounted, ref } from "vue";
|
||||
|
||||
defineProps<{
|
||||
showConnected?: boolean
|
||||
}>();
|
||||
|
||||
const state = ref<MagChannelState>(magStream.state);
|
||||
let stateListener: (e: MagChannelState) => void = (e) => {
|
||||
if (
|
||||
state.value !== "initial" &&
|
||||
state.value !== "connected" &&
|
||||
e === "connected"
|
||||
) {
|
||||
emit("reconnect");
|
||||
}
|
||||
|
||||
state.value = e;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
magStream.on("stateChange", stateListener);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
magStream.off("stateChange", stateListener);
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "reconnect"): void;
|
||||
}>();
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.mag-connection-status {
|
||||
padding: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--infoBg);
|
||||
color: var(--infoFg);
|
||||
|
||||
// Experimental
|
||||
position: sticky;
|
||||
z-index: 5;
|
||||
top: 0;
|
||||
|
||||
&.closed, &.failed {
|
||||
background-color: var(--infoWarnBg);
|
||||
color: var(--error);
|
||||
}
|
||||
|
||||
&.exponentialBackoff {
|
||||
background-color: var(--infoWarnBg);
|
||||
color: var(--infoWarnFg);
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -2199,3 +2199,8 @@ _experiments:
|
|||
_dialog:
|
||||
charactersExceeded: "Max characters exceeded! Current: {current}/Limit: {max}"
|
||||
charactersBelow: "Not enough characters! Current: {current}/Limit: {min}"
|
||||
|
||||
connectionStatus:
|
||||
connected: "Connected"
|
||||
reconnecting: "Reconnecting"
|
||||
disconnected: "Disconnected from the server"
|
||||
|
|
|
@ -13,7 +13,7 @@ import {
|
|||
FrontendApiEndpoints,
|
||||
} from "./fe-api";
|
||||
|
||||
import { MagEventChannel } from "./sse-listener";
|
||||
import { MagEventChannel, MagChannelState } from "./sse-listener";
|
||||
|
||||
import * as types from "./types";
|
||||
import * as packed from "./packed";
|
||||
|
@ -43,6 +43,7 @@ export {
|
|||
FrontendApiEndpoint,
|
||||
FrontendApiEndpoints,
|
||||
MagEventChannel,
|
||||
MagChannelState,
|
||||
types,
|
||||
packed,
|
||||
endpoints,
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import { EventEmitter } from "eventemitter3";
|
||||
import { ChannelEvent } from "./types/ChannelEvent";
|
||||
|
||||
export type MagChannelState = "connected" | "exponentialBackoff" | "failed";
|
||||
export type MagChannelState =
|
||||
| "initial"
|
||||
| "connected"
|
||||
| "exponentialBackoff"
|
||||
| "failed"
|
||||
| "closed";
|
||||
|
||||
export class MagEventChannel extends EventEmitter<{
|
||||
stateChange: MagChannelState;
|
||||
|
@ -15,11 +20,12 @@ export class MagEventChannel extends EventEmitter<{
|
|||
private readonly backoffFactor: number;
|
||||
private readonly backoffBase: number;
|
||||
private readonly closePromise: Promise<"cancelled">;
|
||||
private _state: MagChannelState;
|
||||
|
||||
public constructor(
|
||||
baseUrl: string,
|
||||
token: string | null,
|
||||
maxReconnectAttempts: number = 12,
|
||||
maxReconnectAttempts: number = 15,
|
||||
backoffFactor: number = 1.618,
|
||||
backoffBase: number = 500.0
|
||||
) {
|
||||
|
@ -30,6 +36,8 @@ export class MagEventChannel extends EventEmitter<{
|
|||
this.maxAttempts = maxReconnectAttempts;
|
||||
this.backoffFactor = backoffFactor;
|
||||
this.backoffBase = backoffBase;
|
||||
this._state = "initial";
|
||||
this.updateState("initial");
|
||||
this.closePromise = new Promise((resolve) => {
|
||||
this.on("close", resolve);
|
||||
});
|
||||
|
@ -49,9 +57,18 @@ export class MagEventChannel extends EventEmitter<{
|
|||
return cb;
|
||||
}
|
||||
|
||||
public get state(): MagChannelState {
|
||||
return this._state;
|
||||
}
|
||||
|
||||
private updateState(state: MagChannelState) {
|
||||
this._state = state;
|
||||
this.emit("stateChange", state);
|
||||
}
|
||||
|
||||
private async connect() {
|
||||
if (this.attempts >= this.maxAttempts) {
|
||||
this.emit("stateChange", "failed");
|
||||
this.updateState("failed");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -73,7 +90,7 @@ export class MagEventChannel extends EventEmitter<{
|
|||
response.status >= 500 ||
|
||||
response.body === null
|
||||
) {
|
||||
this.emit("stateChange", "exponentialBackoff");
|
||||
this.updateState("exponentialBackoff");
|
||||
setTimeout(
|
||||
() => this.connect(),
|
||||
this.backoffBase * Math.pow(this.backoffFactor, this.attempts)
|
||||
|
@ -83,7 +100,7 @@ export class MagEventChannel extends EventEmitter<{
|
|||
}
|
||||
|
||||
if (response.status >= 400 && response.status < 500) {
|
||||
this.emit("stateChange", "failed");
|
||||
this.updateState("failed");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -91,7 +108,7 @@ export class MagEventChannel extends EventEmitter<{
|
|||
|
||||
const decoderStream = new TextDecoderStream();
|
||||
const reader = response.body.pipeThrough(decoderStream).getReader();
|
||||
this.emit("stateChange", "connected");
|
||||
this.updateState("connected");
|
||||
|
||||
let buf = "";
|
||||
|
||||
|
@ -101,7 +118,7 @@ export class MagEventChannel extends EventEmitter<{
|
|||
if (res === "cancelled") break;
|
||||
|
||||
if (res.done) {
|
||||
this.emit("stateChange", "exponentialBackoff");
|
||||
this.updateState("exponentialBackoff");
|
||||
setTimeout(
|
||||
() => this.connect(),
|
||||
this.backoffBase *
|
||||
|
@ -134,6 +151,8 @@ export class MagEventChannel extends EventEmitter<{
|
|||
const data = JSON.parse(text) as ChannelEvent;
|
||||
this.emit("message", data);
|
||||
}
|
||||
|
||||
this.updateState("closed");
|
||||
}
|
||||
|
||||
public async close() {
|
||||
|
|
|
@ -1438,8 +1438,8 @@ packages:
|
|||
'@types/node': 20.8.10
|
||||
dev: true
|
||||
|
||||
/@types/yauzl@2.10.2:
|
||||
resolution: {integrity: sha512-Km7XAtUIduROw7QPgvcft0lIupeG8a8rdKL8RiSyKvlE7dYY31fEn41HVuQsRFDuROA8tA4K2UVL+WdfFmErBA==}
|
||||
/@types/yauzl@2.10.3:
|
||||
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
'@types/node': 14.18.63
|
||||
|
@ -2705,8 +2705,8 @@ packages:
|
|||
is-plain-object: 5.0.0
|
||||
dev: true
|
||||
|
||||
/core-js@3.33.2:
|
||||
resolution: {integrity: sha512-XeBzWI6QL3nJQiHmdzbAOiMYqjrb7hwU7A39Qhvd/POSa/t9E1AeZyEZx3fNvp/vtM8zXwhoL0FsiS0hD0pruQ==}
|
||||
/core-js@3.36.1:
|
||||
resolution: {integrity: sha512-BTvUrwxVBezj5SZ3f10ImnX2oRByMxql3EimVqMysepbC9EeMUOpLwdy6Eoili2x6E4kf+ZUB5k/+Jv55alPfA==}
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
|
||||
|
@ -3428,7 +3428,7 @@ packages:
|
|||
get-stream: 5.2.0
|
||||
yauzl: 2.10.0
|
||||
optionalDependencies:
|
||||
'@types/yauzl': 2.10.2
|
||||
'@types/yauzl': 2.10.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
@ -7733,7 +7733,7 @@ packages:
|
|||
name: plyr
|
||||
version: 3.7.0
|
||||
dependencies:
|
||||
core-js: 3.33.2
|
||||
core-js: 3.36.1
|
||||
custom-event-polyfill: 1.0.7
|
||||
loadjs: 4.2.0
|
||||
rangetouch: 2.0.1
|
||||
|
|
Loading…
Reference in New Issue