Angular需配置app.get("*",[...]);才能运行,SSH关机功能开发求助
Hey there! Let's break down your two issues one by one—they're both common hurdles when building full-stack Angular + Express apps, so you're in good company.
app.get("*", ...) in Express Angular is a single-page application (SPA)—all your frontend routing happens client-side, not on the server. When you visit a direct URL like /dashboard or /settings (instead of starting at the root /), your Express server doesn't recognize those routes by default. It would normally throw a 404 error because it only knows about the API endpoints you've defined.
The app.get("*", ...) rule acts as a catch-all: it tells Express to send your Angular index.html file for any request that isn't matched by an existing API route. This lets Angular take over and handle the client-side routing correctly.
Correct Order Matters!
Make sure you define all your API routes before the catch-all rule. If you put the catch-all first, your API requests will get redirected to index.html instead of hitting your endpoints. Here's a typical setup:
const express = require('express'); const path = require('path'); const app = express(); // First: Define your API routes app.get('/api/status', (req, res) => { res.json({ status: 'running' }); }); // Then: Serve Angular's static files (CSS, JS, assets) app.use(express.static(path.join(__dirname, 'dist/your-angular-app'))); // Finally: Catch-all route for Angular client-side routing app.get('*', (req, res) => { res.sendFile(path.join(__dirname, 'dist/your-angular-app/index.html')); }); app.listen(3000, () => console.log('Server running on port 3000'));
You're absolutely right to avoid handling SSH directly in browser scripts—it's a huge security risk (you'd expose credentials or private keys to the client). Instead, we'll build an Express endpoint that acts as a secure middleman to execute the shutdown command via SSH.
Step 1: Use a Node.js SSH Library
We'll use the ssh2 package (a popular, robust SSH client for Node.js). Install it first:
npm install ssh2
Step 2: Build the Secure Express Endpoint
Create a POST endpoint that:
- Verifies the user is authorized (e.g., checks for a valid admin session token—never let unauthenticated users call this!)
- Establishes an SSH connection to your ownCloud server
- Executes the graceful shutdown command
- Returns a success/error response to the Angular frontend
Here's a sample implementation:
const { Client } = require('ssh2'); // Store SSH credentials securely (use environment variables, not hardcoded!) const SSH_CONFIG = { host: process.env.SSH_SERVER_HOST, port: process.env.SSH_SERVER_PORT || 22, username: process.env.SSH_USERNAME, // Use either password or private key (private key is more secure) password: process.env.SSH_PASSWORD, // privateKey: require('fs').readFileSync('/path/to/private/key') }; // Admin-only shutdown endpoint app.post('/api/server/shutdown', (req, res) => { // First: Add your auth check here (e.g., verify req.user is admin) // Example: if (!req.user || !req.user.isAdmin) return res.status(403).send('Unauthorized'); const conn = new Client(); conn.on('ready', () => { console.log('SSH connection ready'); // Execute graceful shutdown command (adjust based on your OS) conn.exec('shutdown -h now', (err, stream) => { if (err) { conn.end(); return res.status(500).json({ error: `Failed to execute shutdown: ${err.message}` }); } stream.on('close', (code, signal) => { conn.end(); if (code === 0) { res.json({ success: true, message: 'Shutdown command sent successfully' }); } else { res.status(500).json({ error: `Shutdown command exited with code ${code}` }); } }).on('data', (data) => { console.log(`SSH stdout: ${data}`); }).stderr.on('data', (data) => { console.error(`SSH stderr: ${data}`); }); }); }).on('error', (err) => { res.status(500).json({ error: `SSH connection failed: ${err.message}` }); }).connect(SSH_CONFIG); });
Step 3: Connect Angular Frontend to the Endpoint
In your Angular component, add a button that calls this endpoint via HttpClient. Make sure to handle loading states and error messages:
import { HttpClient } from '@angular/common/http'; import { Component } from '@angular/core'; @Component({ selector: 'app-server-controls', template: ` <button (click)="shutdownServer()" [disabled]="isLoading"> {{ isLoading ? 'Shutting down...' : 'Stop Server' }} </button> <div *ngIf="errorMessage" class="text-danger">{{ errorMessage }}</div> ` }) export class ServerControlsComponent { isLoading = false; errorMessage = ''; constructor(private http: HttpClient) {} shutdownServer() { this.isLoading = true; this.errorMessage = ''; this.http.post('/api/server/shutdown', {}) .subscribe({ next: (res: any) => { this.isLoading = false; // Show success message to user alert(res.message); }, error: (err) => { this.isLoading = false; this.errorMessage = err.error?.error || 'Failed to shut down server'; } }); } }
Critical Security Notes
- Never hardcode credentials: Use environment variables (e.g.,
dotenvpackage) or a secure secrets manager. - Add authentication: Only allow authenticated admin users to access this endpoint—use sessions, JWT tokens, or OAuth to verify user permissions.
- Limit command scope: Avoid allowing arbitrary command execution; hardcode the shutdown command instead of accepting user input.
- Handle errors gracefully: Return clear error messages to the frontend without exposing sensitive server details.
内容的提问来源于stack exchange,提问作者C. Dautermann

