feat: add origin config option (#582)

Co-authored-by: Marco Pasqualetti <marco.pasqualetti@live.com>
This commit is contained in:
pveierland
2025-02-28 01:25:03 +08:00
committed by GitHub
parent 5f6bad637c
commit 1ba94238ed
10 changed files with 114 additions and 19 deletions
+7 -1
View File
@@ -86,12 +86,18 @@ pub fn build(mut app: App, ssg: bool, no_js_emit: bool) {
while !is_server_ready { while !is_server_ready {
trace!("Checking server availability"); trace!("Checking server availability");
let server_url = format!("http://{}:{}", config.server.host, config.server.port);
let server_url = match &config.server.origin {
Some(origin) => origin.clone(),
None => format!("http://{}:{}", config.server.host, config.server.port),
};
if reqwest_client.get(&server_url).send().is_ok() { if reqwest_client.get(&server_url).send().is_ok() {
is_server_ready = true; is_server_ready = true;
} else { } else {
trace!("Server not ready yet. Sleeping for 1 second"); trace!("Server not ready yet. Sleeping for 1 second");
} }
sleep(Duration::from_secs(1)); sleep(Duration::from_secs(1));
} }
+1
View File
@@ -6,6 +6,7 @@ use std::path::PathBuf;
#[derive(Deserialize, Serialize, Debug, Clone)] #[derive(Deserialize, Serialize, Debug, Clone)]
pub struct ServerConfig { pub struct ServerConfig {
pub host: String, pub host: String,
pub origin: Option<String>,
pub port: u16, pub port: u16,
} }
+24
View File
@@ -20,6 +20,30 @@ fn should_correctly_read_the_config_file() {
let config = config.unwrap(); let config = config.unwrap();
assert_eq!(config.server.host, "localhost"); assert_eq!(config.server.host, "localhost");
assert_eq!(config.server.origin, None);
assert_eq!(config.server.port, 3000);
}
#[test]
#[serial]
fn should_correctly_read_the_config_file_with_origin() {
let folder = TempTuonoProject::new();
folder.add_file_with_content(
"./.tuono/config/config.json",
r#"{ "server": {"host": "localhost", "origin": "https://tuono.localhost", "port": 3000}}"#,
);
let config = Config::get();
assert!(config.is_ok());
let config = config.unwrap();
assert_eq!(config.server.host, "localhost".to_string());
assert_eq!(
config.server.origin,
Some("https://tuono.localhost".to_string())
);
assert_eq!(config.server.port, 3000); assert_eq!(config.server.port, 3000);
} }
+24 -10
View File
@@ -25,9 +25,31 @@ pub struct Server {
mode: Mode, mode: Mode,
pub listener: tokio::net::TcpListener, pub listener: tokio::net::TcpListener,
pub address: String, pub address: String,
pub origin: Option<String>,
} }
impl Server { impl Server {
fn display_start_message(&self) {
/*
* Format the server address as a valid URL so that it becomes clickable in the CLI
* @see https://github.com/tuono-labs/tuono/issues/460
*/
let server_base_url = format!("http://{}", self.address);
if self.mode == Mode::Dev {
println!(" Ready at: {}\n", server_base_url.blue().bold());
} else {
println!(
" Production server at: {}\n",
server_base_url.blue().bold()
);
}
if let Some(origin) = &self.origin {
println!(" Origin: {}\n", origin.blue().bold());
}
}
pub async fn init(router: Router, mode: Mode) -> Server { pub async fn init(router: Router, mode: Mode) -> Server {
let config = Config::get().expect("[SERVER] Failed to load config"); let config = Config::get().expect("[SERVER] Failed to load config");
@@ -44,6 +66,7 @@ impl Server {
router, router,
mode, mode,
address: server_address.clone(), address: server_address.clone(),
origin: config.server.origin.clone(),
listener: tokio::net::TcpListener::bind(&server_address) listener: tokio::net::TcpListener::bind(&server_address)
.await .await
.expect("[SERVER] Failed to bind to address"), .expect("[SERVER] Failed to bind to address"),
@@ -51,14 +74,9 @@ impl Server {
} }
pub async fn start(self) { pub async fn start(self) {
/* self.display_start_message();
* Format the server address as a valid URL so that it becomes clickable in the CLI
* @see https://github.com/tuono-labs/tuono/issues/460
*/
let server_base_url = format!("http://{}", self.address);
if self.mode == Mode::Dev { if self.mode == Mode::Dev {
println!(" Ready at: {}\n", server_base_url.blue().bold());
let router = self let router = self
.router .router
.to_owned() .to_owned()
@@ -74,10 +92,6 @@ impl Server {
.await .await
.expect("Failed to serve development server"); .expect("Failed to serve development server");
} else { } else {
println!(
" Production server at: {}\n",
server_base_url.blue().bold()
);
let router = self let router = self
.router .router
.to_owned() .to_owned()
@@ -1,4 +1,4 @@
import fs from 'fs/promises' import fs from 'node:fs/promises'
import { beforeEach, describe, expect, it, vitest } from 'vitest' import { beforeEach, describe, expect, it, vitest } from 'vitest'
import react from '@vitejs/plugin-react-swc' import react from '@vitejs/plugin-react-swc'
@@ -12,9 +12,9 @@ describe('createJsonConfig', () => {
writeFileSpy.mockClear() writeFileSpy.mockClear()
}) })
const sampleConfig = { server: { host: 'h', port: 1 } }
it('should process config with only server property', async () => { it('should process config with only server property', async () => {
const sampleConfig = { server: { host: 'h', origin: null, port: 1 } }
await createJsonConfig(sampleConfig) await createJsonConfig(sampleConfig)
expect(writeFileSpy).toHaveBeenCalledWith( expect(writeFileSpy).toHaveBeenCalledWith(
@@ -25,6 +25,8 @@ describe('createJsonConfig', () => {
}) })
it('should process config with plugins', async () => { it('should process config with plugins', async () => {
const sampleConfig = { server: { host: 'h', origin: null, port: 1 } }
await createJsonConfig({ ...sampleConfig, vite: { plugins: [react()] } }) await createJsonConfig({ ...sampleConfig, vite: { plugins: [react()] } })
expect(writeFileSpy).toHaveBeenCalledWith( expect(writeFileSpy).toHaveBeenCalledWith(
@@ -33,4 +35,16 @@ describe('createJsonConfig', () => {
expect.any(String), expect.any(String),
) )
}) })
it('should process config with only server property including origin', async () => {
const sampleConfig = { server: { host: 'h', origin: 'o', port: 1 } }
await createJsonConfig(sampleConfig)
expect(writeFileSpy).toHaveBeenCalledWith(
expect.any(String),
expect.stringContaining(JSON.stringify(sampleConfig)),
expect.any(String),
)
})
}) })
@@ -15,7 +15,11 @@ describe('normalizeConfig', () => {
const config: TuonoConfig = {} const config: TuonoConfig = {}
expect(normalizeConfig(config)).toStrictEqual({ expect(normalizeConfig(config)).toStrictEqual({
server: { host: 'localhost', port: 3000 }, server: {
host: 'localhost',
origin: null,
port: 3000,
},
vite: { vite: {
alias: undefined, alias: undefined,
css: undefined, css: undefined,
@@ -28,7 +32,11 @@ describe('normalizeConfig', () => {
it('should return an empty config if invalid values are provided', () => { it('should return an empty config if invalid values are provided', () => {
// @ts-expect-error testing invalid config // @ts-expect-error testing invalid config
expect(normalizeConfig({ invalid: true })).toStrictEqual({ expect(normalizeConfig({ invalid: true })).toStrictEqual({
server: { host: 'localhost', port: 3000 }, server: {
host: 'localhost',
origin: null,
port: 3000,
},
vite: { vite: {
alias: undefined, alias: undefined,
css: undefined, css: undefined,
@@ -55,6 +63,28 @@ describe('normalizeConfig', () => {
}) })
}) })
describe('server - origin', () => {
it('should assign the origin defined by the user', () => {
const config: TuonoConfig = {
server: {
host: '0.0.0.0',
origin: 'https://tuono.localhost',
port: 8080,
},
}
expect(normalizeConfig(config)).toStrictEqual(
expect.objectContaining({
server: expect.objectContaining({
host: '0.0.0.0',
origin: 'https://tuono.localhost',
port: 8080,
}) as unknown,
}),
)
})
})
describe('vite - alias', () => { describe('vite - alias', () => {
it('should not modify alias pointing to packages', () => { it('should not modify alias pointing to packages', () => {
const libraryName = '@tabler/icons-react' const libraryName = '@tabler/icons-react'
@@ -71,6 +71,7 @@ export const normalizeConfig = (config: TuonoConfig): InternalTuonoConfig => {
return { return {
server: { server: {
host: config.server?.host ?? 'localhost', host: config.server?.host ?? 'localhost',
origin: config.server?.origin ?? null,
port: config.server?.port ?? 3000, port: config.server?.port ?? 3000,
}, },
vite: { vite: {
+1
View File
@@ -11,6 +11,7 @@ import type {
export interface TuonoConfig { export interface TuonoConfig {
server?: { server?: {
host?: string host?: string
origin?: string | null
port?: number port?: number
} }
vite?: { vite?: {
+6 -3
View File
@@ -3,13 +3,16 @@ import type { JSX } from 'react'
import { useTuonoContextServerPayload } from './TuonoContext' import { useTuonoContextServerPayload } from './TuonoContext'
const VITE_PROXY_PATH = '/vite-server' const VITE_PROXY_PATH = '/vite-server'
const DEFAULT_SERVER_CONFIG = { host: 'localhost', port: 3000 } const DEFAULT_SERVER_CONFIG = { host: 'localhost', origin: null, port: 3000 }
export const DevResources = (): JSX.Element => { export const DevResources = (): JSX.Element => {
const { devServerConfig } = useTuonoContextServerPayload() const { devServerConfig } = useTuonoContextServerPayload()
const { host, port } = devServerConfig ?? DEFAULT_SERVER_CONFIG const { host, origin, port } = devServerConfig ?? DEFAULT_SERVER_CONFIG
const viteBaseUrl = `http://${host}:${port}${VITE_PROXY_PATH}` const viteBaseUrl =
origin != null
? `${origin}${VITE_PROXY_PATH}`
: `http://${host}:${port}${VITE_PROXY_PATH}`
return ( return (
<> <>
+1
View File
@@ -25,6 +25,7 @@ export interface ServerPayload<TData = unknown> {
/** Available only on 'Dev' mode */ /** Available only on 'Dev' mode */
devServerConfig?: { devServerConfig?: {
port: number port: number
origin: string | null
host: string host: string
} }
} }