1
0
Fork 0
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:
Ash Keel 2021-11-21 22:36:48 +01:00
parent 27a7ede45e
commit 8e02ba6fb7
No known key found for this signature in database
GPG key ID: BAD8D93E7314ED3E
13 changed files with 505 additions and 12399 deletions

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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",

View file

@ -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"
} }
} }

View file

@ -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;

View file

@ -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;

View file

@ -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>

View file

@ -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
View file

@ -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
View file

@ -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=

View file

@ -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

View file

@ -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"`
} }

View file

@ -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())