Compare commits
2 Commits
94a821d657
...
87b187c8f1
Author | SHA1 | Date | |
---|---|---|---|
87b187c8f1 | |||
4a0dc5b87a |
visions
server/src
ui
Taskfile.ymlpackage-lock.jsonpackage.json
src
@ -1,7 +1,7 @@
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use async_std::sync::RwLock;
|
||||
use chrono::{DateTime, TimeDelta, Utc};
|
||||
use chrono::{DateTime, Duration, TimeDelta, Utc};
|
||||
use mime::Mime;
|
||||
use result_extended::{error, fatal, ok, result_as_fatal, return_error, ResultExt};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -165,7 +165,7 @@ impl Core {
|
||||
Some(_) => error(AppError::UsernameUnavailable),
|
||||
None => match state
|
||||
.db
|
||||
.create_user(username, "", false, AccountState::PasswordReset(Utc::now()))
|
||||
.create_user(username, "", false, AccountState::PasswordReset(Utc::now() + Duration::minutes(60)))
|
||||
.await
|
||||
{
|
||||
Ok(user_id) => ok(user_id),
|
||||
@ -341,6 +341,7 @@ impl Core {
|
||||
) -> ResultExt<AuthResponse, AppError, FatalError> {
|
||||
let now = Utc::now();
|
||||
let state = self.0.read().await;
|
||||
let user = state.db.user_by_username(username).await.unwrap().unwrap();
|
||||
let user_info = return_error!(match state.db.user_by_username(username).await {
|
||||
Ok(Some(row)) if row.password == password => ok(row),
|
||||
Ok(_) => error(AppError::AuthFailed),
|
||||
@ -375,6 +376,14 @@ impl Core {
|
||||
Err(fatal_error) => fatal(fatal_error),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn delete_session(&self, session_id: &SessionId) -> ResultExt<(), AppError, FatalError> {
|
||||
let state = self.0.read().await;
|
||||
match state.db.delete_session(session_id).await {
|
||||
Ok(_) => ok(()),
|
||||
Err(err) => fatal(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_expiration_date() -> DateTime<Utc> {
|
||||
|
@ -237,6 +237,21 @@ impl DiskDb {
|
||||
}
|
||||
}
|
||||
|
||||
fn delete_session(&self, session_id: &SessionId) -> Result<(), FatalError> {
|
||||
match self.session(session_id) {
|
||||
Ok(Some(_)) => {
|
||||
let mut stmt = self.conn.prepare("DELETE FROM sessions WHERE id = ?")
|
||||
.map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?;
|
||||
|
||||
let session_id = SessionId::new();
|
||||
stmt.execute((session_id.as_str(),)).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
Ok(None) => Err(FatalError::DatabaseKeyMissing),
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn character(&self, id: CharacterId) -> Result<Option<CharsheetRow>, FatalError> {
|
||||
let mut stmt = self
|
||||
.conn
|
||||
@ -316,6 +331,10 @@ pub async fn db_handler(db: DiskDb, requestor: Receiver<DatabaseRequest>) {
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
Request::DeleteSession(id) => {
|
||||
db.delete_session(&id).unwrap();
|
||||
tx.send(DatabaseResponse::DeleteSession).await.unwrap();
|
||||
}
|
||||
Request::Games => match db.games() {
|
||||
Ok(games) => tx.send(DatabaseResponse::Games(games)).await.unwrap(),
|
||||
_ => unimplemented!("errors for Request::Games"),
|
||||
|
@ -16,6 +16,7 @@ enum Request {
|
||||
CreateGame(UserId, String, String),
|
||||
CreateSession(UserId),
|
||||
CreateUser(String, String, bool, AccountState),
|
||||
DeleteSession(SessionId),
|
||||
Game(GameId),
|
||||
Games,
|
||||
SaveGame(Game),
|
||||
@ -36,6 +37,7 @@ struct DatabaseRequest {
|
||||
enum DatabaseResponse {
|
||||
Charsheet(Option<CharsheetRow>),
|
||||
CreateSession(SessionId),
|
||||
DeleteSession,
|
||||
Games(Vec<Game>),
|
||||
Game(Option<Game>),
|
||||
SaveGame(GameId),
|
||||
@ -49,6 +51,7 @@ enum DatabaseResponse {
|
||||
pub trait Database: Send + Sync {
|
||||
async fn create_session(&self, id: &UserId) -> Result<SessionId, FatalError>;
|
||||
async fn session(&self, id: &SessionId) -> Result<Option<User>, FatalError>;
|
||||
async fn delete_session(&self, id: &SessionId) -> Result<(), FatalError>;
|
||||
|
||||
async fn character(&self, id: &CharacterId) -> Result<Option<CharsheetRow>, FatalError>;
|
||||
|
||||
@ -125,6 +128,9 @@ impl Database for DbConn {
|
||||
async fn session(&self, id: &SessionId) -> Result<Option<User>, FatalError> {
|
||||
send_request!(self, Request::Session(id.to_owned()), DatabaseResponse::Session(row) => Ok(row))
|
||||
}
|
||||
async fn delete_session(&self, id: &SessionId) -> Result<(), FatalError> {
|
||||
send_request!(self, Request::DeleteSession(id.to_owned()), DatabaseResponse::DeleteSession => Ok(()))
|
||||
}
|
||||
async fn character(&self, id: &CharacterId) -> Result<Option<CharsheetRow>, FatalError> {
|
||||
send_request!(self, Request::Charsheet(id.to_owned()), DatabaseResponse::Charsheet(row) => Ok(row))
|
||||
}
|
||||
|
@ -38,10 +38,7 @@ pub struct SetAdminPasswordRequest {
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
async fn check_session(
|
||||
core: &Core,
|
||||
headers: HeaderMap,
|
||||
) -> ResultExt<Option<User>, AppError, FatalError> {
|
||||
fn parse_session_header(headers: HeaderMap) -> ResultExt<Option<SessionId>, AppError, FatalError> {
|
||||
match headers.get("Authorization") {
|
||||
Some(token) => {
|
||||
println!("check_session: {:?}", token);
|
||||
@ -52,7 +49,7 @@ async fn check_session(
|
||||
.collect::<Vec<&str>>()
|
||||
.as_slice()
|
||||
{
|
||||
[_schema, token] => core.session(&SessionId::from(token.to_owned())).await,
|
||||
[_schema, token] => ok(Some(SessionId::from(*token))),
|
||||
_ => error(AppError::BadRequest),
|
||||
}
|
||||
}
|
||||
@ -60,6 +57,16 @@ async fn check_session(
|
||||
}
|
||||
}
|
||||
|
||||
async fn check_session(
|
||||
core: &Core,
|
||||
headers: HeaderMap,
|
||||
) -> ResultExt<Option<User>, AppError, FatalError> {
|
||||
match return_error!(parse_session_header(headers)) {
|
||||
Some(session_id) => core.session(&session_id).await,
|
||||
None => ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn auth_required<F, A, Fut>(
|
||||
core: Core,
|
||||
headers: HeaderMap,
|
||||
@ -101,7 +108,27 @@ pub async fn check_password(
|
||||
req: Json<AuthRequest>,
|
||||
) -> ResultExt<AuthResponse, AppError, FatalError> {
|
||||
let Json(AuthRequest { username, password }) = req;
|
||||
core.auth(&username, &password).await
|
||||
println!("check_password: {} {}", username, password);
|
||||
let result = core.auth(&username, &password).await;
|
||||
println!("auth result: {:?}", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
pub async fn delete_session(core: Core, headers: HeaderMap,) -> ResultExt<(), AppError, FatalError> {
|
||||
/*
|
||||
auth_required(core.clone(), headers, |user| async move {
|
||||
match user_id {
|
||||
Some(user_id) => core.delete_session
|
||||
None => (),
|
||||
}
|
||||
}).await
|
||||
*/
|
||||
|
||||
match return_error!(parse_session_header(headers)) {
|
||||
Some(session_id) => core.delete_session(&session_id).await,
|
||||
None => error(AppError::AuthFailed),
|
||||
}
|
||||
// await core.delete_session(session_id);
|
||||
}
|
||||
|
||||
pub async fn get_user(
|
||||
|
@ -13,8 +13,7 @@ use crate::{
|
||||
core::Core,
|
||||
database::UserId,
|
||||
handlers::{
|
||||
check_password, create_game, create_user, get_user, get_users, healthcheck, set_password,
|
||||
wrap_handler, AuthRequest, CreateGameRequest, CreateUserRequest, SetPasswordRequest,
|
||||
check_password, create_game, create_user, delete_session, get_user, get_users, healthcheck, set_password, wrap_handler, AuthRequest, CreateGameRequest, CreateUserRequest, SetPasswordRequest
|
||||
},
|
||||
};
|
||||
|
||||
@ -38,10 +37,14 @@ pub fn routes(core: Core) -> Router {
|
||||
let core = core.clone();
|
||||
move |req: Json<AuthRequest>| wrap_handler(|| check_password(core, req))
|
||||
})
|
||||
.delete({
|
||||
let core = core.clone();
|
||||
move |headers: HeaderMap| wrap_handler(|| delete_session(core, headers))
|
||||
})
|
||||
.layer(
|
||||
CorsLayer::new()
|
||||
.allow_methods([Method::POST])
|
||||
.allow_headers([CONTENT_TYPE])
|
||||
.allow_methods([Method::DELETE, Method::POST])
|
||||
.allow_headers([AUTHORIZATION, CONTENT_TYPE])
|
||||
.allow_origin(Any),
|
||||
),
|
||||
)
|
||||
@ -93,7 +96,13 @@ pub fn routes(core: Core) -> Router {
|
||||
let Json(req) = req;
|
||||
wrap_handler(|| set_password(core, headers, req))
|
||||
}
|
||||
}),
|
||||
})
|
||||
.layer(
|
||||
CorsLayer::new()
|
||||
.allow_methods([Method::PUT])
|
||||
.allow_headers([AUTHORIZATION, CONTENT_TYPE])
|
||||
.allow_origin(Any),
|
||||
),
|
||||
)
|
||||
.route(
|
||||
"/api/v1/user/:user_id",
|
||||
|
@ -7,3 +7,8 @@ tasks:
|
||||
- npm install
|
||||
- npm run start
|
||||
|
||||
test:
|
||||
cmds:
|
||||
- cd ../visions-types && task build
|
||||
- npm install
|
||||
- npm run test
|
||||
|
4319
visions/ui/package-lock.json
generated
4319
visions/ui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -3,9 +3,10 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^5.17.0",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
"@testing-library/react": "^16.2.0",
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/node": "^16.18.119",
|
||||
"@types/react": "^18.3.12",
|
||||
|
@ -1,9 +0,0 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import App from './App';
|
||||
|
||||
test('renders learn react link', () => {
|
||||
// render(<App />);
|
||||
const linkElement = screen.getByText(/learn react/i);
|
||||
expect(linkElement).toBeInTheDocument();
|
||||
});
|
@ -15,6 +15,7 @@ interface AppProps {
|
||||
client: Client
|
||||
}
|
||||
|
||||
/*
|
||||
const CandelaCharsheet = ({ client }: { client: Client }) => {
|
||||
let [sheet, setSheet] = useState(undefined)
|
||||
useEffect(
|
||||
@ -24,21 +25,7 @@ const CandelaCharsheet = ({ client }: { client: Client }) => {
|
||||
|
||||
return sheet ? <Candela.CharsheetElement sheet={sheet} /> : <div> </div>
|
||||
}
|
||||
|
||||
interface AuthedViewProps {
|
||||
client: Client
|
||||
}
|
||||
|
||||
const AuthedView = ({ client, children }: PropsWithChildren<AuthedViewProps>) => {
|
||||
const [state, manager] = useContext(StateContext)
|
||||
return (
|
||||
<Authentication onSetPassword={(password1, password2) => {
|
||||
manager.setPassword(password1, password2)
|
||||
}} onAuth={(username, password) => manager.auth(username, password)}>
|
||||
{children}
|
||||
</Authentication>
|
||||
)
|
||||
}
|
||||
*/
|
||||
|
||||
const App = ({ client }: AppProps) => {
|
||||
console.log("rendering app")
|
||||
@ -64,10 +51,12 @@ const App = ({ client }: AppProps) => {
|
||||
path: "/admin",
|
||||
element: <AdminView client={client} />
|
||||
},
|
||||
/*
|
||||
{
|
||||
path: "/candela",
|
||||
element: <CandelaCharsheet client={client} />
|
||||
},
|
||||
*/
|
||||
{
|
||||
path: "/design",
|
||||
element: <DesignPage />
|
||||
|
@ -4,7 +4,17 @@ export type PlayingField = {
|
||||
backgroundImage: string;
|
||||
}
|
||||
|
||||
export class Client {
|
||||
export type ServerResponse<A> = { type: "Unauthorized" } | { type: "Unexpected", status: number } | A;
|
||||
|
||||
export interface Client {
|
||||
users: (sessionId: SessionId) => Promise<Array<UserOverview>>;
|
||||
createUser: (sessionId: SessionId, username: string) => Promise<UserId>;
|
||||
auth: (username: string, password: string) => Promise<ServerResponse<AuthResponse>>;
|
||||
setPassword: (sessionId: SessionId, password_1: string, password_2: string) => Promise<void>;
|
||||
logout: (sessionId: SessionId) => Promise<void>;
|
||||
}
|
||||
|
||||
export class Connection implements Client {
|
||||
private base: URL;
|
||||
private sessionId: string | undefined;
|
||||
|
||||
@ -48,7 +58,7 @@ export class Client {
|
||||
return fetch(url, { method: 'PUT', headers: [['Content-Type', 'application/json']], body: JSON.stringify(name) });
|
||||
}
|
||||
|
||||
async users(sessionId: string) {
|
||||
async users(sessionId: string): Promise<Array<UserOverview>> {
|
||||
const url = new URL(this.base);
|
||||
url.pathname = '/api/v1/users';
|
||||
return fetch(url, {
|
||||
@ -63,29 +73,31 @@ export class Client {
|
||||
return fetch(url).then((response) => response.json());
|
||||
}
|
||||
|
||||
async createUser(sessionId: string, username: string) {
|
||||
async createUser(sessionId: string, username: string): Promise<UserId> {
|
||||
const url = new URL(this.base);
|
||||
url.pathname = '/api/v1/user';
|
||||
return fetch(url, {
|
||||
const response: Response = await fetch(url, {
|
||||
method: 'PUT',
|
||||
headers: [['Authorization', `Bearer: ${sessionId}`],
|
||||
['Content-Type', 'application/json']],
|
||||
headers: [['Authorization', `Bearer ${sessionId}`],
|
||||
['Content-Type', 'application/json']],
|
||||
body: JSON.stringify({ username }),
|
||||
}).then((response) => response.json())
|
||||
});
|
||||
const userId: UserId = await response.json();
|
||||
return userId;
|
||||
}
|
||||
|
||||
async setPassword(password_1: string, password_2: string) {
|
||||
async setPassword(sessionId: string, password_1: string, password_2: string) {
|
||||
const url = new URL(this.base);
|
||||
url.pathname = `/api/v1/user/password`;
|
||||
|
||||
return fetch(url, {
|
||||
method: 'PUT', headers: [['Content-Type', 'application/json']], body: JSON.stringify({
|
||||
await fetch(url, {
|
||||
method: 'PUT', headers: [['Authorization', `Bearer ${sessionId}`], ['Content-Type', 'application/json']], body: JSON.stringify({
|
||||
password_1, password_2,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
async auth(username: string, password: string): Promise<AuthResponse> {
|
||||
async auth(username: string, password: string): Promise<ServerResponse<AuthResponse>> {
|
||||
const url = new URL(this.base);
|
||||
url.pathname = `/api/v1/auth`
|
||||
const response = await fetch(url, {
|
||||
@ -93,7 +105,22 @@ export class Client {
|
||||
headers: [['Content-Type', 'application/json']],
|
||||
body: JSON.stringify({ 'username': username, 'password': password })
|
||||
});
|
||||
return await response.json();
|
||||
if (response.ok) {
|
||||
return await response.json();
|
||||
} else if (response.status == 401) {
|
||||
return await response.json().then(() => ({ type: "Unauthorized" }));
|
||||
} else {
|
||||
return await response.json().then(() => ({ type: "Unexpected", status: response.status }));
|
||||
}
|
||||
}
|
||||
|
||||
async logout(sessionId: string) {
|
||||
const url = new URL(this.base);
|
||||
url.pathname = `/api/v1/auth`
|
||||
await fetch(url, {
|
||||
method: 'DELETE',
|
||||
headers: [['Authorization', `Bearer ${sessionId}`]],
|
||||
});
|
||||
}
|
||||
|
||||
async profile(sessionId: SessionId, userId: UserId | undefined): Promise<UserOverview | undefined> {
|
||||
|
@ -3,9 +3,9 @@ import ReactDOM from 'react-dom/client';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
import { Client } from './client';
|
||||
import { Connection } from './client';
|
||||
|
||||
const client = new Client();
|
||||
const client = new Connection();
|
||||
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById('root') as HTMLElement
|
||||
|
@ -0,0 +1,52 @@
|
||||
import { act, fireEvent, render, screen } from "@testing-library/react";
|
||||
import { UserOverview, AuthResponse, SessionId, UserId } from "visions-types";
|
||||
import { Client, ServerResponse } from "../../client";
|
||||
import { StateProvider } from "./StateProvider";
|
||||
import { AuthedView, Authentication } from '../../views/Authentication/Authentication';
|
||||
|
||||
class MockClient implements Client {
|
||||
validUsers: {[username: string]: string};
|
||||
constructor() {
|
||||
this.validUsers = { "vakarian": "aoeu", "shephard": "rubbish" }
|
||||
}
|
||||
|
||||
async users(sessionId: SessionId): Promise<UserOverview[]> {
|
||||
return [];
|
||||
}
|
||||
|
||||
async createUser(sessionId: SessionId, username: string): Promise<UserId> {
|
||||
return "abcdefg";
|
||||
}
|
||||
|
||||
async setPassword(sessionId: SessionId, password_1: string, password_2: string): Promise<void> {
|
||||
}
|
||||
|
||||
async auth(username: string, password: string): Promise<ServerResponse<AuthResponse>> {
|
||||
if (this.validUsers[username] === password) {
|
||||
return { type: "Success", content: "session-id" };
|
||||
} else {
|
||||
return { type: "Unauthorized" };
|
||||
}
|
||||
}
|
||||
|
||||
async logout(sessionId: SessionId): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
test('a user is able to authenticate', async () => {
|
||||
const client = new MockClient;
|
||||
render(<StateProvider client={client}>
|
||||
<AuthedView>
|
||||
<p>Hi, authentication complete</p>
|
||||
</AuthedView>
|
||||
</StateProvider>);
|
||||
expect(screen.getByText(/Welcome to Visions VTT/i)).toBeInTheDocument();
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.change(screen.getByPlaceholderText("Username"), { target: { value: "vakarian" } });
|
||||
fireEvent.change(screen.getByPlaceholderText("Password"), { target: { value: "aoeu" } });
|
||||
fireEvent.click(screen.getByRole("button"));
|
||||
});
|
||||
|
||||
expect(screen.getByText(/Hi, authentication complete/i)).toBeInTheDocument();
|
||||
})
|
@ -62,26 +62,49 @@ export const getSessionId = (state: AppState): SessionId | undefined => {
|
||||
}
|
||||
}
|
||||
|
||||
class StateManager {
|
||||
client: Client | undefined;
|
||||
dispatch: React.Dispatch<Action> | undefined;
|
||||
interface StateManagerInterface {
|
||||
setPassword: (password1: string, password2: string) => void;
|
||||
auth: (username: string, password: string) => void;
|
||||
logout: () => void;
|
||||
createUser: (username: string) => void;
|
||||
}
|
||||
|
||||
constructor(client: Client | undefined, dispatch: React.Dispatch<any> | undefined) {
|
||||
class NullManager implements StateManagerInterface {
|
||||
constructor() { }
|
||||
|
||||
async setPassword(_password1: string, _password2: string) { }
|
||||
|
||||
async auth(_username: string, _password: string) { }
|
||||
|
||||
async logout() { }
|
||||
|
||||
async createUser(_username: string) { }
|
||||
}
|
||||
|
||||
class StateManager implements StateManagerInterface{
|
||||
client: Client;
|
||||
state: AppState;
|
||||
dispatch: React.Dispatch<Action>;
|
||||
|
||||
constructor(client: Client, state: AppState, dispatch: React.Dispatch<any>) {
|
||||
this.client = client;
|
||||
this.state = state;
|
||||
this.dispatch = dispatch;
|
||||
}
|
||||
|
||||
async setPassword(password1: string, password2: string) {
|
||||
if (!this.client || !this.dispatch) return;
|
||||
|
||||
await this.client.setPassword(password1, password2);
|
||||
let sessionId = getSessionId(this.state);
|
||||
console.log(`StateManager.setPassword: ${sessionId}`);
|
||||
if (sessionId) {
|
||||
await this.client.setPassword(sessionId, password1, password2);
|
||||
}
|
||||
}
|
||||
|
||||
async auth(username: string, password: string) {
|
||||
if (!this.client || !this.dispatch) return;
|
||||
|
||||
let authResponse = await this.client.auth(username, password);
|
||||
switch (authResponse.type) {
|
||||
case "Unauthorized": break;
|
||||
case "Unexpected": break;
|
||||
case "Success": {
|
||||
window.localStorage.setItem("sessionId", authResponse.content);
|
||||
this.dispatch({ type: "SetAuthState", content: { type: "Authed", sessionId: authResponse.content } });
|
||||
@ -99,24 +122,33 @@ class StateManager {
|
||||
}
|
||||
}
|
||||
|
||||
async createUser(username: string) {
|
||||
if (!this.client || !this.dispatch) return;
|
||||
async logout() {
|
||||
const sessionId = getSessionId(this.state);
|
||||
if (sessionId) {
|
||||
await this.client.logout(sessionId);
|
||||
window.localStorage.removeItem("sessionId");
|
||||
this.dispatch({ type: "SetAuthState", content: { type: "Unauthed" } });
|
||||
}
|
||||
}
|
||||
|
||||
const sessionId = window.localStorage.getItem("sessionId");
|
||||
async createUser(username: string) {
|
||||
console.log("order to createUser", username);
|
||||
const sessionId = getSessionId(this.state);
|
||||
if (sessionId) {
|
||||
let createUserResponse = await this.client.createUser(sessionId, username);
|
||||
console.log("createUser: ", createUserResponse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const StateContext = createContext<[AppState, StateManager]>([initialState(), new StateManager(undefined, undefined)]);
|
||||
export const StateContext = createContext<[AppState, StateManagerInterface]>([initialState(), new NullManager()]);
|
||||
|
||||
interface StateProviderProps { client: Client; }
|
||||
|
||||
export const StateProvider = ({ client, children }: PropsWithChildren<StateProviderProps>) => {
|
||||
const [state, dispatch] = useReducer(stateReducer, initialState());
|
||||
|
||||
const stateManager = useRef(new StateManager(client, dispatch));
|
||||
const stateManager = useRef(new StateManager(client, state, dispatch));
|
||||
|
||||
return <StateContext.Provider value={[state, stateManager.current]}>
|
||||
{children}
|
||||
|
@ -48,14 +48,14 @@ interface AdminProps {
|
||||
export const AdminView = ({ client }: AdminProps) => {
|
||||
const [users, setUsers] = useState<Array<User>>([]);
|
||||
|
||||
/*
|
||||
useEffect(() => {
|
||||
client.users("aoeu").then((u) => {
|
||||
console.log(u);
|
||||
setUsers(u);
|
||||
});
|
||||
}, [client]);
|
||||
|
||||
console.log(users);
|
||||
*/
|
||||
return (<table>
|
||||
<tbody>
|
||||
{users.map((user) => <UserRow user={user} />)}
|
||||
|
@ -6,9 +6,10 @@ import './Authentication.css';
|
||||
interface AuthenticationProps {
|
||||
onSetPassword: (password1: string, password2: string) => void;
|
||||
onAuth: (username: string, password: string) => void;
|
||||
onLogout: () => void;
|
||||
}
|
||||
|
||||
export const Authentication = ({ onSetPassword, onAuth, children }: PropsWithChildren<AuthenticationProps>) => {
|
||||
export const Authentication = ({ onSetPassword, onAuth, onLogout, children }: PropsWithChildren<AuthenticationProps>) => {
|
||||
// No admin password set: prompt for the admin password
|
||||
// Password set, nobody logged in: prompt for login
|
||||
// User logged in: show the children
|
||||
@ -18,8 +19,6 @@ export const Authentication = ({ onSetPassword, onAuth, children }: PropsWithChi
|
||||
let [pwField2, setPwField2] = useState<string>("");
|
||||
let [state, _] = useContext(StateContext);
|
||||
|
||||
console.log("Authentication component", state.state);
|
||||
|
||||
switch (state.state) {
|
||||
case LoadingState.Loading: {
|
||||
return <div>Loading</div>
|
||||
@ -39,7 +38,10 @@ export const Authentication = ({ onSetPassword, onAuth, children }: PropsWithChi
|
||||
</div>;
|
||||
}
|
||||
case "Authed": {
|
||||
return <div> {children} </div>;
|
||||
return (<div>
|
||||
<div> <button onClick={onLogout}>Logout</button> </div>
|
||||
<div> {children} </div>
|
||||
</div>);
|
||||
}
|
||||
case "PasswordReset": {
|
||||
return <div className="auth">
|
||||
@ -48,7 +50,9 @@ export const Authentication = ({ onSetPassword, onAuth, children }: PropsWithChi
|
||||
<p> Your password currently requires a reset. </p>
|
||||
<input type="password" placeholder="Password" onChange={(evt) => setPwField1(evt.target.value)} />
|
||||
<input type="password" placeholder="Retype your Password" onChange={(evt) => setPwField2(evt.target.value)} />
|
||||
<input type="submit" value="Submit" onClick={() => onSetPassword(pwField1, pwField2)} />
|
||||
<input type="submit" value="Submit" onClick={() => {
|
||||
onSetPassword(pwField1, pwField2);
|
||||
}} />
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
@ -59,5 +63,20 @@ export const Authentication = ({ onSetPassword, onAuth, children }: PropsWithChi
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface AuthedViewProps {}
|
||||
|
||||
export const AuthedView = ({ children }: PropsWithChildren<AuthedViewProps>) => {
|
||||
const [_, manager] = useContext(StateContext)
|
||||
return (
|
||||
<Authentication onSetPassword={(password1, password2) => {
|
||||
manager.setPassword(password1, password2)
|
||||
}} onAuth={(username, password) => manager.auth(username, password)}
|
||||
onLogout={() => manager.logout()}>
|
||||
{children}
|
||||
</Authentication>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
@ -15,6 +15,7 @@ export const MainView = ({ client }: MainProps) => {
|
||||
const [users, setUsers] = useState<UserOverview[]>([])
|
||||
|
||||
const sessionId = getSessionId(state)
|
||||
/*
|
||||
useEffect(() => {
|
||||
if (sessionId) {
|
||||
client.profile(sessionId, undefined).then((profile) => setProfile(profile))
|
||||
@ -24,6 +25,7 @@ export const MainView = ({ client }: MainProps) => {
|
||||
})
|
||||
}
|
||||
}, [sessionId, client])
|
||||
*/
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
Loading…
Reference in New Issue
Block a user