Fix Tailscale route prefix mismatch and increase health check timeout

Mount Tailscale router at /tailscale prefix so all 10 routes resolve
to /api/tailscale/* as expected by middleware, audit logger, and
frontend. Previously 5 routes (status, config, check-connection,
devices, protect-service) resolved to /api/* instead, with config
colliding with the settings route. Strip redundant /tailscale/ prefix
from OAuth routes that were compensating for the missing mount prefix.

Increase default health check timeout from 10s to 20s to reduce false
positives on slower services.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-14 18:44:01 -07:00
parent df0daaad46
commit 70b818c2bd
4 changed files with 27 additions and 23 deletions

View File

@@ -2,12 +2,16 @@
* Tailscale Route Tests
*
* Tests Tailscale status, configuration, and connection-checking endpoints.
* The Tailscale routes are mounted without a prefix on the API router, so:
* - GET /api/status — Tailscale status (returns null status if not installed)
* - POST /api/config — NOTE: shadowed by config/settings.js which also defines POST /config;
* we test it here but it may hit the DashCaddy config route instead.
* - GET /api/check-connection — Check if request comes from Tailscale IP
* - POST /api/tailscale/oauth-config — OAuth credential setup (requires live API)
* The Tailscale routes are mounted at /api/tailscale/ on the API router:
* - GET /api/tailscale/status — Tailscale status
* - POST /api/tailscale/config — Update Tailscale configuration
* - GET /api/tailscale/check-connection — Check if request comes from Tailscale IP
* - GET /api/tailscale/devices — List Tailscale devices
* - POST /api/tailscale/protect-service — Toggle Tailscale-only for a service
* - POST /api/tailscale/oauth-config — OAuth credential setup (requires live API)
* - GET /api/tailscale/api-devices — Enriched device list from API
* - POST /api/tailscale/sync — Trigger API sync
* - GET /api/tailscale/acl — Fetch ACL policy
*/
const request = require('supertest');
@@ -34,9 +38,9 @@ describe('Tailscale Routes', () => {
try { fs.unlinkSync(testConfigFile); } catch (e) { /* ignore */ }
});
describe('GET /api/status (Tailscale status)', () => {
describe('GET /api/tailscale/status', () => {
test('should return 200 with status data', async () => {
const res = await request(app).get('/api/status');
const res = await request(app).get('/api/tailscale/status');
expect(res.statusCode).toBe(200);
expect(res.body.success).toBe(true);
@@ -57,9 +61,9 @@ describe('Tailscale Routes', () => {
});
});
describe('GET /api/check-connection', () => {
describe('GET /api/tailscale/check-connection', () => {
test('should return 200 with connection info', async () => {
const res = await request(app).get('/api/check-connection');
const res = await request(app).get('/api/tailscale/check-connection');
expect(res.statusCode).toBe(200);
expect(res.body.success).toBe(true);
@@ -69,7 +73,7 @@ describe('Tailscale Routes', () => {
});
test('should detect non-Tailscale IP for localhost requests', async () => {
const res = await request(app).get('/api/check-connection');
const res = await request(app).get('/api/tailscale/check-connection');
expect(res.statusCode).toBe(200);
// Supertest connects via loopback, not a 100.x.x.x address
@@ -77,9 +81,9 @@ describe('Tailscale Routes', () => {
});
});
describe('GET /api/devices (Tailscale devices)', () => {
describe('GET /api/tailscale/devices', () => {
test('should return 200 with devices array', async () => {
const res = await request(app).get('/api/devices');
const res = await request(app).get('/api/tailscale/devices');
expect(res.statusCode).toBe(200);
expect(res.body.success).toBe(true);
@@ -122,10 +126,10 @@ describe('Tailscale Routes', () => {
});
});
describe('POST /api/protect-service', () => {
describe('POST /api/tailscale/protect-service', () => {
test('should reject missing subdomain', async () => {
const res = await request(app)
.post('/api/protect-service')
.post('/api/tailscale/protect-service')
.send({});
expect(res.statusCode).toBe(400);