Add native self-hosted instance connection to fluxer_desktop

Trimmed monorepo checkout (fluxer_desktop + packages/voice_engine_v2 +
tools/ci) with a "Connect to a Different Server" menu item and popout
that lets the desktop app switch to any self-hosted Fluxer instance,
plus fixes for well-known discovery on single-domain self-hosted
deployments and a false-positive ERR_ABORTED on same-origin client
redirects during the switch. Defaults to chat.fluxr.chat and uses an
isolated userData directory from the official build.
This commit is contained in:
2026-07-01 18:22:43 -04:00
commit 682afacd30
1763 changed files with 613720 additions and 0 deletions
@@ -0,0 +1,302 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
use napi::Task;
use napi::bindgen_prelude::{AsyncTask, Env, Error, Result, Status};
use napi_derive::napi;
#[cfg(any(target_os = "windows", test))]
const DROPFILES_HEADER_SIZE: usize = 20;
#[cfg(any(target_os = "windows", test))]
const DROPFILES_PFILES_OFFSET: usize = 0;
#[cfg(any(target_os = "windows", test))]
const DROPFILES_FWIDE_OFFSET: usize = 16;
pub struct WriteFileReferenceTask {
path: String,
}
#[napi(js_name = "writeFileReferenceToClipboard")]
pub fn write_file_reference_to_clipboard(
file_path: String,
) -> Result<AsyncTask<WriteFileReferenceTask>> {
validate_file_path(&file_path)?;
Ok(AsyncTask::new(WriteFileReferenceTask { path: file_path }))
}
impl Task for WriteFileReferenceTask {
type Output = ();
type JsValue = ();
fn compute(&mut self) -> Result<Self::Output> {
platform::write_file_reference(&self.path)
}
fn resolve(&mut self, _env: Env, _output: Self::Output) -> Result<Self::JsValue> {
Ok(())
}
}
fn validate_file_path(path: &str) -> Result<()> {
if path.is_empty() {
return Err(Error::new(Status::InvalidArg, "path must be non-empty"));
}
if path.as_bytes().contains(&0) {
return Err(Error::new(
Status::InvalidArg,
"path must not contain NUL bytes",
));
}
Ok(())
}
#[cfg(any(target_os = "windows", test))]
fn build_hdrop_payload(path: &str) -> Vec<u8> {
let utf16: Vec<u16> = path.encode_utf16().collect();
let mut payload = vec![0; DROPFILES_HEADER_SIZE + ((utf16.len() + 2) * size_of::<u16>())];
payload[DROPFILES_PFILES_OFFSET..DROPFILES_PFILES_OFFSET + size_of::<u32>()]
.copy_from_slice(&(DROPFILES_HEADER_SIZE as u32).to_le_bytes());
payload[DROPFILES_FWIDE_OFFSET..DROPFILES_FWIDE_OFFSET + size_of::<i32>()]
.copy_from_slice(&1_i32.to_le_bytes());
let mut offset = DROPFILES_HEADER_SIZE;
for code_unit in utf16 {
payload[offset..offset + size_of::<u16>()].copy_from_slice(&code_unit.to_le_bytes());
offset += size_of::<u16>();
}
payload
}
#[cfg(target_os = "windows")]
mod platform {
use super::build_hdrop_payload;
use napi::bindgen_prelude::{Error, Result, Status};
use std::ffi::c_void;
use std::ptr::{copy_nonoverlapping, null_mut};
use windows_sys::Win32::Foundation::{GetLastError, GlobalFree, HGLOBAL, HWND};
use windows_sys::Win32::System::DataExchange::{
CloseClipboard, EmptyClipboard, OpenClipboard, SetClipboardData,
};
use windows_sys::Win32::System::Memory::{
GMEM_MOVEABLE, GMEM_ZEROINIT, GlobalAlloc, GlobalLock, GlobalUnlock,
};
use windows_sys::Win32::System::Ole::CF_HDROP;
use windows_sys::Win32::UI::WindowsAndMessaging::{CreateWindowExW, DestroyWindow};
const STATIC_WINDOW_CLASS: [u16; 7] = [
'S' as u16, 'T' as u16, 'A' as u16, 'T' as u16, 'I' as u16, 'C' as u16, 0,
];
struct OwnedGlobal {
handle: HGLOBAL,
}
impl OwnedGlobal {
fn new(handle: HGLOBAL) -> Self {
Self { handle }
}
fn handle(&self) -> HGLOBAL {
self.handle
}
fn into_clipboard(mut self) -> HGLOBAL {
let handle = self.handle;
self.handle = null_mut();
handle
}
}
impl Drop for OwnedGlobal {
fn drop(&mut self) {
if !self.handle.is_null() {
unsafe {
let _ = GlobalFree(self.handle);
}
}
}
}
struct LockedGlobal {
handle: HGLOBAL,
ptr: *mut c_void,
}
impl LockedGlobal {
fn new(handle: HGLOBAL) -> Result<Self> {
let ptr = unsafe { GlobalLock(handle) };
if ptr.is_null() {
return Err(last_error("GlobalLock failed"));
}
Ok(Self { handle, ptr })
}
fn as_mut_ptr(&self) -> *mut u8 {
self.ptr.cast()
}
}
impl Drop for LockedGlobal {
fn drop(&mut self) {
unsafe {
let _ = GlobalUnlock(self.handle);
}
}
}
struct ClipboardOwnerWindow {
hwnd: HWND,
}
impl ClipboardOwnerWindow {
fn create() -> Result<Self> {
let hwnd = unsafe {
CreateWindowExW(
0,
STATIC_WINDOW_CLASS.as_ptr(),
null_mut(),
0,
0,
0,
0,
0,
null_mut(),
null_mut(),
null_mut(),
null_mut(),
)
};
if hwnd.is_null() {
return Err(last_error("CreateWindowExW failed"));
}
Ok(Self { hwnd })
}
fn hwnd(&self) -> HWND {
self.hwnd
}
}
impl Drop for ClipboardOwnerWindow {
fn drop(&mut self) {
unsafe {
let _ = DestroyWindow(self.hwnd);
}
}
}
struct ClipboardGuard;
impl ClipboardGuard {
fn open(hwnd: HWND) -> Result<Self> {
if unsafe { OpenClipboard(hwnd) } == 0 {
return Err(last_error("OpenClipboard failed"));
}
Ok(Self)
}
}
impl Drop for ClipboardGuard {
fn drop(&mut self) {
unsafe {
let _ = CloseClipboard();
}
}
}
pub(super) fn write_file_reference(path: &str) -> Result<()> {
let payload = build_hdrop_payload(path);
let handle = unsafe { GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, payload.len()) };
if handle.is_null() {
return Err(last_error("GlobalAlloc failed"));
}
let memory = OwnedGlobal::new(handle);
{
let locked = LockedGlobal::new(memory.handle())?;
unsafe {
copy_nonoverlapping(payload.as_ptr(), locked.as_mut_ptr(), payload.len());
}
}
let owner = ClipboardOwnerWindow::create()?;
let _clipboard = ClipboardGuard::open(owner.hwnd())?;
if unsafe { EmptyClipboard() } == 0 {
return Err(last_error("EmptyClipboard failed"));
}
if unsafe { SetClipboardData(CF_HDROP as u32, memory.handle()) }.is_null() {
return Err(last_error("SetClipboardData failed"));
}
let _ = memory.into_clipboard();
Ok(())
}
fn last_error(message: &str) -> Error {
Error::new(
Status::GenericFailure,
format!("{message} (err {})", unsafe { GetLastError() }),
)
}
}
#[cfg(not(target_os = "windows"))]
mod platform {
use napi::bindgen_prelude::{Error, Result, Status};
pub(super) fn write_file_reference(_path: &str) -> Result<()> {
Err(Error::new(
Status::GenericFailure,
"win-clipboard not supported on this platform",
))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn validation_rejects_empty_paths() {
let err = validate_file_path("").expect_err("empty path should be rejected");
assert_eq!(err.status, Status::InvalidArg);
assert_eq!(err.reason, "path must be non-empty");
}
#[test]
fn validation_rejects_nul_bytes() {
let err = validate_file_path("before\0after").expect_err("NUL path should be rejected");
assert_eq!(err.status, Status::InvalidArg);
assert_eq!(err.reason, "path must not contain NUL bytes");
}
#[test]
fn hdrop_payload_uses_unicode_dropfiles_layout() {
let payload = build_hdrop_payload("C:\\Temp\\a.txt");
assert_eq!(payload.len(), DROPFILES_HEADER_SIZE + ((13 + 2) * 2));
assert_eq!(
u32::from_le_bytes(payload[0..4].try_into().unwrap()),
DROPFILES_HEADER_SIZE as u32
);
assert_eq!(i32::from_le_bytes(payload[4..8].try_into().unwrap()), 0);
assert_eq!(i32::from_le_bytes(payload[8..12].try_into().unwrap()), 0);
assert_eq!(i32::from_le_bytes(payload[12..16].try_into().unwrap()), 0);
assert_eq!(i32::from_le_bytes(payload[16..20].try_into().unwrap()), 1);
let path_units: Vec<u16> = payload[DROPFILES_HEADER_SIZE..payload.len() - 4]
.chunks_exact(2)
.map(|chunk| u16::from_le_bytes(chunk.try_into().unwrap()))
.collect();
assert_eq!(String::from_utf16(&path_units).unwrap(), "C:\\Temp\\a.txt");
assert_eq!(&payload[payload.len() - 4..], &[0, 0, 0, 0]);
}
#[cfg(not(target_os = "windows"))]
#[test]
fn non_windows_worker_preserves_stub_error_contract() {
let err =
platform::write_file_reference("/tmp/file.txt").expect_err("non-Windows should fail");
assert_eq!(err.status, Status::GenericFailure);
assert_eq!(err.reason, "win-clipboard not supported on this platform");
}
}