mirror of
https://git.sr.ht/~ashkeel/strimertul
synced 2024-09-20 02:00:49 +00:00
Add authentication (using Kilovolt v6)
This commit is contained in:
parent
27a7ede45e
commit
8e02ba6fb7
13 changed files with 505 additions and 12399 deletions
|
@ -6,7 +6,9 @@ module.exports = {
|
||||||
'no-console': 0,
|
'no-console': 0,
|
||||||
'import/extensions': 0,
|
'import/extensions': 0,
|
||||||
'no-use-before-define': 'off',
|
'no-use-before-define': 'off',
|
||||||
|
'no-shadow': 'off',
|
||||||
'@typescript-eslint/no-use-before-define': ['error'],
|
'@typescript-eslint/no-use-before-define': ['error'],
|
||||||
|
'@typescript-eslint/no-shadow': ['error'],
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
'import/resolver': {
|
'import/resolver': {
|
||||||
|
|
12612
frontend/package-lock.json
generated
12612
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -5,7 +5,7 @@
|
||||||
"@billjs/event-emitter": "^1.0.3",
|
"@billjs/event-emitter": "^1.0.3",
|
||||||
"@reach/router": "^1.3.4",
|
"@reach/router": "^1.3.4",
|
||||||
"@reduxjs/toolkit": "^1.5.1",
|
"@reduxjs/toolkit": "^1.5.1",
|
||||||
"@strimertul/kilovolt-client": "^5.0.1",
|
"@strimertul/kilovolt-client": "^6.2.0",
|
||||||
"@types/node": "^15.0.2",
|
"@types/node": "^15.0.2",
|
||||||
"@types/reach__router": "^1.3.7",
|
"@types/reach__router": "^1.3.7",
|
||||||
"@types/react": "^17.0.5",
|
"@types/react": "^17.0.5",
|
||||||
|
@ -13,7 +13,6 @@
|
||||||
"@vitejs/plugin-react": "^1.0.9",
|
"@vitejs/plugin-react": "^1.0.9",
|
||||||
"bulma": "^0.9.2",
|
"bulma": "^0.9.2",
|
||||||
"i18next": "^20.6.1",
|
"i18next": "^20.6.1",
|
||||||
"parcel": "^2.0.1",
|
|
||||||
"pretty-ms": "^7.0.1",
|
"pretty-ms": "^7.0.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
|
@ -33,7 +32,6 @@
|
||||||
"last 1 Chrome version"
|
"last 1 Chrome version"
|
||||||
],
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@parcel/transformer-sass": "^2.0.0-beta.2",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^4.23.0",
|
"@typescript-eslint/eslint-plugin": "^4.23.0",
|
||||||
"@typescript-eslint/parser": "^4.23.0",
|
"@typescript-eslint/parser": "^4.23.0",
|
||||||
"eslint": "^7.26.0",
|
"eslint": "^7.26.0",
|
||||||
|
|
|
@ -54,7 +54,8 @@
|
||||||
"server-bind": "HTTP server bind",
|
"server-bind": "HTTP server bind",
|
||||||
"static-content": "Static content",
|
"static-content": "Static content",
|
||||||
"enable-static": "Enable static server",
|
"enable-static": "Enable static server",
|
||||||
"static-root-path": "Static content root path"
|
"static-root-path": "Static content root path",
|
||||||
|
"kv-password": "Kilovolt password"
|
||||||
},
|
},
|
||||||
"backend": {
|
"backend": {
|
||||||
"config": {
|
"config": {
|
||||||
|
@ -226,5 +227,11 @@
|
||||||
"err-twitchbot-disabled": "Twitch bot must be enabled in order to use timers!",
|
"err-twitchbot-disabled": "Twitch bot must be enabled in order to use timers!",
|
||||||
"enable": "Enable timers"
|
"enable": "Enable timers"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"auth": {
|
||||||
|
"message": "Strimertül's database is protected by a password, please write it below to access the control panel. If the database has no password (for example, it was recently changed from having one to none), leave the field empty.",
|
||||||
|
"header": "Authorization needed",
|
||||||
|
"placeholder": "Password",
|
||||||
|
"button": "Authenticate"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,10 @@ import {
|
||||||
PayloadAction,
|
PayloadAction,
|
||||||
} from '@reduxjs/toolkit';
|
} from '@reduxjs/toolkit';
|
||||||
import KilovoltWS from '@strimertul/kilovolt-client';
|
import KilovoltWS from '@strimertul/kilovolt-client';
|
||||||
|
import { kvError } from '@strimertul/kilovolt-client/lib/messages';
|
||||||
import {
|
import {
|
||||||
APIState,
|
APIState,
|
||||||
|
ConnectionStatus,
|
||||||
LoyaltyPointsEntry,
|
LoyaltyPointsEntry,
|
||||||
LoyaltyRedeem,
|
LoyaltyRedeem,
|
||||||
LoyaltyStorage,
|
LoyaltyStorage,
|
||||||
|
@ -63,8 +65,11 @@ function makeModule<T>(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-mutable-exports, @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
export let setupClientReconnect: AsyncThunk<void, KilovoltWS, {}>;
|
let setupClientReconnect: AsyncThunk<void, KilovoltWS, {}>;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
|
let kvErrorReceived: AsyncThunk<void, kvError, {}>;
|
||||||
|
|
||||||
// Storage
|
// Storage
|
||||||
const loyaltyPointsPrefix = 'loyalty/points/';
|
const loyaltyPointsPrefix = 'loyalty/points/';
|
||||||
|
@ -76,8 +81,11 @@ const loyaltyRemoveRedeemKey = 'loyalty/@remove-redeem';
|
||||||
|
|
||||||
export const createWSClient = createAsyncThunk(
|
export const createWSClient = createAsyncThunk(
|
||||||
'api/createClient',
|
'api/createClient',
|
||||||
async (address: string, { dispatch }) => {
|
async (options: { address: string; password?: string }, { dispatch }) => {
|
||||||
const client = new KilovoltWS(address);
|
const client = new KilovoltWS(options.address, options.password);
|
||||||
|
client.on('error', (err) => {
|
||||||
|
dispatch(kvErrorReceived(err.data));
|
||||||
|
});
|
||||||
await client.wait();
|
await client.wait();
|
||||||
dispatch(setupClientReconnect(client));
|
dispatch(setupClientReconnect(client));
|
||||||
return client;
|
return client;
|
||||||
|
@ -233,7 +241,8 @@ const moduleChangeReducers = Object.fromEntries(
|
||||||
|
|
||||||
const initialState: APIState = {
|
const initialState: APIState = {
|
||||||
client: null,
|
client: null,
|
||||||
connected: false,
|
connectionStatus: ConnectionStatus.NotConnected,
|
||||||
|
kvError: null,
|
||||||
initialLoadComplete: false,
|
initialLoadComplete: false,
|
||||||
loyalty: {
|
loyalty: {
|
||||||
users: null,
|
users: null,
|
||||||
|
@ -264,8 +273,14 @@ const apiReducer = createSlice({
|
||||||
initialLoadCompleted(state) {
|
initialLoadCompleted(state) {
|
||||||
state.initialLoadComplete = true;
|
state.initialLoadComplete = true;
|
||||||
},
|
},
|
||||||
connectionStatusChanged(state, { payload }: PayloadAction<boolean>) {
|
connectionStatusChanged(
|
||||||
state.connected = payload;
|
state,
|
||||||
|
{ payload }: PayloadAction<ConnectionStatus>,
|
||||||
|
) {
|
||||||
|
state.connectionStatus = payload;
|
||||||
|
},
|
||||||
|
kvErrorReceived(state, { payload }: PayloadAction<kvError>) {
|
||||||
|
state.kvError = payload;
|
||||||
},
|
},
|
||||||
loyaltyUserPointsChanged(
|
loyaltyUserPointsChanged(
|
||||||
state,
|
state,
|
||||||
|
@ -279,7 +294,7 @@ const apiReducer = createSlice({
|
||||||
extraReducers: (builder) => {
|
extraReducers: (builder) => {
|
||||||
builder.addCase(createWSClient.fulfilled, (state, { payload }) => {
|
builder.addCase(createWSClient.fulfilled, (state, { payload }) => {
|
||||||
state.client = payload;
|
state.client = payload;
|
||||||
state.connected = true;
|
state.connectionStatus = ConnectionStatus.Connected;
|
||||||
});
|
});
|
||||||
builder.addCase(getUserPoints.fulfilled, (state, { payload }) => {
|
builder.addCase(getUserPoints.fulfilled, (state, { payload }) => {
|
||||||
state.loyalty.users = payload;
|
state.loyalty.users = payload;
|
||||||
|
@ -299,12 +314,38 @@ setupClientReconnect = createAsyncThunk(
|
||||||
console.info('Attempting reconnection');
|
console.info('Attempting reconnection');
|
||||||
client.reconnect();
|
client.reconnect();
|
||||||
}, 5000);
|
}, 5000);
|
||||||
dispatch(apiReducer.actions.connectionStatusChanged(false));
|
dispatch(
|
||||||
|
apiReducer.actions.connectionStatusChanged(
|
||||||
|
ConnectionStatus.NotConnected,
|
||||||
|
),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
client.on('open', () => {
|
client.on('open', () => {
|
||||||
dispatch(apiReducer.actions.connectionStatusChanged(true));
|
dispatch(
|
||||||
|
apiReducer.actions.connectionStatusChanged(ConnectionStatus.Connected),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
kvErrorReceived = createAsyncThunk(
|
||||||
|
'api/kvErrorReceived',
|
||||||
|
async (error: kvError, { dispatch }) => {
|
||||||
|
switch (error.error) {
|
||||||
|
case 'authentication required':
|
||||||
|
case 'authentication failed':
|
||||||
|
dispatch(
|
||||||
|
apiReducer.actions.connectionStatusChanged(
|
||||||
|
ConnectionStatus.AuthenticationNeeded,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Unsupported error
|
||||||
|
dispatch(apiReducer.actions.kvErrorReceived(error));
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export default apiReducer;
|
export default apiReducer;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/* eslint-disable camelcase */
|
/* eslint-disable camelcase */
|
||||||
|
|
||||||
import KilovoltWS from '@strimertul/kilovolt-client';
|
import KilovoltWS from '@strimertul/kilovolt-client';
|
||||||
|
import { kvError } from '@strimertul/kilovolt-client/lib/messages';
|
||||||
|
|
||||||
interface ModuleConfig {
|
interface ModuleConfig {
|
||||||
configured: boolean;
|
configured: boolean;
|
||||||
|
@ -13,6 +14,7 @@ interface ModuleConfig {
|
||||||
interface HTTPConfig {
|
interface HTTPConfig {
|
||||||
bind: string;
|
bind: string;
|
||||||
enable_static_server: boolean;
|
enable_static_server: boolean;
|
||||||
|
kv_password: string;
|
||||||
path: string;
|
path: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,9 +111,16 @@ export interface LoyaltyRedeem {
|
||||||
request_text: string;
|
request_text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ConnectionStatus {
|
||||||
|
NotConnected,
|
||||||
|
AuthenticationNeeded,
|
||||||
|
Connected,
|
||||||
|
}
|
||||||
|
|
||||||
export interface APIState {
|
export interface APIState {
|
||||||
client: KilovoltWS;
|
client: KilovoltWS;
|
||||||
connected: boolean;
|
connectionStatus: ConnectionStatus;
|
||||||
|
kvError: kvError;
|
||||||
initialLoadComplete: boolean;
|
initialLoadComplete: boolean;
|
||||||
loyalty: {
|
loyalty: {
|
||||||
users: LoyaltyStorage;
|
users: LoyaltyStorage;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Link, Redirect, Router, useLocation } from '@reach/router';
|
import { Link, Redirect, Router, useLocation } from '@reach/router';
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
@ -23,6 +23,8 @@ import TwitchBotModulesPage from './pages/twitch/Modules';
|
||||||
import StulbeConfigPage from './pages/stulbe/Config';
|
import StulbeConfigPage from './pages/stulbe/Config';
|
||||||
import StulbeWebhooksPage from './pages/stulbe/Webhook';
|
import StulbeWebhooksPage from './pages/stulbe/Webhook';
|
||||||
import TwitchBotTimersPage from './pages/twitch/Timers';
|
import TwitchBotTimersPage from './pages/twitch/Timers';
|
||||||
|
import { ConnectionStatus } from '../store/api/types';
|
||||||
|
import Field from './components/Field';
|
||||||
|
|
||||||
interface RouteItem {
|
interface RouteItem {
|
||||||
name?: string;
|
name?: string;
|
||||||
|
@ -59,27 +61,86 @@ const menu: RouteItem[] = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function AuthModal(): React.ReactElement {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
const inputRef = useRef<HTMLInputElement>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (inputRef?.current) {
|
||||||
|
inputRef.current.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const submit = () => {
|
||||||
|
localStorage.setItem('password', password);
|
||||||
|
window.location.reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="modal is-active">
|
||||||
|
<div className="modal-background"></div>
|
||||||
|
<div className="modal-card">
|
||||||
|
<header className="modal-card-head">
|
||||||
|
<p className="modal-card-title">{t('auth.header')}</p>
|
||||||
|
</header>
|
||||||
|
<section className="modal-card-body">
|
||||||
|
<Field>{t('auth.message')}</Field>
|
||||||
|
<Field>
|
||||||
|
<input
|
||||||
|
className="input"
|
||||||
|
type="password"
|
||||||
|
placeholder={t('auth.placeholder')}
|
||||||
|
value={password ?? ''}
|
||||||
|
ref={inputRef}
|
||||||
|
onChange={(ev) => setPassword(ev.target.value)}
|
||||||
|
onKeyUp={(ev) => {
|
||||||
|
if (ev.key === 'Enter' || ev.code === 'Enter') {
|
||||||
|
submit();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
</section>
|
||||||
|
<footer className="modal-card-foot">
|
||||||
|
<button className="button is-success" onClick={() => submit()}>
|
||||||
|
{t('auth.button')}
|
||||||
|
</button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function App(): React.ReactElement {
|
export default function App(): React.ReactElement {
|
||||||
const loc = useLocation();
|
const loc = useLocation();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const client = useSelector((state: RootState) => state.api.client);
|
const client = useSelector((state: RootState) => state.api.client);
|
||||||
const connected = useSelector((state: RootState) => state.api.connected);
|
const connected = useSelector(
|
||||||
|
(state: RootState) => state.api.connectionStatus,
|
||||||
|
);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
// Create WS client
|
// Create WS client
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!client) {
|
if (!client) {
|
||||||
dispatch(
|
dispatch(
|
||||||
createWSClient(
|
createWSClient({
|
||||||
process.env.NODE_ENV === 'development'
|
address:
|
||||||
? 'ws://localhost:4337/ws'
|
process.env.NODE_ENV === 'development'
|
||||||
: `ws://${loc.host}/ws`,
|
? 'ws://localhost:4337/ws'
|
||||||
),
|
: `ws://${loc.host}/ws`,
|
||||||
|
password: localStorage.password,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
if (connected === ConnectionStatus.AuthenticationNeeded) {
|
||||||
|
return <AuthModal />;
|
||||||
|
}
|
||||||
|
|
||||||
if (!client) {
|
if (!client) {
|
||||||
return <div className="container">{t('system.loading')}</div>;
|
return <div className="container">{t('system.loading')}</div>;
|
||||||
}
|
}
|
||||||
|
@ -108,7 +169,7 @@ export default function App(): React.ReactElement {
|
||||||
return (
|
return (
|
||||||
<section className="main-content columns is-fullheight">
|
<section className="main-content columns is-fullheight">
|
||||||
<section className="notifications">
|
<section className="notifications">
|
||||||
{!connected ? (
|
{connected !== ConnectionStatus.Connected ? (
|
||||||
<div className="notification is-danger">
|
<div className="notification is-danger">
|
||||||
{t('system.connection-lost')}
|
{t('system.connection-lost')}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { RouteComponentProps } from '@reach/router';
|
import { RouteComponentProps } from '@reach/router';
|
||||||
import React, { useEffect } from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import { useModule } from '../../lib/react-utils';
|
import { useModule } from '../../lib/react-utils';
|
||||||
import apiReducer, { modules } from '../../store/api/reducer';
|
import apiReducer, { modules } from '../../store/api/reducer';
|
||||||
|
import Field from '../components/Field';
|
||||||
|
|
||||||
export default function HTTPPage(
|
export default function HTTPPage(
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
@ -19,8 +20,7 @@ export default function HTTPPage(
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h1 className="title is-4">{t('http.header')}</h1>
|
<h1 className="title is-4">{t('http.header')}</h1>
|
||||||
<div className="field">
|
<Field name={t('http.server-bind')}>
|
||||||
<label className="label">{t('http.server-bind')}</label>
|
|
||||||
<p className="control">
|
<p className="control">
|
||||||
<input
|
<input
|
||||||
disabled={busy}
|
disabled={busy}
|
||||||
|
@ -38,9 +38,28 @@ export default function HTTPPage(
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</Field>
|
||||||
<label className="label">{t('http.static-content')}</label>
|
<Field name={t('http.kv-password')}>
|
||||||
<div className="field">
|
<p className="control">
|
||||||
|
<input
|
||||||
|
className="input"
|
||||||
|
type="password"
|
||||||
|
disabled={busy}
|
||||||
|
placeholder="None"
|
||||||
|
value={httpConfig?.kv_password ?? ''}
|
||||||
|
onChange={(ev) =>
|
||||||
|
dispatch(
|
||||||
|
apiReducer.actions.httpConfigChanged({
|
||||||
|
...httpConfig,
|
||||||
|
kv_password: ev.target.value,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<p className="help">Leave empty to disable authentication</p>
|
||||||
|
</Field>
|
||||||
|
<Field name={t('http.static-content')}>
|
||||||
<label className="checkbox">
|
<label className="checkbox">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
|
@ -57,26 +76,27 @@ export default function HTTPPage(
|
||||||
/>{' '}
|
/>{' '}
|
||||||
{t('http.enable-static')}
|
{t('http.enable-static')}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</Field>
|
||||||
<div className="field">
|
{active && (
|
||||||
<label className="label">{t('http.static-root-path')}</label>
|
<Field name={t('http.static-root-path')}>
|
||||||
<p className="control">
|
<p className="control">
|
||||||
<input
|
<input
|
||||||
className="input"
|
className="input"
|
||||||
type="text"
|
type="text"
|
||||||
disabled={busy || !active}
|
disabled={busy || !active}
|
||||||
value={httpConfig?.path ?? ''}
|
value={httpConfig?.path ?? ''}
|
||||||
onChange={(ev) =>
|
onChange={(ev) =>
|
||||||
dispatch(
|
dispatch(
|
||||||
apiReducer.actions.httpConfigChanged({
|
apiReducer.actions.httpConfigChanged({
|
||||||
...httpConfig,
|
...httpConfig,
|
||||||
path: ev.target.value,
|
path: ev.target.value,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</Field>
|
||||||
|
)}
|
||||||
<button
|
<button
|
||||||
className="button"
|
className="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
6
go.mod
6
go.mod
|
@ -17,7 +17,7 @@ require (
|
||||||
github.com/nicklaw5/helix v1.25.0
|
github.com/nicklaw5/helix v1.25.0
|
||||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4
|
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/strimertul/kilovolt/v5 v5.0.1
|
github.com/strimertul/kilovolt/v6 v6.0.1
|
||||||
github.com/strimertul/stulbe v0.4.3
|
github.com/strimertul/stulbe v0.5.1
|
||||||
github.com/strimertul/stulbe-client-go v0.4.0
|
github.com/strimertul/stulbe-client-go v0.5.0
|
||||||
)
|
)
|
||||||
|
|
14
go.sum
14
go.sum
|
@ -90,6 +90,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLD
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/nicklaw5/helix v1.13.1/go.mod h1:XeeXY7oY5W+MVMu6wF4qGm8uvjZ1/Nss0FqprVkXKrg=
|
github.com/nicklaw5/helix v1.13.1/go.mod h1:XeeXY7oY5W+MVMu6wF4qGm8uvjZ1/Nss0FqprVkXKrg=
|
||||||
github.com/nicklaw5/helix v1.15.0/go.mod h1:XeeXY7oY5W+MVMu6wF4qGm8uvjZ1/Nss0FqprVkXKrg=
|
github.com/nicklaw5/helix v1.15.0/go.mod h1:XeeXY7oY5W+MVMu6wF4qGm8uvjZ1/Nss0FqprVkXKrg=
|
||||||
|
github.com/nicklaw5/helix v1.24.2/go.mod h1:XeeXY7oY5W+MVMu6wF4qGm8uvjZ1/Nss0FqprVkXKrg=
|
||||||
github.com/nicklaw5/helix v1.25.0 h1:Mrz537izZVsGdM3I46uGAAlslj61frgkhS/9xQqyT/M=
|
github.com/nicklaw5/helix v1.25.0 h1:Mrz537izZVsGdM3I46uGAAlslj61frgkhS/9xQqyT/M=
|
||||||
github.com/nicklaw5/helix v1.25.0/go.mod h1:yvXZFapT6afIoxnAvlWiJiUMsYnoHl7tNs+t0bloAMw=
|
github.com/nicklaw5/helix v1.25.0/go.mod h1:yvXZFapT6afIoxnAvlWiJiUMsYnoHl7tNs+t0bloAMw=
|
||||||
github.com/orcaman/concurrent-map v0.0.0-20210501183033-44dafcb38ecc h1:Ak86L+yDSOzKFa7WM5bf5itSOo1e3Xh8bm5YCMUXIjQ=
|
github.com/orcaman/concurrent-map v0.0.0-20210501183033-44dafcb38ecc h1:Ak86L+yDSOzKFa7WM5bf5itSOo1e3Xh8bm5YCMUXIjQ=
|
||||||
|
@ -129,16 +130,17 @@ github.com/strimertul/kilovolt-client-go/v2 v2.0.0/go.mod h1:y98V8uVMiJZhnBLtSVk
|
||||||
github.com/strimertul/kilovolt/v3 v3.0.0/go.mod h1:AgfPYRp+kffN64tcqCcQUZdpL/Dm5DGHIYRDm9t3E0Y=
|
github.com/strimertul/kilovolt/v3 v3.0.0/go.mod h1:AgfPYRp+kffN64tcqCcQUZdpL/Dm5DGHIYRDm9t3E0Y=
|
||||||
github.com/strimertul/kilovolt/v4 v4.0.1 h1:81isohdSixVURO2+dZKKZBPw97HJmNN4/BXn6ADFoWM=
|
github.com/strimertul/kilovolt/v4 v4.0.1 h1:81isohdSixVURO2+dZKKZBPw97HJmNN4/BXn6ADFoWM=
|
||||||
github.com/strimertul/kilovolt/v4 v4.0.1/go.mod h1:AO2ZFQtSB+AcjCw0RTkXjbM6XBAjhsXsrRq10BX95kw=
|
github.com/strimertul/kilovolt/v4 v4.0.1/go.mod h1:AO2ZFQtSB+AcjCw0RTkXjbM6XBAjhsXsrRq10BX95kw=
|
||||||
github.com/strimertul/kilovolt/v5 v5.0.1 h1:LHAVqb3SrXiew3loTpYuPdz16Nl8/aTReBYj56xwF7I=
|
github.com/strimertul/kilovolt/v6 v6.0.0/go.mod h1:O5Rwg8o66omRP4O3qInBKreW9jILZz2MEq4MuotzAXw=
|
||||||
github.com/strimertul/kilovolt/v5 v5.0.1/go.mod h1:HxfnnlEGhY6p+Im9U7pso07HEV+cXEsJH7uFTM7c6uE=
|
github.com/strimertul/kilovolt/v6 v6.0.1 h1:CNiaRsh0wWtZ3yQlz+9RZAtJr3m14ODIa9WQt7NyQ40=
|
||||||
|
github.com/strimertul/kilovolt/v6 v6.0.1/go.mod h1:O5Rwg8o66omRP4O3qInBKreW9jILZz2MEq4MuotzAXw=
|
||||||
github.com/strimertul/strimertul v1.3.0/go.mod h1:1pSe9zVWF4BYt56ii1Hg+xTxvtfqfvT4FQ7bYffWsUA=
|
github.com/strimertul/strimertul v1.3.0/go.mod h1:1pSe9zVWF4BYt56ii1Hg+xTxvtfqfvT4FQ7bYffWsUA=
|
||||||
github.com/strimertul/stulbe v0.2.5/go.mod h1:0AsY4OVf1dNCwOn9s7KySuAxJ85w88pXeostu1n9E7w=
|
github.com/strimertul/stulbe v0.2.5/go.mod h1:0AsY4OVf1dNCwOn9s7KySuAxJ85w88pXeostu1n9E7w=
|
||||||
github.com/strimertul/stulbe v0.4.0/go.mod h1:Pb0UQCKdyES7UKSKm2i2g9parkgXSJAFeMH/LSOSbgQ=
|
github.com/strimertul/stulbe v0.4.0/go.mod h1:Pb0UQCKdyES7UKSKm2i2g9parkgXSJAFeMH/LSOSbgQ=
|
||||||
github.com/strimertul/stulbe v0.4.3 h1:apDTvFaChCoMxUokc1Y51Wn43hWyf0qqMNkptnlIj/c=
|
github.com/strimertul/stulbe v0.5.1 h1:CY3s/Vv6I5YkmLcFE3OsY+ysjw+7HywcMcW4EuE6Ejo=
|
||||||
github.com/strimertul/stulbe v0.4.3/go.mod h1:Pb0UQCKdyES7UKSKm2i2g9parkgXSJAFeMH/LSOSbgQ=
|
github.com/strimertul/stulbe v0.5.1/go.mod h1:fj25VOPKQH2IYcrmjr+bkWffpTiKd7O3ixbe8ZiwbzQ=
|
||||||
github.com/strimertul/stulbe-client-go v0.1.0/go.mod h1:KtfuDhxCHZ9DCFHnrBOHqb2Pu9zoj+EqA8ZRIUqLD/w=
|
github.com/strimertul/stulbe-client-go v0.1.0/go.mod h1:KtfuDhxCHZ9DCFHnrBOHqb2Pu9zoj+EqA8ZRIUqLD/w=
|
||||||
github.com/strimertul/stulbe-client-go v0.4.0 h1:9DEHnbjU452qFQaK9ilrzydEirpVxwVeiCz7T0kZxEk=
|
github.com/strimertul/stulbe-client-go v0.5.0 h1:tci0uZ4AiNXb4izCjxL2/qAh1kmR8D74ltZJojgP7Ik=
|
||||||
github.com/strimertul/stulbe-client-go v0.4.0/go.mod h1:Ssz1mEEWt4y7yj6Dm4aCaEV651Tzz9M+suzhWzC2QqQ=
|
github.com/strimertul/stulbe-client-go v0.5.0/go.mod h1:Ssz1mEEWt4y7yj6Dm4aCaEV651Tzz9M+suzhWzC2QqQ=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.0/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
github.com/twitchyliquid64/golang-asm v0.15.0/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||||
|
|
8
main.go
8
main.go
|
@ -13,8 +13,6 @@ import (
|
||||||
|
|
||||||
"github.com/strimertul/strimertul/modules/http"
|
"github.com/strimertul/strimertul/modules/http"
|
||||||
|
|
||||||
kv "github.com/strimertul/kilovolt/v5"
|
|
||||||
|
|
||||||
"github.com/strimertul/strimertul/database"
|
"github.com/strimertul/strimertul/database"
|
||||||
"github.com/strimertul/strimertul/modules"
|
"github.com/strimertul/strimertul/modules"
|
||||||
"github.com/strimertul/strimertul/modules/loyalty"
|
"github.com/strimertul/strimertul/modules/loyalty"
|
||||||
|
@ -118,11 +116,6 @@ func main() {
|
||||||
fmt.Printf("It appears this is your first time running %s! Please go to http://%s and make sure to configure anything you want!\n\n", AppTitle, DefaultBind)
|
fmt.Printf("It appears this is your first time running %s! Please go to http://%s and make sure to configure anything you want!\n\n", AppTitle, DefaultBind)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize KV (required)
|
|
||||||
hub, err := kv.NewHub(db.Client(), wrapLogger("kv"))
|
|
||||||
failOnError(err, "Could not initialize kilovolt hub")
|
|
||||||
go hub.Run()
|
|
||||||
|
|
||||||
// Get Stulbe config, if enabled
|
// Get Stulbe config, if enabled
|
||||||
var stulbeManager *stulbe.Manager = nil
|
var stulbeManager *stulbe.Manager = nil
|
||||||
if moduleConfig.EnableStulbe {
|
if moduleConfig.EnableStulbe {
|
||||||
|
@ -185,7 +178,6 @@ func main() {
|
||||||
|
|
||||||
fedir, _ := fs.Sub(frontend, "frontend/dist")
|
fedir, _ := fs.Sub(frontend, "frontend/dist")
|
||||||
httpServer.SetFrontend(fedir)
|
httpServer.SetFrontend(fedir)
|
||||||
httpServer.SetHub(hub)
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(time.Second) // THIS IS STUPID
|
time.Sleep(time.Second) // THIS IS STUPID
|
||||||
|
|
|
@ -6,4 +6,5 @@ type ServerConfig struct {
|
||||||
Bind string `json:"bind"`
|
Bind string `json:"bind"`
|
||||||
EnableStaticServer bool `json:"enable_static_server"`
|
EnableStaticServer bool `json:"enable_static_server"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
|
KVPassword string `json:"kv_password"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
kv "github.com/strimertul/kilovolt/v5"
|
kv "github.com/strimertul/kilovolt/v6"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
@ -35,18 +35,25 @@ func NewServer(db *database.DB, log logrus.FieldLogger) (*Server, error) {
|
||||||
server: &http.Server{},
|
server: &http.Server{},
|
||||||
}
|
}
|
||||||
err := db.GetJSON(ServerConfigKey, &server.Config)
|
err := db.GetJSON(ServerConfigKey, &server.Config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return server, err
|
server.hub, err = kv.NewHub(db.Client(), kv.HubOptions{
|
||||||
|
Password: server.Config.KVPassword,
|
||||||
|
}, log.WithField("module", "kv"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
go server.hub.Run()
|
||||||
|
|
||||||
|
return server, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) SetFrontend(files fs.FS) {
|
func (s *Server) SetFrontend(files fs.FS) {
|
||||||
s.frontend = files
|
s.frontend = files
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) SetHub(hub *kv.Hub) {
|
|
||||||
s.hub = hub
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) makeMux() *http.ServeMux {
|
func (s *Server) makeMux() *http.ServeMux {
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
|
|
||||||
|
@ -74,11 +81,19 @@ func (s *Server) Listen() error {
|
||||||
for _, pair := range changed {
|
for _, pair := range changed {
|
||||||
if pair.Key == ServerConfigKey {
|
if pair.Key == ServerConfigKey {
|
||||||
oldBind := s.Config.Bind
|
oldBind := s.Config.Bind
|
||||||
|
oldPassword := s.Config.KVPassword
|
||||||
err := s.db.GetJSON(ServerConfigKey, &s.Config)
|
err := s.db.GetJSON(ServerConfigKey, &s.Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.mux = s.makeMux()
|
s.mux = s.makeMux()
|
||||||
|
// Restart hub if password changed
|
||||||
|
if oldPassword != s.Config.KVPassword {
|
||||||
|
s.hub.SetOptions(kv.HubOptions{
|
||||||
|
Password: s.Config.KVPassword,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Restart server if bind changed
|
||||||
if oldBind != s.Config.Bind {
|
if oldBind != s.Config.Bind {
|
||||||
restart.Set(true)
|
restart.Set(true)
|
||||||
err = s.server.Shutdown(context.Background())
|
err = s.server.Shutdown(context.Background())
|
||||||
|
|
Loading…
Reference in a new issue