mirror of
https://github.com/warp-tech/warpgate.git
synced 2025-09-06 22:55:24 +08:00
External host setting (#162)
This commit is contained in:
parent
6830c0c20d
commit
f0d62412a7
10 changed files with 70 additions and 19 deletions
|
@ -19,31 +19,38 @@ const fn _default_port() -> u16 {
|
|||
22
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn _default_username() -> String {
|
||||
"root".to_owned()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn _default_empty_string() -> String {
|
||||
"".to_owned()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn _default_recordings_path() -> String {
|
||||
"./data/recordings".to_owned()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn _default_database_url() -> Secret<String> {
|
||||
Secret::new("sqlite:data/db".to_owned())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn _default_http_listen() -> String {
|
||||
"0.0.0.0:8888".to_owned()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn _default_retention() -> Duration {
|
||||
Duration::SECOND * 60 * 60 * 24 * 7
|
||||
}
|
||||
|
||||
fn _default_empty_string_vec() -> Vec<String> {
|
||||
#[inline]
|
||||
fn _default_empty_vec<T>() -> Vec<T> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
|
@ -89,7 +96,7 @@ pub struct TargetWebAdminOptions {}
|
|||
#[derive(Debug, Deserialize, Serialize, Clone, Object)]
|
||||
pub struct Target {
|
||||
pub name: String,
|
||||
#[serde(default = "_default_empty_string_vec")]
|
||||
#[serde(default = "_default_empty_vec")]
|
||||
pub allow_roles: Vec<String>,
|
||||
#[serde(flatten)]
|
||||
pub options: TargetOptions,
|
||||
|
@ -239,6 +246,9 @@ pub struct WarpgateConfigStore {
|
|||
#[serde(default)]
|
||||
pub recordings: RecordingsConfig,
|
||||
|
||||
#[serde(default)]
|
||||
pub external_host: Option<String>,
|
||||
|
||||
#[serde(default = "_default_database_url")]
|
||||
pub database_url: Secret<String>,
|
||||
|
||||
|
@ -259,6 +269,7 @@ impl Default for WarpgateConfigStore {
|
|||
users: vec![],
|
||||
roles: vec![],
|
||||
recordings: RecordingsConfig::default(),
|
||||
external_host: None,
|
||||
database_url: _default_database_url(),
|
||||
ssh: SSHConfig::default(),
|
||||
http: HTTPConfig::default(),
|
||||
|
|
|
@ -3,6 +3,7 @@ use std::net::ToSocketAddrs;
|
|||
use crate::common::SessionExt;
|
||||
use poem::session::Session;
|
||||
use poem::web::Data;
|
||||
use poem::Request;
|
||||
use poem_openapi::payload::Json;
|
||||
use poem_openapi::{ApiResponse, Object, OpenApi};
|
||||
use serde::Serialize;
|
||||
|
@ -20,6 +21,7 @@ pub struct Info {
|
|||
version: String,
|
||||
username: Option<String>,
|
||||
selected_target: Option<String>,
|
||||
external_host: Option<String>,
|
||||
ports: PortsInfo,
|
||||
}
|
||||
|
||||
|
@ -34,14 +36,22 @@ impl Api {
|
|||
#[oai(path = "/info", method = "get", operation_id = "get_info")]
|
||||
async fn api_get_info(
|
||||
&self,
|
||||
req: &Request,
|
||||
session: &Session,
|
||||
services: Data<&Services>,
|
||||
) -> poem::Result<InstanceInfoResponse> {
|
||||
let config = services.config.lock().await;
|
||||
let external_host = config
|
||||
.store
|
||||
.external_host
|
||||
.as_deref()
|
||||
.or_else(|| req.header(http::header::HOST))
|
||||
.or_else(|| req.original_uri().host());
|
||||
Ok(InstanceInfoResponse::Ok(Json(Info {
|
||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
username: session.get_username(),
|
||||
selected_target: session.get_target_name(),
|
||||
external_host: external_host.map(&str::to_string),
|
||||
ports: if session.is_authenticated() {
|
||||
PortsInfo {
|
||||
ssh: config
|
||||
|
|
|
@ -12,6 +12,7 @@ use warpgate_common::{Services, TargetOptions, WarpgateServerHandle};
|
|||
|
||||
#[derive(Deserialize)]
|
||||
struct QueryParams {
|
||||
#[serde(rename="warpgate-target")]
|
||||
warpgate_target: Option<String>,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
<script lang="ts">
|
||||
import { api, Target, UserSnapshot } from 'admin/lib/api'
|
||||
import { getSSHUsername } from 'admin/lib/ssh'
|
||||
import { makeExampleSSHCommand, makeSSHUsername } from 'common/ssh'
|
||||
import { Alert, FormGroup, Modal, ModalBody, ModalHeader } from 'sveltestrap'
|
||||
import { serverInfo } from 'gateway/lib/store'
|
||||
import CopyButton from 'common/CopyButton.svelte'
|
||||
import { makeTargetURL } from 'common/http';
|
||||
|
||||
let error: Error|undefined
|
||||
let targets: Target[]|undefined
|
||||
|
@ -20,7 +23,9 @@ load().catch(e => {
|
|||
error = e
|
||||
})
|
||||
|
||||
$: sshUsername = getSSHUsername(selectedUser, selectedTarget)
|
||||
$: sshUsername = makeSSHUsername(selectedTarget?.name, selectedUser?.username)
|
||||
$: exampleCommand = makeExampleSSHCommand(selectedTarget?.name, selectedUser?.username, $serverInfo)
|
||||
$: targetURL = selectedTarget ? makeTargetURL(selectedTarget.name) : ''
|
||||
|
||||
</script>
|
||||
|
||||
|
@ -36,7 +41,11 @@ $: sshUsername = getSSHUsername(selectedUser, selectedTarget)
|
|||
<div class="list-group list-group-flush">
|
||||
{#each targets as target}
|
||||
<!-- svelte-ignore a11y-missing-attribute -->
|
||||
<a class="list-group-item list-group-item-action" on:click={() => selectedTarget = target}>
|
||||
<a class="list-group-item list-group-item-action" on:click={() => {
|
||||
if (target.options.kind !== 'TargetWebAdminOptions') {
|
||||
selectedTarget = target
|
||||
}
|
||||
}}>
|
||||
<strong class="me-auto">
|
||||
{target.name}
|
||||
</strong>
|
||||
|
@ -67,8 +76,8 @@ $: sshUsername = getSSHUsername(selectedUser, selectedTarget)
|
|||
</div>
|
||||
</ModalHeader>
|
||||
<ModalBody>
|
||||
<h3>Access instructions</h3>
|
||||
{#if selectedTarget?.options.kind === 'TargetSSHOptions'}
|
||||
<h3>Connection instructions</h3>
|
||||
{#if users}
|
||||
<FormGroup floating label="Select a user">
|
||||
<select bind:value={selectedUser} class="form-control">
|
||||
|
@ -81,12 +90,21 @@ $: sshUsername = getSSHUsername(selectedUser, selectedTarget)
|
|||
</FormGroup>
|
||||
{/if}
|
||||
|
||||
<FormGroup floating label="SSH username">
|
||||
<FormGroup floating label="SSH username" class="d-flex align-items-center">
|
||||
<input type="text" class="form-control" readonly value={sshUsername} />
|
||||
<CopyButton text={sshUsername} />
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup floating label="Example command">
|
||||
<input type="text" class="form-control" readonly value={'ssh ' + sshUsername + '@warpgate-host -p warpgate-port'} />
|
||||
<FormGroup floating label="Example command" class="d-flex align-items-center">
|
||||
<input type="text" class="form-control" readonly value={exampleCommand} />
|
||||
<CopyButton text={exampleCommand} />
|
||||
</FormGroup>
|
||||
{/if}
|
||||
|
||||
{#if selectedTarget?.options.kind === 'TargetHTTPOptions'}
|
||||
<FormGroup floating label="Access URL" class="d-flex align-items-center">
|
||||
<input type="text" class="form-control" readonly value={targetURL} />
|
||||
<CopyButton text={targetURL} />
|
||||
</FormGroup>
|
||||
{/if}
|
||||
</ModalBody>
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
import type { Target, UserSnapshot } from './api'
|
||||
|
||||
export function getSSHUsername (user: UserSnapshot|undefined, target: Target|undefined): string {
|
||||
return `${user?.username ?? '<username>'}:${target?.name}`
|
||||
}
|
3
warpgate-web/src/common/http.ts
Normal file
3
warpgate-web/src/common/http.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export function makeTargetURL (targetName: string): string {
|
||||
return `${location.protocol}//${location.host}/?warpgate-target=${targetName}`
|
||||
}
|
9
warpgate-web/src/common/ssh.ts
Normal file
9
warpgate-web/src/common/ssh.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import type { Info } from 'gateway/lib/api'
|
||||
|
||||
export function makeSSHUsername (targetName?: string, username?: string): string {
|
||||
return `${username ?? 'username'}:${targetName ?? 'target'}`
|
||||
}
|
||||
|
||||
export function makeExampleSSHCommand (targetName?: string, username?: string, serverInfo?: Info): string {
|
||||
return `ssh ${makeSSHUsername(targetName, username)}@${serverInfo?.externalHost ?? 'warpgate-host'} -p ${serverInfo?.ports.ssh ?? 'warpgate-port'}`
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { faArrowRight } from '@fortawesome/free-solid-svg-icons'
|
||||
import CopyButton from 'common/CopyButton.svelte'
|
||||
import { makeExampleSSHCommand, makeSSHUsername } from 'common/ssh';
|
||||
import { api, Target, TargetKind } from 'gateway/lib/api'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import Fa from 'svelte-fa'
|
||||
|
@ -13,8 +14,8 @@ let targets: Target[]|undefined
|
|||
let selectedTarget: Target|undefined
|
||||
let sshUsername: string
|
||||
|
||||
$: sshUsername = `${$serverInfo?.username}:${selectedTarget?.name}`
|
||||
$: exampleCommand = `ssh ${sshUsername}@warpgate-host -p ${$serverInfo?.ports.ssh}`
|
||||
$: sshUsername = makeSSHUsername(selectedTarget?.name, $serverInfo?.username)
|
||||
$: exampleCommand = makeExampleSSHCommand(selectedTarget?.name, $serverInfo?.username, $serverInfo)
|
||||
|
||||
async function init () {
|
||||
targets = await api.getTargets()
|
||||
|
@ -22,7 +23,7 @@ async function init () {
|
|||
|
||||
function selectTarget (target: Target) {
|
||||
if (target.kind === TargetKind.Http) {
|
||||
loadURL(`/?warpgate_target=${target.name}`)
|
||||
loadURL(`/?warpgate-target=${target.name}`)
|
||||
} else if (target.kind === TargetKind.WebAdmin) {
|
||||
loadURL('/@warpgate/admin')
|
||||
} else {
|
||||
|
@ -46,7 +47,7 @@ init()
|
|||
class="list-group-item list-group-item-action target-item"
|
||||
href={
|
||||
target.kind === TargetKind.Http
|
||||
? `/?warpgate_target=${target.name}`
|
||||
? `/?warpgate-target=${target.name}`
|
||||
: '/@warpgate/admin'
|
||||
}
|
||||
on:click={e => {
|
||||
|
|
|
@ -107,6 +107,9 @@
|
|||
"selected_target": {
|
||||
"type": "string"
|
||||
},
|
||||
"external_host": {
|
||||
"type": "string"
|
||||
},
|
||||
"ports": {
|
||||
"$ref": "#/components/schemas/PortsInfo"
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { writable } from 'svelte/store'
|
||||
import { api, Info } from './api'
|
||||
|
||||
export const serverInfo = writable<Info|null>(null)
|
||||
export const serverInfo = writable<Info|undefined>(undefined)
|
||||
|
||||
export async function reloadServerInfo (): Promise<void> {
|
||||
serverInfo.set(await api.getInfo())
|
||||
|
|
Loading…
Add table
Reference in a new issue