Compare commits

...

3 Commits

Author SHA1 Message Date
aewing dd2f00cd37 Convert to NAS folder opener HTTP server
- Replace Slint UI with HTTP server using actix-web
- Add CORS support for prism.ccllc.pro
- Implement /open endpoint to open NAS folders in file explorer
- Add /health endpoint for server status
- Support Windows, macOS, and Linux
- Update README with usage instructions
2026-01-16 15:02:02 +00:00
eewing fad5d0991b Merge branch 'main' of https://gitea.ccllc.pro/Cardoza_Construction/RPC 2026-01-15 16:49:34 -06:00
eewing 73c1f58a8f Add API key generator application with Slint UI 2026-01-15 16:49:20 -06:00
4 changed files with 277 additions and 1 deletions
+16
View File
@@ -0,0 +1,16 @@
# Rust
/target/
**/*.rs.bk
*.pdb
Cargo.lock
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
+10
View File
@@ -0,0 +1,10 @@
[package]
name = "rpc"
version = "0.1.0"
edition = "2021"
[dependencies]
actix-web = "4.4"
actix-cors = "0.6"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
+132 -1
View File
@@ -1,2 +1,133 @@
# RPC
# RPC - NAS Folder Opener
A local HTTP server that opens NAS folder paths in the system file explorer. Designed to work with the PRISM web application hosted at `prism.ccllc.pro`.
## Purpose
Browsers cannot directly open network file shares (NAS folders) due to security restrictions. This tool bridges that gap by:
1. Running a local HTTP server on `localhost:8080`
2. Accepting requests from the PRISM web application
3. Opening NAS folder paths in the user's file explorer
## How It Works
1. **User visits PRISM** at `prism.ccllc.pro` and views job information
2. **User clicks a folder link** from the `Job_Folder_Link` field
3. **PRISM makes a request** to `http://localhost:8080/open?path=<NAS_PATH>`
4. **RPC tool opens the path** in the system file explorer
## Installation
### Prerequisites
- Rust toolchain installed ([rustup.rs](https://rustup.rs/))
### Build
```bash
cargo build --release
```
The binary will be in `target/release/rpc` (or `target/release/rpc.exe` on Windows).
### Run
```bash
cargo run
```
Or run the release binary:
```bash
./target/release/rpc
```
The server will start on `http://127.0.0.1:8080`.
## API Endpoints
### `GET /open?path=<NAS_PATH>`
Opens a NAS folder path in the file explorer.
**Query Parameters:**
- `path` (required): The NAS folder path to open. Supports:
- UNC paths: `\\server\share\folder`
- SMB paths: `smb://server/share/folder`
- Unix-style: `//server/share/folder`
**Response:**
```json
{
"success": true,
"message": "Opening path: \\\\server\\share\\folder"
}
```
**Error Response:**
```json
{
"success": false,
"message": "Error description"
}
```
### `GET /health`
Health check endpoint.
**Response:**
```json
{
"status": "ok",
"service": "NAS Folder Opener",
"version": "0.1.0"
}
```
## Security
- The server only accepts requests from `https://prism.ccllc.pro` (CORS)
- Paths are validated to ensure they are network shares
- Server only binds to `127.0.0.1` (localhost), not exposed to network
## Integration with PRISM
In your Svelte component, add a function to open NAS folders:
```typescript
async function openNasFolder(nasPath: string) {
try {
const response = await fetch(
`http://localhost:8080/open?path=${encodeURIComponent(nasPath)}`
);
const result = await response.json();
if (!result.success) {
alert(`Failed to open folder: ${result.message}`);
}
} catch (error) {
alert('Please ensure the NAS opener tool is running on your computer');
console.error('Error opening NAS folder:', error);
}
}
```
Then use it when displaying job folder links:
```svelte
<button onclick={() => openNasFolder(job.Job_Folder_Link)}>
Open Folder
</button>
```
## Cross-Platform Support
- **Windows**: Uses `explorer.exe`
- **macOS**: Uses `open` command
- **Linux**: Uses `xdg-open` command
## License
Apache-2.0
+119
View File
@@ -0,0 +1,119 @@
use actix_cors::Cors;
use actix_web::{web, App, HttpServer, HttpResponse, Result as ActixResult};
use serde::{Deserialize, Serialize};
use std::process::Command;
use std::collections::HashMap;
#[derive(Deserialize)]
struct OpenPathQuery {
path: String,
}
#[derive(Serialize)]
struct OpenPathResponse {
success: bool,
message: String,
}
/// Opens a NAS path in the file explorer
async fn open_path(query: web::Query<OpenPathQuery>) -> ActixResult<HttpResponse> {
let path = query.path.trim();
// Validate that the path is a network path (UNC path on Windows or SMB path)
// Accepts paths like: \\server\share, //server/share, or smb://server/share
let is_valid_nas_path = path.starts_with("\\\\")
|| path.starts_with("//")
|| path.starts_with("smb://")
|| path.starts_with("file://");
if !is_valid_nas_path {
return Ok(HttpResponse::BadRequest().json(OpenPathResponse {
success: false,
message: "Invalid path. Path must be a network share (UNC path starting with \\\\ or //)".to_string(),
}));
}
// Normalize the path for the OS
let normalized_path = if path.starts_with("smb://") {
// Convert smb:// to UNC format
path.replace("smb://", "\\\\").replace("/", "\\")
} else if path.starts_with("//") {
// Convert // to \\
path.replace("//", "\\\\")
} else if path.starts_with("file://") {
// Remove file:// prefix
path.strip_prefix("file://").unwrap_or(path).to_string()
} else {
path.to_string()
};
// Determine the OS and use the appropriate command
let result = if cfg!(target_os = "windows") {
// Windows: use explorer.exe
Command::new("explorer")
.arg(&normalized_path)
.spawn()
} else if cfg!(target_os = "macos") {
// macOS: use open command
Command::new("open")
.arg(&normalized_path)
.spawn()
} else {
// Linux: try xdg-open, fallback to other file managers
Command::new("xdg-open")
.arg(&normalized_path)
.spawn()
};
match result {
Ok(_) => Ok(HttpResponse::Ok().json(OpenPathResponse {
success: true,
message: format!("Opening path: {}", normalized_path),
})),
Err(e) => {
eprintln!("Error opening path: {}", e);
Ok(HttpResponse::InternalServerError().json(OpenPathResponse {
success: false,
message: format!("Failed to open path: {}", e),
}))
}
}
}
/// Health check endpoint
async fn health() -> ActixResult<HttpResponse> {
Ok(HttpResponse::Ok().json(serde_json::json!({
"status": "ok",
"service": "NAS Folder Opener",
"version": "0.1.0"
})))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
println!("Starting NAS Folder Opener server...");
println!("Server will listen on http://127.0.0.1:8080");
println!("Allowed origin: https://prism.ccllc.pro");
HttpServer::new(|| {
// Configure CORS to allow requests from prism.ccllc.pro
let cors = Cors::default()
.allowed_origin("https://prism.ccllc.pro")
.allowed_origin("http://localhost:5173") // Allow local dev
.allowed_origin("http://127.0.0.1:5173") // Allow local dev
.allowed_methods(vec!["GET", "POST", "OPTIONS"])
.allowed_headers(vec![
actix_web::http::header::CONTENT_TYPE,
actix_web::http::header::AUTHORIZATION,
])
.max_age(3600);
App::new()
.wrap(cors)
.route("/health", web::get().to(health))
.route("/open", web::get().to(open_path))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}