Load server config on react app
This commit is contained in:
parent
ec98e728d8
commit
8f0a3e1f07
1
geneit_app/.env
Normal file
1
geneit_app/.env
Normal file
@ -0,0 +1 @@
|
|||||||
|
REACT_APP_BACKEND=http://localhost:8000
|
@ -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();
|
|
||||||
});
|
|
52
geneit_app/src/api/ApiClient.ts
Normal file
52
geneit_app/src/api/ApiClient.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
interface APIResponse {
|
||||||
|
data: any;
|
||||||
|
status: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ApiError extends Error {
|
||||||
|
constructor(message: string, public code: number, public data: any) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class APIClient {
|
||||||
|
/**
|
||||||
|
* Get backend URL
|
||||||
|
*/
|
||||||
|
static backendURL(): string {
|
||||||
|
const URL = process.env.REACT_APP_BACKEND ?? "";
|
||||||
|
if (URL.length === 0) throw new Error("Backend URL undefined!");
|
||||||
|
return URL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform a request on the backend
|
||||||
|
*/
|
||||||
|
static async exec(args: {
|
||||||
|
uri: string;
|
||||||
|
method: "GET" | "POST" | "DELETE";
|
||||||
|
allowFail?: boolean;
|
||||||
|
jsonData?: any;
|
||||||
|
}): Promise<APIResponse> {
|
||||||
|
const res = await fetch(this.backendURL() + args.uri, {
|
||||||
|
method: args.method,
|
||||||
|
body: args.jsonData ? JSON.stringify(args.jsonData) : undefined,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": args.jsonData ? "application/json" : "text/plain",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let data;
|
||||||
|
if (res.headers.get("content-type") === "application/json")
|
||||||
|
data = await res.json();
|
||||||
|
else data = await res.blob();
|
||||||
|
|
||||||
|
if (!args.allowFail && !res.ok)
|
||||||
|
throw new ApiError("Request failed!", res.status, data);
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: data,
|
||||||
|
status: res.status,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
47
geneit_app/src/api/ServerApi.ts
Normal file
47
geneit_app/src/api/ServerApi.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { APIClient } from "./ApiClient";
|
||||||
|
|
||||||
|
interface LenConstraint {
|
||||||
|
min: number;
|
||||||
|
max: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Constraints {
|
||||||
|
mail_len: LenConstraint;
|
||||||
|
user_name_len: LenConstraint;
|
||||||
|
password_len: LenConstraint;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OIDCProvider {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ServerConfig {
|
||||||
|
constraints: Constraints;
|
||||||
|
mail: string;
|
||||||
|
oidc_providers: OIDCProvider[];
|
||||||
|
}
|
||||||
|
|
||||||
|
let config: ServerConfig | null = null;
|
||||||
|
|
||||||
|
export class ServerApi {
|
||||||
|
/**
|
||||||
|
* Get server configuration
|
||||||
|
*/
|
||||||
|
static async LoadConfig(): Promise<void> {
|
||||||
|
config = (
|
||||||
|
await APIClient.exec({
|
||||||
|
uri: "/server/config",
|
||||||
|
method: "GET",
|
||||||
|
})
|
||||||
|
).data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cached configuration
|
||||||
|
*/
|
||||||
|
static Config(): ServerConfig {
|
||||||
|
if (config === null) throw new Error("Missing configuration!");
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
@ -1,17 +1,30 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from "react-dom/client";
|
||||||
import './index.css';
|
import "./index.css";
|
||||||
import App from './App';
|
import App from "./App";
|
||||||
import reportWebVitals from './reportWebVitals';
|
import reportWebVitals from "./reportWebVitals";
|
||||||
|
import { ServerApi } from "./api/ServerApi";
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
try {
|
||||||
|
await ServerApi.LoadConfig();
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(
|
const root = ReactDOM.createRoot(
|
||||||
document.getElementById('root') as HTMLElement
|
document.getElementById("root") as HTMLElement
|
||||||
);
|
);
|
||||||
|
|
||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
alert("Echec de l'initialisation de l'application !");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
// If you want to start measuring performance in your app, pass a function
|
// If you want to start measuring performance in your app, pass a function
|
||||||
// to log results (for example: reportWebVitals(console.log))
|
// to log results (for example: reportWebVitals(console.log))
|
||||||
|
16
geneit_backend/Cargo.lock
generated
16
geneit_backend/Cargo.lock
generated
@ -19,6 +19,21 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-cors"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b340e9cfa5b08690aae90fb61beb44e9b06f44fe3d0f93781aaa58cfba86245e"
|
||||||
|
dependencies = [
|
||||||
|
"actix-utils",
|
||||||
|
"actix-web",
|
||||||
|
"derive_more",
|
||||||
|
"futures-util",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "actix-http"
|
name = "actix-http"
|
||||||
version = "3.3.1"
|
version = "3.3.1"
|
||||||
@ -779,6 +794,7 @@ dependencies = [
|
|||||||
name = "geneit_backend"
|
name = "geneit_backend"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"actix-cors",
|
||||||
"actix-remote-ip",
|
"actix-remote-ip",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
@ -12,6 +12,7 @@ clap = { version = "4.3.0", features = ["derive", "env"] }
|
|||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
anyhow = "1.0.71"
|
anyhow = "1.0.71"
|
||||||
actix-web = "4.3.1"
|
actix-web = "4.3.1"
|
||||||
|
actix-cors = "0.6.4"
|
||||||
futures-util = "0.3.28"
|
futures-util = "0.3.28"
|
||||||
diesel = { version = "2.0.4", features = ["postgres"] }
|
diesel = { version = "2.0.4", features = ["postgres"] }
|
||||||
serde = { version = "1.0.163", features = ["derive"] }
|
serde = { version = "1.0.163", features = ["derive"] }
|
||||||
|
@ -81,19 +81,11 @@ pub struct AppConfig {
|
|||||||
pub smtp_password: Option<String>,
|
pub smtp_password: Option<String>,
|
||||||
|
|
||||||
/// Password reset URL
|
/// Password reset URL
|
||||||
#[clap(
|
#[clap(long, env, default_value = "APP_ORIGIN/reset_password#TOKEN")]
|
||||||
long,
|
|
||||||
env,
|
|
||||||
default_value = "http://localhost:3000/reset_password#TOKEN"
|
|
||||||
)]
|
|
||||||
pub reset_password_url: String,
|
pub reset_password_url: String,
|
||||||
|
|
||||||
/// Delete account URL
|
/// Delete account URL
|
||||||
#[clap(
|
#[clap(long, env, default_value = "APP_ORIGIN/delete_account#TOKEN")]
|
||||||
long,
|
|
||||||
env,
|
|
||||||
default_value = "http://localhost:3000/delete_account#TOKEN"
|
|
||||||
)]
|
|
||||||
pub delete_account_url: String,
|
pub delete_account_url: String,
|
||||||
|
|
||||||
/// URL where the OpenID configuration can be found
|
/// URL where the OpenID configuration can be found
|
||||||
@ -121,8 +113,8 @@ pub struct AppConfig {
|
|||||||
pub oidc_client_secret: String,
|
pub oidc_client_secret: String,
|
||||||
|
|
||||||
/// OpenID login redirect URL
|
/// OpenID login redirect URL
|
||||||
#[arg(long, env, default_value = "http://localhost:3000/oidc_cb")]
|
#[arg(long, env, default_value = "APP_ORIGIN/oidc_cb")]
|
||||||
pub oidc_redirect_url: String,
|
oidc_redirect_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
@ -159,12 +151,16 @@ impl AppConfig {
|
|||||||
|
|
||||||
/// Get password reset URL
|
/// Get password reset URL
|
||||||
pub fn get_password_reset_url(&self, token: &str) -> String {
|
pub fn get_password_reset_url(&self, token: &str) -> String {
|
||||||
self.reset_password_url.replace("TOKEN", token)
|
self.reset_password_url
|
||||||
|
.replace("APP_ORIGIN", &self.website_origin)
|
||||||
|
.replace("TOKEN", token)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get account delete URL
|
/// Get account delete URL
|
||||||
pub fn get_account_delete_url(&self, token: &str) -> String {
|
pub fn get_account_delete_url(&self, token: &str) -> String {
|
||||||
self.delete_account_url.replace("TOKEN", token)
|
self.delete_account_url
|
||||||
|
.replace("APP_ORIGIN", &self.website_origin)
|
||||||
|
.replace("TOKEN", token)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get OpenID providers configuration
|
/// Get OpenID providers configuration
|
||||||
@ -181,6 +177,12 @@ impl AppConfig {
|
|||||||
name: self.oidc_provider_name.as_str(),
|
name: self.oidc_provider_name.as_str(),
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get OIDC callback URL
|
||||||
|
pub fn oidc_redirect_url(&self) -> String {
|
||||||
|
self.oidc_redirect_url
|
||||||
|
.replace("APP_ORIGIN", &self.website_origin)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, serde::Serialize)]
|
#[derive(Debug, Clone, serde::Serialize)]
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use actix_cors::Cors;
|
||||||
use actix_remote_ip::RemoteIPConfig;
|
use actix_remote_ip::RemoteIPConfig;
|
||||||
use actix_web::middleware::Logger;
|
use actix_web::middleware::Logger;
|
||||||
use actix_web::{web, App, HttpServer};
|
use actix_web::{web, App, HttpServer};
|
||||||
@ -12,6 +13,14 @@ async fn main() -> std::io::Result<()> {
|
|||||||
|
|
||||||
HttpServer::new(|| {
|
HttpServer::new(|| {
|
||||||
App::new()
|
App::new()
|
||||||
|
.wrap(
|
||||||
|
Cors::default()
|
||||||
|
.allowed_origin(&AppConfig::get().website_origin)
|
||||||
|
.allowed_methods(vec!["GET", "POST"])
|
||||||
|
.allowed_header("X-Auth-Token")
|
||||||
|
.supports_credentials()
|
||||||
|
.max_age(3600),
|
||||||
|
)
|
||||||
.wrap(Logger::default())
|
.wrap(Logger::default())
|
||||||
.app_data(web::Data::new(RemoteIPConfig {
|
.app_data(web::Data::new(RemoteIPConfig {
|
||||||
proxy: AppConfig::get().proxy_ip.clone(),
|
proxy: AppConfig::get().proxy_ip.clone(),
|
||||||
|
@ -96,7 +96,7 @@ pub async fn start_login(prov_id: &str, ip: IpAddr) -> anyhow::Result<String> {
|
|||||||
Ok(prov.conf.gen_authorization_url(
|
Ok(prov.conf.gen_authorization_url(
|
||||||
prov.prov.client_id,
|
prov.prov.client_id,
|
||||||
&state_key,
|
&state_key,
|
||||||
&AppConfig::get().oidc_redirect_url,
|
&AppConfig::get().oidc_redirect_url(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,7 +133,7 @@ pub async fn finish_login(
|
|||||||
prov.prov.client_id,
|
prov.prov.client_id,
|
||||||
prov.prov.client_secret,
|
prov.prov.client_secret,
|
||||||
code,
|
code,
|
||||||
&AppConfig::get().oidc_redirect_url,
|
&AppConfig::get().oidc_redirect_url(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| OpenIDServiceError::QueryTokenEndpoint(e.to_string()))?;
|
.map_err(|e| OpenIDServiceError::QueryTokenEndpoint(e.to_string()))?;
|
||||||
|
Loading…
Reference in New Issue
Block a user