Skip to content

Commit bfea62c

Browse files
committed
Serve TeslaMate at root and dashboard under subpath
1 parent fc763b6 commit bfea62c

7 files changed

Lines changed: 38 additions & 57 deletions

File tree

.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ IMAGE_NAMESPACE=your-namespace
44
IMAGE_TAG=latest
55

66
# Only Nginx is published to the host.
7-
WEB_PORT=8080
7+
WEB_PORT=80
88
BASIC_AUTH_USER=admin
99
BASIC_AUTH_PASSWORD=change-this-password
1010

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
v
1212
Nginx / Frontend -- 内网 HTTP --> FastAPI Backend -- Docker 内网 --> PostgreSQL
1313
|
14-
└── 可选代理 /teslamate/ 到内网 TeslaMate 初始化页面
14+
└── 根路径 / 反向代理到内网 TeslaMate
1515
1616
TeslaMate Collector -- 内网 --> PostgreSQL + MQTT
1717
```
@@ -49,7 +49,7 @@ sudo docker compose -f compose.local.yaml up --build
4949
访问:
5050

5151
```text
52-
http://localhost:8080
52+
http://localhost:8080/dashboard/
5353
用户名:admin
5454
密码:change-me-local
5555
```
@@ -88,7 +88,7 @@ IMAGE_REGISTRY=registry.cn-shenzhen.aliyuncs.com
8888
IMAGE_NAMESPACE=your-namespace
8989
IMAGE_TAG=latest
9090
91-
WEB_PORT=8080
91+
WEB_PORT=80
9292
BASIC_AUTH_USER=admin
9393
BASIC_AUTH_PASSWORD=change-this-password
9494
@@ -145,10 +145,10 @@ sudo docker compose up -d
145145
之后访问:
146146

147147
```text
148-
http://部署机IP:8080
148+
http://部署机IP/
149149
```
150150

151-
第一次启动后,先用同一个入口访问 `http://部署机IP:8080/teslamate/` 完成 TeslaMate 授权初始化;之后日常使用 `http://部署机IP:8080/` 查看本项目的报表界面。TeslaMate 没有发布独立宿主机端口,`/teslamate/` 也受同一组 Basic Auth 保护。
151+
第一次启动后,访问 `http://部署机IP/` 完成 TeslaMate 授权初始化;之后日常访问 `http://部署机IP/dashboard/` 查看本项目的报表界面。TeslaMate 没有发布独立宿主机端口,根路径 `/``/dashboard/` 都受同一组 Basic Auth 保护。
152152

153153
只要外层还有反向代理或公网入口,把它转发到 `WEB_PORT` 即可;不需要额外暴露 TeslaMate、PostgreSQL 或 MQTT。
154154

@@ -164,7 +164,7 @@ http://部署机IP:8080
164164

165165
如果目录里有多个备份文件,脚本会按名称优先级取第一个匹配项。这个导入只在数据库卷首次初始化时执行一次;后续镜像更新、容器重启不会再次导入,也不会覆盖已有数据。若要重新导入,需要先删除对应的 PostgreSQL volume。
166166

167-
恢复脚本会在备份导入完成后自动清空 `private.tokens`。如果备份来自旧版 TeslaMate,脚本也会兼容清空 `public.tokens`。这样历史行程、充电、位置等数据会保留,但不会继承备份里的旧 Tesla 授权;启动后访问 `http://部署机IP:8080/teslamate/` 重新完成授权即可。
167+
恢复脚本会在备份导入完成后自动清空 `private.tokens`。如果备份来自旧版 TeslaMate,脚本也会兼容清空 `public.tokens`。这样历史行程、充电、位置等数据会保留,但不会继承备份里的旧 Tesla 授权;启动后访问 `http://部署机IP/` 重新完成授权即可。
168168

169169
完整重建并重新导入备份:
170170

compose.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ services:
5050
DATABASE_HOST: database
5151
MQTT_HOST: mosquitto
5252
CHECK_ORIGIN: "false"
53+
URL_PATH: /
5354
TZ: ${TZ:-Asia/Shanghai}
5455
volumes:
5556
- ./import:/opt/app/import
@@ -119,7 +120,7 @@ services:
119120
BASIC_AUTH_USER: ${BASIC_AUTH_USER:-admin}
120121
BASIC_AUTH_PASSWORD: ${BASIC_AUTH_PASSWORD}
121122
ports:
122-
- "${WEB_PORT:-8080}:80"
123+
- "${WEB_PORT:-80}:80"
123124
networks:
124125
- app
125126
- data

frontend/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ RUN apk add --no-cache openssl
1414

1515
COPY nginx/default.conf /etc/nginx/conf.d/default.conf
1616
COPY nginx/10-basic-auth.sh /docker-entrypoint.d/10-basic-auth.sh
17-
COPY --from=build /app/dist /usr/share/nginx/html
17+
COPY --from=build /app/dist /usr/share/nginx/html/dashboard
1818

1919
RUN chmod +x /docker-entrypoint.d/10-basic-auth.sh
2020

frontend/nginx/default.conf

Lines changed: 17 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
geo $dollar_sign {
2-
default "$";
3-
}
4-
51
map $http_upgrade $connection_upgrade {
62
default upgrade;
73
"" close;
@@ -28,8 +24,9 @@ server {
2824
return 200 "ok\n";
2925
}
3026

31-
location /api/ {
27+
location /dashboard/api/ {
3228
set $api_upstream api:8000;
29+
rewrite ^/dashboard/api(/.*)$ /api$1 break;
3330
proxy_pass http://$api_upstream;
3431
proxy_http_version 1.1;
3532
proxy_set_header Host $host;
@@ -39,8 +36,9 @@ server {
3936
proxy_read_timeout 60s;
4037
}
4138

42-
location /health {
39+
location = /dashboard/health {
4340
set $api_upstream api:8000;
41+
rewrite ^ /health break;
4442
proxy_pass http://$api_upstream;
4543
proxy_http_version 1.1;
4644
proxy_set_header Host $host;
@@ -49,59 +47,31 @@ server {
4947
proxy_set_header X-Forwarded-Proto $scheme;
5048
}
5149

52-
location = /teslamate {
53-
return 301 /teslamate/;
50+
location = /dashboard {
51+
return 301 /dashboard/;
5452
}
5553

56-
location /teslamate/ {
57-
set $teslamate_upstream teslamate:4000;
58-
rewrite ^/teslamate/(.*)$ /$1 break;
59-
proxy_pass http://$teslamate_upstream;
60-
proxy_http_version 1.1;
61-
proxy_set_header Accept-Encoding "";
62-
proxy_set_header Host $host;
63-
proxy_set_header X-Real-IP $remote_addr;
64-
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
65-
proxy_set_header X-Forwarded-Proto $scheme;
66-
proxy_set_header X-Forwarded-Prefix /teslamate;
67-
proxy_set_header Upgrade $http_upgrade;
68-
proxy_set_header Connection $connection_upgrade;
69-
proxy_read_timeout 3600s;
70-
proxy_redirect / /teslamate/;
71-
sub_filter_once off;
72-
sub_filter_types *;
73-
sub_filter 'href="/' 'href="/teslamate/';
74-
sub_filter 'src="/' 'src="/teslamate/';
75-
sub_filter 'action="/' 'action="/teslamate/';
76-
sub_filter '"/live' '"/teslamate/live';
77-
sub_filter '"/js/' '"/teslamate/js/';
78-
sub_filter 'url(/' 'url(/teslamate/';
79-
sub_filter 'baseUrl:window.location.origin' 'baseUrl:window.location.origin+"/teslamate"';
80-
sub_filter 'url:this.redirect?void 0:this.href||void 0' 'url:this.redirect?void 0:(this.href!==null?this.href.replace("/teslamate","/"):this.href)||void 0';
81-
sub_filter 'onRedirect({to:o,flash:r}){this.liveSocket.redirect(o,r)}' 'onRedirect({to:o,flash:r}){this.liveSocket.redirect(o.replace(o.startsWith("/")?/^\//gm:/\/${dollar_sign}/gm, "/teslamate/"),r)}';
54+
location /dashboard/assets/ {
55+
try_files $uri =404;
56+
expires 30d;
57+
add_header Cache-Control "public, max-age=2592000, immutable";
58+
}
59+
60+
location /dashboard/ {
61+
try_files $uri $uri/ /dashboard/index.html;
8262
}
8363

84-
location /live/websocket {
64+
location / {
8565
set $teslamate_upstream teslamate:4000;
8666
proxy_pass http://$teslamate_upstream;
8767
proxy_http_version 1.1;
88-
proxy_set_header Host $host;
68+
proxy_set_header Host $http_host;
8969
proxy_set_header X-Real-IP $remote_addr;
9070
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
9171
proxy_set_header X-Forwarded-Proto $scheme;
92-
proxy_set_header X-Forwarded-Prefix /teslamate;
72+
proxy_set_header X-Forwarded-Host $http_host;
9373
proxy_set_header Upgrade $http_upgrade;
9474
proxy_set_header Connection $connection_upgrade;
9575
proxy_read_timeout 3600s;
9676
}
97-
98-
location /assets/ {
99-
try_files $uri =404;
100-
expires 30d;
101-
add_header Cache-Control "public, max-age=2592000, immutable";
102-
}
103-
104-
location / {
105-
try_files $uri /index.html;
106-
}
10777
}

frontend/src/main.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,8 @@ type Dashboard = {
205205

206206
type ReportKey = 'overview' | 'trends' | 'drives' | 'charging' | 'locations' | 'vehicle';
207207

208-
const API_BASE = import.meta.env.VITE_API_BASE ?? '';
208+
const defaultApiBase = import.meta.env.BASE_URL === '/' ? '' : import.meta.env.BASE_URL.replace(/\/$/, '');
209+
const API_BASE = import.meta.env.VITE_API_BASE ?? defaultApiBase;
209210

210211
const ranges = [
211212
{ label: '30天', value: 30 },

frontend/vite.config.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { defineConfig } from 'vite';
22
import react from '@vitejs/plugin-react';
33

44
export default defineConfig({
5+
base: process.env.VITE_BASE_PATH ?? '/dashboard/',
56
plugins: [react()],
67
build: {
78
chunkSizeWarningLimit: 700,
@@ -17,6 +18,14 @@ export default defineConfig({
1718
server: {
1819
port: 5173,
1920
proxy: {
21+
'/dashboard/api': {
22+
target: 'http://localhost:8000',
23+
rewrite: (path) => path.replace(/^\/dashboard\/api/, '/api')
24+
},
25+
'/dashboard/health': {
26+
target: 'http://localhost:8000',
27+
rewrite: () => '/health'
28+
},
2029
'/api': 'http://localhost:8000',
2130
'/health': 'http://localhost:8000'
2231
}

0 commit comments

Comments
 (0)