Skip to content
Clone

chore: drop dead cors, config sections and the unused frontend http stack

ZhenYi committed
parent 1ff66b4 commit d6dc02f Browse files

14 files changed

+0 −656

File tree 14
Deleted lib/config/src/app/app_config.rs
@@ -1,106 +0,0 @@
1use serde::Deserialize;
2
3use super::key::AppKeyConfig;
4
5#[derive(Debug, Clone, Deserialize)]
6pub struct AppConfig {
7 pub id: String,
8 #[serde(default)]
9 pub audience: Option<String>,
10 #[serde(default)]
11 pub auth_center: Option<String>,
12 pub active_key_id: String,
13 #[serde(default)]
14 pub keys: Vec<AppKeyConfig>,
15}
16
17impl AppConfig {
18 pub fn key(&self, key_id: &str) -> Option<&AppKeyConfig> {
19 self.keys.iter().find(|key| key.key_id == key_id)
20 }
21
22 pub fn active_key(&self) -> Option<&AppKeyConfig> {
23 self.key(&self.active_key_id)
24 }
25
26 pub fn enabled_keys(&self) -> impl Iterator<Item = &AppKeyConfig> {
27 self.keys.iter().filter(|key| key.enabled)
28 }
29
30 pub fn encryption_key(&self) -> Option<&AppKeyConfig> {
31 let active = self.active_key();
32 match active {
33 Some(key) if key.enabled => Some(key),
34 _ => self.enabled_keys().next(),
35 }
36 }
37
38 pub fn decryption_keys(&self) -> impl Iterator<Item = &AppKeyConfig> {
39 self.keys
40 .iter()
41 .filter(|key| key.enabled && key.private_key.is_some())
42 }
43
44 pub fn validate(&self) -> Result<(), String> {
45 let id = self.id.trim();
46 if id.is_empty() {
47 return Err("app.id must not be empty".to_string());
48 }
49 if id.len() > 128 || id.chars().any(char::is_whitespace) {
50 return Err(
51 "app.id must be a token of at most 128 characters without whitespace".to_string(),
52 );
53 }
54 if self.active_key_id.trim().is_empty() {
55 return Err("app.active_key_id must not be empty".to_string());
56 }
57 if let Some(audience) = self.audience.as_deref()
58 && (audience.trim().is_empty() || audience.chars().any(char::is_whitespace))
59 {
60 return Err(
61 "app.audience must be a non-empty token without whitespace when set".to_string(),
62 );
63 }
64 if self.keys.is_empty() {
65 return Err("app.keys must contain at least one key".to_string());
66 }
67 for (index, key) in self.keys.iter().enumerate() {
68 key.validate()
69 .map_err(|error| format!("app.keys[{index}]: {error}"))?;
70 }
71 for (index, key) in self.keys.iter().enumerate() {
72 if self.keys[index + 1..]
73 .iter()
74 .any(|other| other.key_id == key.key_id)
75 {
76 return Err(format!(
77 "app.keys[{index}].key_id `{}` is duplicated",
78 key.key_id
79 ));
80 }
81 }
82 let active = self.active_key().ok_or_else(|| {
83 format!(
84 "app.active_key_id `{}` is not listed in app.keys",
85 self.active_key_id
86 )
87 })?;
88 if !active.enabled {
89 return Err(format!(
90 "app.active_key_id `{}` is disabled",
91 self.active_key_id
92 ));
93 }
94 if active.private_key.is_none() {
95 return Err(format!(
96 "app.active_key_id `{}` requires a private_key: this service decrypts with it",
97 self.active_key_id
98 ));
99 }
100 Ok(())
101 }
102}
103
104impl crate::loader::Section for AppConfig {
105 const KEY: &'static str = "app";
106}
Deleted lib/config/src/app/key.rs
@@ -1,112 +0,0 @@
1use std::fmt;
2use std::path::PathBuf;
3
4use base64::Engine;
5use base64::engine::general_purpose::{STANDARD, URL_SAFE_NO_PAD};
6use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Deserialize, Serialize)]
10#[serde(rename_all = "snake_case")]
11pub enum AppKeyAlgorithm {
12 #[default]
13 RsaOaep256A256Gcm,
14}
15
16#[derive(Clone, Deserialize, Serialize)]
17#[serde(untagged)]
18pub enum SecretMaterial {
19 File { file: PathBuf },
20 Env { env: String },
21 Inline(String),
22}
23
24impl SecretMaterial {
25 pub fn resolve(&self) -> Result<String, String> {
26 match self {
27 Self::File { file } => std::fs::read_to_string(file).map_err(|error| {
28 format!("app key file {} is not readable: {error}", file.display())
29 }),
30 Self::Env { env } => std::env::var(env)
31 .map_err(|_| format!("app key environment variable {env} is not set")),
32 Self::Inline(text) => Ok(decode_inline(text)),
33 }
34 }
35}
36
37fn decode_inline(text: &str) -> String {
38 let trimmed = text.trim();
39 if trimmed.contains("BEGIN") {
40 return trimmed.to_owned();
41 }
42 let decoded = URL_SAFE_NO_PAD
43 .decode(trimmed)
44 .or_else(|_| STANDARD.decode(trimmed))
45 .ok()
46 .and_then(|bytes| String::from_utf8(bytes).ok());
47 match decoded {
48 Some(pem) if pem.contains("BEGIN") => pem,
49 _ => trimmed.to_owned(),
50 }
51}
52
53impl fmt::Debug for SecretMaterial {
54 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
55 match self {
56 Self::File { file } => formatter
57 .debug_struct("SecretMaterial")
58 .field("file", file)
59 .finish_non_exhaustive(),
60 Self::Env { env } => formatter
61 .debug_struct("SecretMaterial")
62 .field("env", env)
63 .finish_non_exhaustive(),
64 Self::Inline(_) => formatter
65 .debug_struct("SecretMaterial")
66 .field("inline", &"<redacted>")
67 .finish(),
68 }
69 }
70}
71
72#[derive(Debug, Clone, Deserialize, Serialize)]
73pub struct AppKeyConfig {
74 pub key_id: String,
75 #[serde(default)]
76 pub algorithm: AppKeyAlgorithm,
77 #[serde(default)]
78 pub private_key: Option<SecretMaterial>,
79 pub public_key: SecretMaterial,
80 #[serde(default)]
81 pub not_before: Option<DateTime<Utc>>,
82 #[serde(default)]
83 pub not_after: Option<DateTime<Utc>>,
84 #[serde(default = "default_enabled")]
85 pub enabled: bool,
86}
87
88fn default_enabled() -> bool {
89 true
90}
91
92impl AppKeyConfig {
93 pub fn validate(&self) -> Result<(), String> {
94 let key_id = self.key_id.trim();
95 if key_id.is_empty() {
96 return Err("app.keys[].key_id must not be empty".to_string());
97 }
98 if key_id.len() > 128 {
99 return Err(format!(
100 "app.keys[].key_id `{key_id}` must be at most 128 characters"
101 ));
102 }
103 if let (Some(not_before), Some(not_after)) = (self.not_before, self.not_after)
104 && not_after <= not_before
105 {
106 return Err(format!(
107 "app.keys[{key_id}].not_after must be greater than not_before"
108 ));
109 }
110 Ok(())
111 }
112}
Deleted lib/config/src/app/mod.rs
@@ -1,5 +0,0 @@
1mod app_config;
2mod key;
3
4pub use app_config::AppConfig;
5pub use key::{AppKeyAlgorithm, AppKeyConfig, SecretMaterial};
Modified lib/config/src/lib.rs View lib/config/src/lib.rs
@@ -1,5 +1,4 @@
11pub mod apihost;
2pub mod app;
32pub mod auth;
43pub mod chat;
54pub mod database;
@@ -15,8 +14,6 @@
1514pub mod rocketmq;
1615pub mod s3;
1716pub mod scoped;
18pub mod server;
19pub mod service_auth;
2017pub mod session;
2118pub mod smtp;
2219pub mod space;
Deleted lib/config/src/server.rs
@@ -1,21 +0,0 @@
1use std::net::{Ipv4Addr, SocketAddr};
2
3use serde::Deserialize;
4
5#[derive(Debug, Clone, Deserialize)]
6#[serde(default)]
7pub struct ServerConfig {
8 pub grpc_addr: SocketAddr,
9}
10
11impl Default for ServerConfig {
12 fn default() -> Self {
13 Self {
14 grpc_addr: SocketAddr::from((Ipv4Addr::LOCALHOST, 8082)),
15 }
16 }
17}
18
19impl crate::loader::Section for ServerConfig {
20 const KEY: &'static str = "server";
21}
Deleted lib/config/src/service_auth.rs
@@ -1,58 +0,0 @@
1use serde::Deserialize;
2use std::collections::HashMap;
3
4#[derive(Clone, Default, PartialEq, Eq, Deserialize)]
5#[serde(default)]
6pub struct ServiceAuthConfig {
7 pub service_id: String,
8 pub key: String,
9 pub peers: HashMap<String, String>,
10}
11
12impl std::fmt::Debug for ServiceAuthConfig {
13 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
14 let mut peer_ids: Vec<&str> = self.peers.keys().map(String::as_str).collect();
15 peer_ids.sort_unstable();
16 formatter
17 .debug_struct("ServiceAuthConfig")
18 .field("service_id", &self.service_id)
19 .field("key", &"<redacted>")
20 .field("peers", &peer_ids)
21 .finish()
22 }
23}
24
25impl ServiceAuthConfig {
26 pub fn validate(&self) -> Result<(), String> {
27 for id in self.peers.keys().chain(std::iter::once(&self.service_id)) {
28 if id.is_empty()
29 || !id
30 .bytes()
31 .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'_' | b'.'))
32 {
33 return Err(
34 "service_auth identities require token IDs and keys of at least 32 bytes"
35 .into(),
36 );
37 }
38 }
39 if self.key.len() < 32
40 || self.key.starts_with("REPLACE_WITH_")
41 || self.key.bytes().any(|byte| !byte.is_ascii_graphic())
42 {
43 return Err("service_auth.key requires at least 32 bytes of secret material".into());
44 }
45 if self.peers.values().any(|digest| {
46 digest.strip_prefix("sha256:").is_none_or(|hex| {
47 hex.len() != 64 || !hex.bytes().all(|byte| byte.is_ascii_hexdigit())
48 })
49 }) {
50 return Err("service_auth.peers must contain sha256:<hex> verification digests".into());
51 }
52 Ok(())
53 }
54}
55
56impl crate::loader::Section for ServiceAuthConfig {
57 const KEY: &'static str = "service_auth";
58}
Deleted lib/crypt/src/app.rs
@@ -1,59 +0,0 @@
1use config::app::{AppConfig, AppKeyAlgorithm, AppKeyConfig, SecretMaterial};
2
3use crate::error::{CryptError, CryptResult};
4use crate::key::{RsaDecryptor, RsaEncryptor, RsaKeyPair};
5
6pub fn encryptor(config: &AppConfig) -> CryptResult<RsaEncryptor> {
7 let key = config
8 .encryption_key()
9 .ok_or_else(|| CryptError::KeyNotFound(config.active_key_id.clone()))?;
10 encryptor_for(key)
11}
12
13pub fn encryptor_for(key: &AppKeyConfig) -> CryptResult<RsaEncryptor> {
14 check_algorithm(key)?;
15 let pem = resolve(&key.public_key, &key.key_id)?;
16 RsaEncryptor::from_public_pem(key.key_id.clone(), &pem)
17}
18
19pub fn encryptor_from_public_pem(
20 key_id: impl Into<String>,
21 pem: &str,
22) -> CryptResult<RsaEncryptor> {
23 RsaEncryptor::from_public_pem(key_id, pem)
24}
25
26pub fn decryptor(config: &AppConfig) -> CryptResult<RsaDecryptor> {
27 let mut keys = Vec::new();
28 for key in config.decryption_keys() {
29 check_algorithm(key)?;
30 let private = key
31 .private_key
32 .as_ref()
33 .ok_or_else(|| CryptError::KeyNotFound(key.key_id.clone()))?;
34 let private_pem = resolve(private, &key.key_id)?;
35 let public_pem = resolve(&key.public_key, &key.key_id)?;
36 keys.push(RsaKeyPair::from_pem_pair(
37 key.key_id.clone(),
38 &private_pem,
39 &public_pem,
40 )?);
41 }
42 RsaDecryptor::new(keys)
43}
44
45pub fn fingerprint_for(key: &AppKeyConfig) -> CryptResult<String> {
46 Ok(encryptor_for(key)?.fingerprint())
47}
48
49fn resolve(material: &SecretMaterial, key_id: &str) -> CryptResult<String> {
50 material
51 .resolve()
52 .map_err(|error| CryptError::Config(format!("key `{key_id}`: {error}")))
53}
54
55fn check_algorithm(key: &AppKeyConfig) -> CryptResult<()> {
56 match key.algorithm {
57 AppKeyAlgorithm::RsaOaep256A256Gcm => Ok(()),
58 }
59}
Modified lib/crypt/src/lib.rs View lib/crypt/src/lib.rs
@@ -1,9 +1,7 @@
1pub mod app;
21pub mod envelope;
32pub mod error;
43pub mod key;
54
6pub use app::{decryptor, encryptor, encryptor_for, encryptor_from_public_pem, fingerprint_for};
75pub use envelope::{
86 ENVELOPE_PREFIX, decrypt, decrypt_string, encrypt, encrypt_string, envelope_key_id,
97};
Modified lib/crypt/Cargo.toml View lib/crypt/Cargo.toml
@@ -4,10 +4,8 @@
44edition.workspace = true
55
66[dependencies]
7config = { workspace = true }
87aes-gcm = "0.11.1"
98base64 = { workspace = true }
10bytes = { workspace = true }
119rand_core = { version = "0.6.4", features = ["getrandom"] }
1210rsa = { version = "0.9.10", features = ["sha2", "getrandom"] }
1311sha2 = "0.10.9"
Deleted lib/envelope/src/cors.rs
@@ -1,196 +0,0 @@
1use std::future::{Ready, ready};
2use std::rc::Rc;
3use std::sync::Arc;
4use std::task::{Context, Poll};
5
6use actix_web::body::{BoxBody, EitherBody, MessageBody};
7use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
8use actix_web::http::Method;
9use actix_web::http::header::{self, HeaderMap, HeaderValue};
10use actix_web::{Error, HttpResponse};
11use config::web::WebConfig;
12use futures_util::future::LocalBoxFuture;
13
14#[derive(Debug, Clone)]
15pub struct Cors {
16 policy: Arc<CorsPolicy>,
17}
18
19impl Cors {
20 pub fn new(web: &WebConfig) -> Self {
21 Self {
22 policy: Arc::new(CorsPolicy::new(web)),
23 }
24 }
25}
26
27impl<S, B> Transform<S, ServiceRequest> for Cors
28where
29 S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
30 B: MessageBody + 'static,
31{
32 type Response = ServiceResponse<EitherBody<B>>;
33 type Error = Error;
34 type Transform = CorsMiddleware<S>;
35 type InitError = ();
36 type Future = Ready<Result<Self::Transform, Self::InitError>>;
37
38 fn new_transform(&self, service: S) -> Self::Future {
39 ready(Ok(CorsMiddleware {
40 service: Rc::new(service),
41 policy: Arc::clone(&self.policy),
42 }))
43 }
44}
45
46pub struct CorsMiddleware<S> {
47 service: Rc<S>,
48 policy: Arc<CorsPolicy>,
49}
50
51impl<S, B> Service<ServiceRequest> for CorsMiddleware<S>
52where
53 S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
54 B: MessageBody + 'static,
55{
56 type Response = ServiceResponse<EitherBody<B>>;
57 type Error = Error;
58 type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
59
60 fn poll_ready(&self, context: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
61 self.service.poll_ready(context)
62 }
63
64 fn call(&self, request: ServiceRequest) -> Self::Future {
65 let service = Rc::clone(&self.service);
66 let policy = Arc::clone(&self.policy);
67 Box::pin(async move {
68 let Some(origin) = allowed_origin(&request, &policy) else {
69 return service
70 .call(request)
71 .await
72 .map(|response| response.map_into_left_body());
73 };
74 let __inl_cond = {
75 let request = &request;
76 request.method() == Method::OPTIONS
77 && request
78 .headers()
79 .contains_key(header::ACCESS_CONTROL_REQUEST_METHOD)
80 };
81 if __inl_cond {
82 let mut response = HttpResponse::NoContent().finish();
83 policy.write_preflight(response.headers_mut(), &origin);
84 let response = response.map_body(|_, body: BoxBody| EitherBody::<B>::right(body));
85 return Ok(ServiceResponse::new(request.into_parts().0, response));
86 }
87 let mut response = service.call(request).await?.map_into_left_body();
88 policy.write_headers(response.headers_mut(), &origin);
89 Ok(response)
90 })
91 }
92}
93
94#[derive(Debug, Clone)]
95struct CorsPolicy {
96 allow_any_origin: bool,
97 allow_origins: Vec<String>,
98 allow_methods: HeaderValue,
99 allow_headers: HeaderValue,
100 expose_headers: Option<HeaderValue>,
101 allow_credentials: bool,
102 max_age: HeaderValue,
103}
104
105impl CorsPolicy {
106 fn new(web: &WebConfig) -> Self {
107 let origins = web.allow_origins();
108 let allow_any_origin = origins.iter().any(|origin| origin == "*");
109 let allow_origins = origins.into_iter().filter(|origin| origin != "*").collect();
110 Self {
111 allow_any_origin,
112 allow_origins,
113 allow_methods: joined(&web.allow_methods()),
114 allow_headers: joined(&web.allow_headers()),
115 expose_headers: optional(&web.expose_headers()),
116 allow_credentials: web.cors_allow_credentials && !allow_any_origin,
117 max_age: header_value(&web.cors_max_age_secs.to_string()),
118 }
119 }
120
121 fn allows(&self, origin: &str) -> bool {
122 {
123 let allow_any = &self.allow_any_origin;
124 let allow_origins = &self.allow_origins;
125 *allow_any
126 || allow_origins
127 .iter()
128 .any(|allowed| allowed.eq_ignore_ascii_case(origin))
129 }
130 }
131
132 fn write_preflight(&self, headers: &mut HeaderMap, origin: &str) {
133 self.write_origin(headers, origin);
134 headers.insert(
135 header::ACCESS_CONTROL_ALLOW_METHODS,
136 self.allow_methods.clone(),
137 );
138 headers.insert(
139 header::ACCESS_CONTROL_ALLOW_HEADERS,
140 self.allow_headers.clone(),
141 );
142 headers.insert(header::ACCESS_CONTROL_MAX_AGE, self.max_age.clone());
143 }
144
145 fn write_headers(&self, headers: &mut HeaderMap, origin: &str) {
146 self.write_origin(headers, origin);
147 if let Some(expose) = &self.expose_headers {
148 headers.insert(header::ACCESS_CONTROL_EXPOSE_HEADERS, expose.clone());
149 }
150 }
151
152 fn write_origin(&self, headers: &mut HeaderMap, origin: &str) {
153 if headers.contains_key(header::ACCESS_CONTROL_ALLOW_ORIGIN) {
154 return;
155 }
156 if self.allow_any_origin {
157 headers.insert(
158 header::ACCESS_CONTROL_ALLOW_ORIGIN,
159 HeaderValue::from_static("*"),
160 );
161 return;
162 }
163 if let Ok(value) = HeaderValue::from_str(origin) {
164 headers.insert(header::ACCESS_CONTROL_ALLOW_ORIGIN, value);
165 }
166 headers.append(header::VARY, HeaderValue::from_static("origin"));
167 if self.allow_credentials {
168 headers.insert(
169 header::ACCESS_CONTROL_ALLOW_CREDENTIALS,
170 HeaderValue::from_static("true"),
171 );
172 }
173 }
174}
175
176fn allowed_origin(request: &ServiceRequest, policy: &CorsPolicy) -> Option<String> {
177 let origin = request
178 .headers()
179 .get(header::ORIGIN)
180 .and_then(|value| value.to_str().ok())
181 .map(str::trim)
182 .filter(|origin| !origin.is_empty())?;
183 policy.allows(origin).then(|| origin.to_owned())
184}
185
186fn joined(values: &[String]) -> HeaderValue {
187 header_value(&values.join(", "))
188}
189
190fn header_value(raw: &str) -> HeaderValue {
191 HeaderValue::from_str(raw).unwrap_or_else(|_| HeaderValue::from_static(""))
192}
193
194fn optional(values: &[String]) -> Option<HeaderValue> {
195 (!values.is_empty()).then(|| joined(values))
196}
Modified lib/envelope/src/lib.rs View lib/envelope/src/lib.rs
@@ -1,9 +1,7 @@
11pub mod context;
2pub mod cors;
32pub mod middleware;
43pub mod response;
54
65pub use context::{request_id, with_request_id};
7pub use cors::Cors;
86pub use middleware::EnvelopeContext;
97pub use response::{Envelope, NoData};
Deleted view/auth/src/shared/lib/http/http.ts
@@ -1,86 +0,0 @@
1export type HttpService = 'auth' | 'api' | 'git'
2
3export interface RequestOptions extends RequestInit {
4 baseUrl?: string
5 timeoutMs?: number
6 responseType?: 'json' | 'text' | 'blob'
7 auth?: 'session' | 'bearer' | 'optional-bearer' | 'public'
8 handleUnauthorized?: boolean
9 manager?: unknown
10}
11
12export interface Response<T> {
13 data: T
14 status: number
15 headers: Headers
16}
17
18const resolveBase = (baseUrl?: string): string => {
19 if (baseUrl) return baseUrl
20 if (typeof document === 'undefined') return ''
21 const value = document.querySelector<HTMLMetaElement>('meta[name="api-base-url"]')?.content?.trim() ?? ''
22 return value.startsWith('%') && value.endsWith('%') ? '' : value
23}
24
25const buildUrl = (path: string, baseUrl?: string): string => {
26 const origin = typeof location === 'undefined' ? 'http://localhost' : location.origin
27 const base = new URL(resolveBase(baseUrl) || origin, origin)
28 const absolute = /^[a-z][a-z\d+.-]*:/i.test(path)
29 const prefix = base.pathname.replace(/\/+$/, '')
30 const url = new URL(absolute ? path : `${base.origin}${prefix}/${path.replace(/^\/+/, '')}`)
31 if (url.origin !== base.origin) throw new TypeError('Request URL must belong to the configured service')
32 return url.href
33}
34
35export const serviceFetch = (
36 _service: HttpService,
37 path: string,
38 options: RequestOptions = {},
39): Promise<globalThis.Response> => {
40 const {
41 baseUrl,
42 timeoutMs,
43 responseType: _responseType,
44 auth: _auth,
45 handleUnauthorized: _handleUnauthorized,
46 manager: _manager,
47 ...init
48 } = options
49 const signal = AbortSignal.timeout(timeoutMs ?? 20_000)
50 return (globalThis.fetch ?? fetch)(buildUrl(path, baseUrl), {
51 ...init,
52 credentials: 'include',
53 signal: init.signal ?? signal,
54 })
55}
56
57export const request = async <T>(
58 service: HttpService,
59 path: string,
60 options: RequestOptions = {},
61): Promise<Response<T>> => {
62 const response = await serviceFetch(service, path, options)
63 const contentType = response.headers.get('Content-Type')?.toLowerCase() ?? ''
64 let data: unknown = null
65 if (options.method?.toUpperCase() !== 'HEAD' && ![204, 205, 304].includes(response.status)) {
66 if (
67 options.responseType === 'blob' ||
68 /^(application\/(octet-stream|zip|x-tar|gzip)|image\/|video\/|audio\/)/.test(contentType)
69 ) {
70 data = await response.blob()
71 } else if (contentType.includes('json')) {
72 data = await response.json()
73 } else {
74 data = await response.text()
75 }
76 }
77 return { data: data as T, status: response.status, headers: response.headers }
78}
79
80export const authClient = <T>(path: string, options: RequestOptions = {}) => request<T>('auth', path, options)
81
82export const apiClient = <T>(path: string, options: RequestOptions = {}) => request<T>('api', path, options)
83
84export const gitClient = <T>(path: string, options: RequestOptions = {}) => request<T>('git', path, options)
85
86export const configureRequests = (_options: unknown): void => {}
Deleted view/auth/src/shared/lib/http/index.ts
@@ -1,2 +0,0 @@
1export { apiClient, authClient, configureRequests, gitClient, serviceFetch } from './http'
2export type { HttpService, RequestOptions, Response } from './http'
Modified Cargo.lock View Cargo.lock
@@ -1729,8 +1729,6 @@
17291729dependencies = [
17301730 "aes-gcm 0.11.1",
17311731 "base64 0.23.1",
1732 "bytes",
1733 "config 1.0.0",
17341732 "rand_core 0.6.4",
17351733 "rsa",
17361734 "sha2 0.10.9",