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 {
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() {
is_server_ready = true;
} else {
trace!("Server not ready yet. Sleeping for 1 second");
}
sleep(Duration::from_secs(1));
}
+1
View File
@@ -6,6 +6,7 @@ use std::path::PathBuf;
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct ServerConfig {
pub host: String,
pub origin: Option<String>,
pub port: u16,
}
+24
View File
@@ -20,6 +20,30 @@ fn should_correctly_read_the_config_file() {
let config = config.unwrap();
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);
}
+24 -10
View File
@@ -25,9 +25,31 @@ pub struct Server {
mode: Mode,
pub listener: tokio::net::TcpListener,
pub address: String,
pub origin: Option<String>,
}
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 {
let config = Config::get().expect("[SERVER] Failed to load config");
@@ -44,6 +66,7 @@ impl Server {
router,
mode,
address: server_address.clone(),
origin: config.server.origin.clone(),
listener: tokio::net::TcpListener::bind(&server_address)
.await
.expect("[SERVER] Failed to bind to address"),
@@ -51,14 +74,9 @@ impl Server {
}
pub async fn start(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);
self.display_start_message();
if self.mode == Mode::Dev {
println!(" Ready at: {}\n", server_base_url.blue().bold());
let router = self
.router
.to_owned()
@@ -74,10 +92,6 @@ impl Server {
.await
.expect("Failed to serve development server");
} else {
println!(
" Production server at: {}\n",
server_base_url.blue().bold()
);
let router = self
.router
.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 react from '@vitejs/plugin-react-swc'
@@ -12,9 +12,9 @@ describe('createJsonConfig', () => {
writeFileSpy.mockClear()
})
const sampleConfig = { server: { host: 'h', port: 1 } }
it('should process config with only server property', async () => {
const sampleConfig = { server: { host: 'h', origin: null, port: 1 } }
await createJsonConfig(sampleConfig)
expect(writeFileSpy).toHaveBeenCalledWith(
@@ -25,6 +25,8 @@ describe('createJsonConfig', () => {
})
it('should process config with plugins', async () => {
const sampleConfig = { server: { host: 'h', origin: null, port: 1 } }
await createJsonConfig({ ...sampleConfig, vite: { plugins: [react()] } })
expect(writeFileSpy).toHaveBeenCalledWith(
@@ -33,4 +35,16 @@ describe('createJsonConfig', () => {
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 = {}
expect(normalizeConfig(config)).toStrictEqual({
server: { host: 'localhost', port: 3000 },
server: {
host: 'localhost',
origin: null,
port: 3000,
},
vite: {
alias: undefined,
css: undefined,
@@ -28,7 +32,11 @@ describe('normalizeConfig', () => {
it('should return an empty config if invalid values are provided', () => {
// @ts-expect-error testing invalid config
expect(normalizeConfig({ invalid: true })).toStrictEqual({
server: { host: 'localhost', port: 3000 },
server: {
host: 'localhost',
origin: null,
port: 3000,
},
vite: {
alias: 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', () => {
it('should not modify alias pointing to packages', () => {
const libraryName = '@tabler/icons-react'
@@ -71,6 +71,7 @@ export const normalizeConfig = (config: TuonoConfig): InternalTuonoConfig => {
return {
server: {
host: config.server?.host ?? 'localhost',
origin: config.server?.origin ?? null,
port: config.server?.port ?? 3000,
},
vite: {
+1
View File
@@ -11,6 +11,7 @@ import type {
export interface TuonoConfig {
server?: {
host?: string
origin?: string | null
port?: number
}
vite?: {
+6 -3
View File
@@ -3,13 +3,16 @@ import type { JSX } from 'react'
import { useTuonoContextServerPayload } from './TuonoContext'
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 => {
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 (
<>
+1
View File
@@ -25,6 +25,7 @@ export interface ServerPayload<TData = unknown> {
/** Available only on 'Dev' mode */
devServerConfig?: {
port: number
origin: string | null
host: string
}
}