mirror of
https://github.com/sigmasternchen/threadule
synced 2025-03-15 08:09:01 +00:00
some frontend code for requests and authentication
This commit is contained in:
parent
5ba50cb2f6
commit
b253feda0c
10 changed files with 187 additions and 9 deletions
|
@ -10,6 +10,7 @@
|
|||
"@types/node": "^12.0.0",
|
||||
"@types/react": "^17.0.0",
|
||||
"@types/react-dom": "^17.0.0",
|
||||
"axios": "^0.21.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-scripts": "4.0.3",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import './App.css';
|
||||
import {AuthProvider} from "./auth/AuthProvider";
|
||||
import AuthProvider from "./auth/AuthProvider";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
|
|
26
frontend/src/api/client.ts
Normal file
26
frontend/src/api/client.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
import axios, {AxiosInstance} from "axios";
|
||||
|
||||
const AUTHORIZATION_TYPE = "Bearer"
|
||||
|
||||
export type Client = {
|
||||
axios: AxiosInstance
|
||||
authorized: boolean
|
||||
}
|
||||
|
||||
export const getClient = (authToken?: string): Client => {
|
||||
if (authToken) {
|
||||
return {
|
||||
axios: axios.create({
|
||||
headers: {
|
||||
"Authorization": AUTHORIZATION_TYPE + " " + authToken
|
||||
}
|
||||
}),
|
||||
authorized: true
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
axios: axios,
|
||||
authorized: false
|
||||
}
|
||||
}
|
||||
}
|
24
frontend/src/api/endpoints/AuthenticationEndpoint.ts
Normal file
24
frontend/src/api/endpoints/AuthenticationEndpoint.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import Endpoint from "./Endpoint";
|
||||
import {Client} from "../client";
|
||||
import {LoginParams, LoginResponse} from "../entities/login";
|
||||
import User from "../entities/User";
|
||||
|
||||
const API_PREFIX = "http://localhost:8080/authentication/"
|
||||
|
||||
class AuthenticationEndpoint extends Endpoint {
|
||||
constructor(client: Client) {
|
||||
super(client);
|
||||
}
|
||||
|
||||
public async login(params: LoginParams): Promise<LoginResponse> {
|
||||
return this.post<LoginResponse>(API_PREFIX, {
|
||||
data: params
|
||||
})
|
||||
}
|
||||
|
||||
public async getUser(): Promise<User> {
|
||||
return this.get<User>(API_PREFIX)
|
||||
}
|
||||
}
|
||||
|
||||
export default AuthenticationEndpoint
|
62
frontend/src/api/endpoints/Endpoint.ts
Normal file
62
frontend/src/api/endpoints/Endpoint.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
import {Client} from "../client";
|
||||
import {AxiosRequestConfig} from "axios";
|
||||
import ErrorStatus, {ErrorStatusGuard} from "../entities/ErrorStatus";
|
||||
|
||||
abstract class Endpoint {
|
||||
protected client: Client
|
||||
|
||||
protected constructor(client: Client) {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
protected requireAuthenticated() {
|
||||
if (!this.client.authorized)
|
||||
throw "this endpoint needs an authorized client"
|
||||
}
|
||||
|
||||
private static handleResponse<T>(response: any): T {
|
||||
if (ErrorStatusGuard(response)) {
|
||||
throw (response as ErrorStatus)
|
||||
}
|
||||
|
||||
return response as T
|
||||
}
|
||||
|
||||
protected async get<T>(path: string, config?: AxiosRequestConfig): Promise<T> {
|
||||
return Endpoint.handleResponse<T>(
|
||||
this.client.axios.get(path, config)
|
||||
)
|
||||
}
|
||||
|
||||
protected async post<T>(path: string, config?: AxiosRequestConfig): Promise<T> {
|
||||
return Endpoint.handleResponse<T>(
|
||||
this.client.axios.post(path, config)
|
||||
)
|
||||
}
|
||||
|
||||
protected async options<T>(path: string, config?: AxiosRequestConfig): Promise<T> {
|
||||
return Endpoint.handleResponse<T>(
|
||||
this.client.axios.options(path, config)
|
||||
)
|
||||
}
|
||||
|
||||
protected async head<T>(path: string, config?: AxiosRequestConfig): Promise<T> {
|
||||
return Endpoint.handleResponse<T>(
|
||||
this.client.axios.head(path, config)
|
||||
)
|
||||
}
|
||||
|
||||
protected async put<T>(path: string, config?: AxiosRequestConfig): Promise<T> {
|
||||
return Endpoint.handleResponse<T>(
|
||||
this.client.axios.put(path, config)
|
||||
)
|
||||
}
|
||||
|
||||
protected async delete<T>(path: string, config?: AxiosRequestConfig): Promise<T> {
|
||||
return Endpoint.handleResponse<T>(
|
||||
this.client.axios.delete(path, config)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Endpoint
|
17
frontend/src/api/entities/ErrorStatus.ts
Normal file
17
frontend/src/api/entities/ErrorStatus.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
type ErrorStatus = {
|
||||
status: number,
|
||||
message: string,
|
||||
details: string,
|
||||
time: Date
|
||||
}
|
||||
|
||||
export const ErrorStatusGuard = (value: any): value is ErrorStatus => {
|
||||
return value.status !== undefined &&
|
||||
typeof value.status == "number" &&
|
||||
value.message !== undefined &&
|
||||
value.details !== undefined &&
|
||||
value.time !== undefined;
|
||||
}
|
||||
|
||||
|
||||
export default ErrorStatus
|
6
frontend/src/api/entities/Group.ts
Normal file
6
frontend/src/api/entities/Group.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
type Group = {
|
||||
// TODO
|
||||
}
|
||||
|
||||
export default Group
|
9
frontend/src/api/entities/User.ts
Normal file
9
frontend/src/api/entities/User.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import Group from "./Group";
|
||||
|
||||
type User = {
|
||||
id: string
|
||||
username: string
|
||||
groups: Group[]
|
||||
}
|
||||
|
||||
export default User
|
9
frontend/src/api/entities/login.ts
Normal file
9
frontend/src/api/entities/login.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
|
||||
export type LoginParams = {
|
||||
username: string
|
||||
password: string
|
||||
}
|
||||
|
||||
export type LoginResponse = {
|
||||
token: string
|
||||
}
|
|
@ -1,32 +1,54 @@
|
|||
import React, {FunctionComponent, useContext, useState} from "react";
|
||||
import AuthenticationEndpoint from "../api/endpoints/AuthenticationEndpoint";
|
||||
import {Client, getClient} from "../api/client";
|
||||
import User from "../api/entities/User";
|
||||
|
||||
type AuthState = {
|
||||
loggedIn: boolean
|
||||
|
||||
login: (username: string, password: string) => Promise<string>
|
||||
login: (username: string, password: string) => Promise<User>
|
||||
}
|
||||
|
||||
const emptyAuthState = {
|
||||
loggedIn: false,
|
||||
|
||||
login: async () => "not implemented"
|
||||
login: async () => {
|
||||
throw "not implemented"
|
||||
}
|
||||
}
|
||||
|
||||
const AuthContext = React.createContext<AuthState>(emptyAuthState)
|
||||
|
||||
export const useAuth = () => {
|
||||
export const useAuth = (): AuthState => {
|
||||
return useContext(AuthContext)
|
||||
}
|
||||
|
||||
type AuthProviderProps = {
|
||||
}
|
||||
|
||||
export const AuthProvider: FunctionComponent<AuthProviderProps> = ({children}) => {
|
||||
const defaultAuthState = {
|
||||
...emptyAuthState
|
||||
}
|
||||
|
||||
const [authState, setAuthState] = useState<AuthState>(defaultAuthState)
|
||||
const AuthProvider: FunctionComponent<AuthProviderProps> = ({children}) => {
|
||||
const [client, setClient] = useState<Client>(getClient())
|
||||
const authenticationEndpoint = new AuthenticationEndpoint(client)
|
||||
|
||||
const [authState, setAuthState] = useState<AuthState>({
|
||||
...emptyAuthState,
|
||||
|
||||
login: async (username: string, password: string): Promise<User> => {
|
||||
const response = await authenticationEndpoint.login({
|
||||
username: username,
|
||||
password: password
|
||||
})
|
||||
|
||||
// local new client
|
||||
const client = getClient(response.token)
|
||||
setClient(client)
|
||||
|
||||
// local new authenticationEndpoint
|
||||
const tmpAuthenticationEndpoint = new AuthenticationEndpoint(client)
|
||||
return await tmpAuthenticationEndpoint.getUser()
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={authState}>
|
||||
|
@ -34,3 +56,5 @@ export const AuthProvider: FunctionComponent<AuthProviderProps> = ({children}) =
|
|||
</AuthContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export default AuthProvider
|
Loading…
Reference in a new issue