代理与否可控

This commit is contained in:
2026-06-06 12:20:16 +08:00
parent 563d609121
commit 2faac32d87
7 changed files with 109 additions and 40 deletions
+23 -17
View File
@@ -10,12 +10,13 @@ import (
) )
type mapTileSourceRequest struct { type mapTileSourceRequest struct {
Name string `json:"name"` Name string `json:"name"`
URLTemplate string `json:"url_template"` URLTemplate string `json:"url_template"`
Attribution string `json:"attribution"` Attribution string `json:"attribution"`
MaxZoom int `json:"max_zoom"` MaxZoom int `json:"max_zoom"`
Enabled bool `json:"enabled"` Enabled bool `json:"enabled"`
IsDefault bool `json:"is_default"` IsDefault bool `json:"is_default"`
ProxyEnabled bool `json:"proxy_enabled"`
} }
func registerMapSourceRoutes(r gin.IRouter, store *store) { func registerMapSourceRoutes(r gin.IRouter, store *store) {
@@ -96,12 +97,13 @@ func registerAdminMapSourceRoutes(r gin.IRouter, store *store) {
func mapTileSourceInputFromRequest(req mapTileSourceRequest) mapTileSourceInput { func mapTileSourceInputFromRequest(req mapTileSourceRequest) mapTileSourceInput {
return mapTileSourceInput{ return mapTileSourceInput{
Name: req.Name, Name: req.Name,
URLTemplate: req.URLTemplate, URLTemplate: req.URLTemplate,
Attribution: req.Attribution, Attribution: req.Attribution,
MaxZoom: req.MaxZoom, MaxZoom: req.MaxZoom,
Enabled: req.Enabled, Enabled: req.Enabled,
IsDefault: req.IsDefault, IsDefault: req.IsDefault,
ProxyEnabled: req.ProxyEnabled,
} }
} }
@@ -151,13 +153,17 @@ func writeMapTileSourceDeleteResponse(c *gin.Context, err error) {
} }
func mapTileSourceDTO(row mapTileSourceRecord) gin.H { func mapTileSourceDTO(row mapTileSourceRecord) gin.H {
return gin.H{"id": row.ID, "name": row.Name, "url_template": row.URLTemplate, "attribution": row.Attribution, "max_zoom": row.MaxZoom, "enabled": row.Enabled, "is_default": row.IsDefault, "created_at": row.CreatedAt, "updated_at": row.UpdatedAt} return gin.H{"id": row.ID, "name": row.Name, "url_template": row.URLTemplate, "attribution": row.Attribution, "max_zoom": row.MaxZoom, "enabled": row.Enabled, "is_default": row.IsDefault, "proxy_enabled": row.ProxyEnabled, "created_at": row.CreatedAt, "updated_at": row.UpdatedAt}
} }
func publicMapTileSourceDTO(row mapTileSourceRecord) gin.H { func publicMapTileSourceDTO(row mapTileSourceRecord) gin.H {
hash := row.URLTemplateHash urlTemplate := row.URLTemplate
if hash == "" { if row.ProxyEnabled {
hash = mapTileSourceHash(row.URLTemplate) hash := row.URLTemplateHash
if hash == "" {
hash = mapTileSourceHash(row.URLTemplate)
}
urlTemplate = "/api/map/" + hash + "?x={x}&y={y}&z={z}"
} }
return gin.H{"id": row.ID, "name": row.Name, "url_template": "/api/map/" + hash + "?x={x}&y={y}&z={z}", "attribution": row.Attribution, "max_zoom": row.MaxZoom} return gin.H{"id": row.ID, "name": row.Name, "url_template": urlTemplate, "attribution": row.Attribution, "max_zoom": row.MaxZoom}
} }
+10
View File
@@ -124,6 +124,7 @@ type mapTileSourceRecord struct {
MaxZoom int `gorm:"column:max_zoom;not null"` MaxZoom int `gorm:"column:max_zoom;not null"`
Enabled bool `gorm:"column:enabled;not null;index"` Enabled bool `gorm:"column:enabled;not null;index"`
IsDefault bool `gorm:"column:is_default;not null;index"` IsDefault bool `gorm:"column:is_default;not null;index"`
ProxyEnabled bool `gorm:"column:proxy_enabled;not null;index"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime"` CreatedAt time.Time `gorm:"column:created_at;autoCreateTime"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;index"` UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;index"`
} }
@@ -472,6 +473,15 @@ func (s *store) migrate() error {
} }
func migrateMapTileSourceHash(tx *gorm.DB, migrator gorm.Migrator, driver string) error { func migrateMapTileSourceHash(tx *gorm.DB, migrator gorm.Migrator, driver string) error {
if !migrator.HasColumn(&mapTileSourceRecord{}, "ProxyEnabled") {
if driver == databaseDriverSQLite {
if err := tx.Exec("ALTER TABLE map_tile_sources ADD COLUMN proxy_enabled numeric NOT NULL DEFAULT true").Error; err != nil {
return fmt.Errorf("migrate map_tile_sources proxy_enabled column: %w", err)
}
} else if err := migrator.AddColumn(&mapTileSourceRecord{}, "ProxyEnabled"); err != nil {
return fmt.Errorf("migrate map_tile_sources proxy_enabled column: %w", err)
}
}
if !migrator.HasColumn(&mapTileSourceRecord{}, "URLTemplateHash") { if !migrator.HasColumn(&mapTileSourceRecord{}, "URLTemplateHash") {
if driver == databaseDriverSQLite { if driver == databaseDriverSQLite {
if err := tx.Exec("ALTER TABLE map_tile_sources ADD COLUMN url_template_hash TEXT NOT NULL DEFAULT ''").Error; err != nil { if err := tx.Exec("ALTER TABLE map_tile_sources ADD COLUMN url_template_hash TEXT NOT NULL DEFAULT ''").Error; err != nil {
+11 -7
View File
@@ -29,12 +29,13 @@ var (
) )
type mapTileSourceInput struct { type mapTileSourceInput struct {
Name string Name string
URLTemplate string URLTemplate string
Attribution string Attribution string
MaxZoom int MaxZoom int
Enabled bool Enabled bool
IsDefault bool IsDefault bool
ProxyEnabled bool
} }
func (s *store) ListMapTileSources(opts listOptions) ([]mapTileSourceRecord, error) { func (s *store) ListMapTileSources(opts listOptions) ([]mapTileSourceRecord, error) {
@@ -85,7 +86,7 @@ func (s *store) GetDefaultMapTileSource() (*mapTileSourceRecord, error) {
func (s *store) GetEnabledMapTileSourceByHash(hash string) (*mapTileSourceRecord, error) { func (s *store) GetEnabledMapTileSourceByHash(hash string) (*mapTileSourceRecord, error) {
var row mapTileSourceRecord var row mapTileSourceRecord
if err := s.db.Where("enabled = ? AND url_template_hash = ?", true, hash).Take(&row).Error; err != nil { if err := s.db.Where("enabled = ? AND proxy_enabled = ? AND url_template_hash = ?", true, true, hash).Take(&row).Error; err != nil {
return nil, err return nil, err
} }
return &row, nil return &row, nil
@@ -154,6 +155,7 @@ func (s *store) UpdateMapTileSource(id uint64, input mapTileSourceInput) (*mapTi
"max_zoom": row.MaxZoom, "max_zoom": row.MaxZoom,
"enabled": row.Enabled, "enabled": row.Enabled,
"is_default": row.IsDefault, "is_default": row.IsDefault,
"proxy_enabled": row.ProxyEnabled,
"updated_at": time.Now(), "updated_at": time.Now(),
} }
if err := tx.Model(&mapTileSourceRecord{}).Where("id = ?", id).Updates(updates).Error; err != nil { if err := tx.Model(&mapTileSourceRecord{}).Where("id = ?", id).Updates(updates).Error; err != nil {
@@ -269,6 +271,7 @@ func defaultMapTileSourceRecord() mapTileSourceRecord {
MaxZoom: defaultMapTileSourceMaxZoom, MaxZoom: defaultMapTileSourceMaxZoom,
Enabled: true, Enabled: true,
IsDefault: true, IsDefault: true,
ProxyEnabled: true,
} }
} }
@@ -296,6 +299,7 @@ func mapTileSourceFromInput(input mapTileSourceInput) (*mapTileSourceRecord, err
MaxZoom: maxZoom, MaxZoom: maxZoom,
Enabled: input.Enabled, Enabled: input.Enabled,
IsDefault: input.IsDefault, IsDefault: input.IsDefault,
ProxyEnabled: input.ProxyEnabled,
}, nil }, nil
} }
+47 -10
View File
@@ -25,13 +25,13 @@ func TestCreateMapTileSourceValidation(t *testing.T) {
st := openTestStore(t) st := openTestStore(t)
defer st.Close() defer st.Close()
if _, err := st.CreateMapTileSource(mapTileSourceInput{Name: "bad", URLTemplate: "https://tiles.example.com/{z}/{x}.png", MaxZoom: 19, Enabled: true}); err == nil { if _, err := st.CreateMapTileSource(mapTileSourceInput{Name: "bad", URLTemplate: "https://tiles.example.com/{z}/{x}.png", MaxZoom: 19, Enabled: true, ProxyEnabled: true}); err == nil {
t.Fatal("CreateMapTileSource() missing placeholder error = nil, want error") t.Fatal("CreateMapTileSource() missing placeholder error = nil, want error")
} }
if _, err := st.CreateMapTileSource(mapTileSourceInput{Name: "bad", URLTemplate: "javascript:alert(1)/{z}/{x}/{y}", MaxZoom: 19, Enabled: true}); err == nil { if _, err := st.CreateMapTileSource(mapTileSourceInput{Name: "bad", URLTemplate: "javascript:alert(1)/{z}/{x}/{y}", MaxZoom: 19, Enabled: true, ProxyEnabled: true}); err == nil {
t.Fatal("CreateMapTileSource() invalid scheme error = nil, want error") t.Fatal("CreateMapTileSource() invalid scheme error = nil, want error")
} }
if _, err := st.CreateMapTileSource(mapTileSourceInput{Name: "bad", URLTemplate: "https://user:pass@tiles.example.com/{z}/{x}/{y}.png", MaxZoom: 19, Enabled: true}); err == nil { if _, err := st.CreateMapTileSource(mapTileSourceInput{Name: "bad", URLTemplate: "https://user:pass@tiles.example.com/{z}/{x}/{y}.png", MaxZoom: 19, Enabled: true, ProxyEnabled: true}); err == nil {
t.Fatal("CreateMapTileSource() credentials error = nil, want error") t.Fatal("CreateMapTileSource() credentials error = nil, want error")
} }
} }
@@ -44,7 +44,7 @@ func TestListEnabledMapTileSources(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("CreateMapTileSource(disabled) error = %v", err) t.Fatalf("CreateMapTileSource(disabled) error = %v", err)
} }
custom, err := st.CreateMapTileSource(mapTileSourceInput{Name: "Custom", URLTemplate: "https://custom.example.com/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true}) custom, err := st.CreateMapTileSource(mapTileSourceInput{Name: "Custom", URLTemplate: "https://custom.example.com/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true, ProxyEnabled: true})
if err != nil { if err != nil {
t.Fatalf("CreateMapTileSource(custom) error = %v", err) t.Fatalf("CreateMapTileSource(custom) error = %v", err)
} }
@@ -76,14 +76,14 @@ func TestMapTileSourceDuplicateAndDefaultRules(t *testing.T) {
st := openTestStore(t) st := openTestStore(t)
defer st.Close() defer st.Close()
first, err := st.CreateMapTileSource(mapTileSourceInput{Name: "Custom", URLTemplate: "https://tiles.example.com/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true}) first, err := st.CreateMapTileSource(mapTileSourceInput{Name: "Custom", URLTemplate: "https://tiles.example.com/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true, ProxyEnabled: true})
if err != nil { if err != nil {
t.Fatalf("CreateMapTileSource() error = %v", err) t.Fatalf("CreateMapTileSource() error = %v", err)
} }
if _, err := st.CreateMapTileSource(mapTileSourceInput{Name: "Custom", URLTemplate: "https://tiles2.example.com/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true}); !errors.Is(err, errMapTileSourceAlreadyExists) { if _, err := st.CreateMapTileSource(mapTileSourceInput{Name: "Custom", URLTemplate: "https://tiles2.example.com/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true, ProxyEnabled: true}); !errors.Is(err, errMapTileSourceAlreadyExists) {
t.Fatalf("duplicate name error = %v, want errMapTileSourceAlreadyExists", err) t.Fatalf("duplicate name error = %v, want errMapTileSourceAlreadyExists", err)
} }
if _, err := st.CreateMapTileSource(mapTileSourceInput{Name: "Custom 2", URLTemplate: first.URLTemplate, MaxZoom: 18, Enabled: true}); !errors.Is(err, errMapTileSourceAlreadyExists) { if _, err := st.CreateMapTileSource(mapTileSourceInput{Name: "Custom 2", URLTemplate: first.URLTemplate, MaxZoom: 18, Enabled: true, ProxyEnabled: true}); !errors.Is(err, errMapTileSourceAlreadyExists) {
t.Fatalf("duplicate url error = %v, want errMapTileSourceAlreadyExists", err) t.Fatalf("duplicate url error = %v, want errMapTileSourceAlreadyExists", err)
} }
@@ -114,7 +114,7 @@ func TestMapTileSourceHashIsSetOnCreate(t *testing.T) {
st := openTestStore(t) st := openTestStore(t)
defer st.Close() defer st.Close()
row, err := st.CreateMapTileSource(mapTileSourceInput{Name: "Hashed", URLTemplate: "https://test.example.com/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true}) row, err := st.CreateMapTileSource(mapTileSourceInput{Name: "Hashed", URLTemplate: "https://test.example.com/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true, ProxyEnabled: true})
if err != nil { if err != nil {
t.Fatalf("CreateMapTileSource() error = %v", err) t.Fatalf("CreateMapTileSource() error = %v", err)
} }
@@ -122,6 +122,9 @@ func TestMapTileSourceHashIsSetOnCreate(t *testing.T) {
if row.URLTemplateHash != want { if row.URLTemplateHash != want {
t.Fatalf("URLTemplateHash = %q, want %q", row.URLTemplateHash, want) t.Fatalf("URLTemplateHash = %q, want %q", row.URLTemplateHash, want)
} }
if !row.ProxyEnabled {
t.Fatal("ProxyEnabled = false, want true")
}
} }
func TestMapTileSourceDefaultHasHash(t *testing.T) { func TestMapTileSourceDefaultHasHash(t *testing.T) {
@@ -142,7 +145,7 @@ func TestGetEnabledMapTileSourceByHash(t *testing.T) {
st := openTestStore(t) st := openTestStore(t)
defer st.Close() defer st.Close()
row, err := st.CreateMapTileSource(mapTileSourceInput{Name: "HashLookup", URLTemplate: "https://lookup.example.com/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true}) row, err := st.CreateMapTileSource(mapTileSourceInput{Name: "HashLookup", URLTemplate: "https://lookup.example.com/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true, ProxyEnabled: true})
if err != nil { if err != nil {
t.Fatalf("CreateMapTileSource() error = %v", err) t.Fatalf("CreateMapTileSource() error = %v", err)
} }
@@ -171,6 +174,21 @@ func TestGetEnabledMapTileSourceByHashDisabled(t *testing.T) {
} }
} }
func TestGetEnabledMapTileSourceByHashProxyDisabled(t *testing.T) {
st := openTestStore(t)
defer st.Close()
row, err := st.CreateMapTileSource(mapTileSourceInput{Name: "ProxyDisabledHash", URLTemplate: "https://proxy-disabled.example.com/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true, ProxyEnabled: false})
if err != nil {
t.Fatalf("CreateMapTileSource() error = %v", err)
}
_, err = st.GetEnabledMapTileSourceByHash(row.URLTemplateHash)
if !errors.Is(err, gorm.ErrRecordNotFound) {
t.Fatalf("GetEnabledMapTileSourceByHash(proxy disabled) = %v, want gorm.ErrRecordNotFound", err)
}
}
func TestGetEnabledMapTileSourceByHashUnknown(t *testing.T) { func TestGetEnabledMapTileSourceByHashUnknown(t *testing.T) {
st := openTestStore(t) st := openTestStore(t)
defer st.Close() defer st.Close()
@@ -185,7 +203,7 @@ func TestPublicMapTileSourceDTOProxyURL(t *testing.T) {
st := openTestStore(t) st := openTestStore(t)
defer st.Close() defer st.Close()
row, err := st.CreateMapTileSource(mapTileSourceInput{Name: "ProxyTest", URLTemplate: "https://proxy.example.com/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true}) row, err := st.CreateMapTileSource(mapTileSourceInput{Name: "ProxyTest", URLTemplate: "https://proxy.example.com/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true, ProxyEnabled: true})
if err != nil { if err != nil {
t.Fatalf("CreateMapTileSource() error = %v", err) t.Fatalf("CreateMapTileSource() error = %v", err)
} }
@@ -204,6 +222,25 @@ func TestPublicMapTileSourceDTOProxyURL(t *testing.T) {
} }
} }
func TestPublicMapTileSourceDTORawURLWhenProxyDisabled(t *testing.T) {
st := openTestStore(t)
defer st.Close()
row, err := st.CreateMapTileSource(mapTileSourceInput{Name: "RawTest", URLTemplate: "https://raw.example.com/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true, ProxyEnabled: false})
if err != nil {
t.Fatalf("CreateMapTileSource() error = %v", err)
}
dto := publicMapTileSourceDTO(*row)
urlTemplate, ok := dto["url_template"].(string)
if !ok {
t.Fatal("url_template is not a string")
}
if urlTemplate != row.URLTemplate {
t.Fatalf("url_template = %q, want raw %q", urlTemplate, row.URLTemplate)
}
}
func TestMapTileSourceHashFunction(t *testing.T) { func TestMapTileSourceHashFunction(t *testing.T) {
hash1 := mapTileSourceHash("https://tile.openstreetmap.jp/{z}/{x}/{y}.png") hash1 := mapTileSourceHash("https://tile.openstreetmap.jp/{z}/{x}/{y}.png")
hash2 := mapTileSourceHash("https://tile.openstreetmap.jp/{z}/{x}/{y}.png") hash2 := mapTileSourceHash("https://tile.openstreetmap.jp/{z}/{x}/{y}.png")
+11 -6
View File
@@ -24,7 +24,7 @@ func TestMapTileProxyFetchesAndCaches(t *testing.T) {
})) }))
defer upstream.Close() defer upstream.Close()
row, err := st.CreateMapTileSource(mapTileSourceInput{Name: "Tiles", URLTemplate: upstream.URL + "/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true}) row, err := st.CreateMapTileSource(mapTileSourceInput{Name: "Tiles", URLTemplate: upstream.URL + "/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true, ProxyEnabled: true})
if err != nil { if err != nil {
t.Fatalf("CreateMapTileSource() error = %v", err) t.Fatalf("CreateMapTileSource() error = %v", err)
} }
@@ -62,7 +62,7 @@ func TestMapTileProxyRejectsInvalidCoordinates(t *testing.T) {
st := openTestStore(t) st := openTestStore(t)
defer st.Close() defer st.Close()
row, err := st.CreateMapTileSource(mapTileSourceInput{Name: "Tiles", URLTemplate: "https://tiles.example.com/{z}/{x}/{y}.png", MaxZoom: 3, Enabled: true}) row, err := st.CreateMapTileSource(mapTileSourceInput{Name: "Tiles", URLTemplate: "https://tiles.example.com/{z}/{x}/{y}.png", MaxZoom: 3, Enabled: true, ProxyEnabled: true})
if err != nil { if err != nil {
t.Fatalf("CreateMapTileSource() error = %v", err) t.Fatalf("CreateMapTileSource() error = %v", err)
} }
@@ -91,7 +91,11 @@ func TestMapTileProxyUnknownAndDisabledSource(t *testing.T) {
disabled, err := st.CreateMapTileSource(mapTileSourceInput{Name: "Disabled", URLTemplate: "https://disabled.example.com/{z}/{x}/{y}.png", MaxZoom: 3, Enabled: false}) disabled, err := st.CreateMapTileSource(mapTileSourceInput{Name: "Disabled", URLTemplate: "https://disabled.example.com/{z}/{x}/{y}.png", MaxZoom: 3, Enabled: false})
if err != nil { if err != nil {
t.Fatalf("CreateMapTileSource() error = %v", err) t.Fatalf("CreateMapTileSource(disabled) error = %v", err)
}
proxyDisabled, err := st.CreateMapTileSource(mapTileSourceInput{Name: "ProxyDisabled", URLTemplate: "https://proxy-disabled.example.com/{z}/{x}/{y}.png", MaxZoom: 3, Enabled: true, ProxyEnabled: false})
if err != nil {
t.Fatalf("CreateMapTileSource(proxy disabled) error = %v", err)
} }
router := newRouter(webConfig{StaticDir: t.TempDir(), MapTileCacheDir: t.TempDir()}, st, nil, nil, nil, nil, nil) router := newRouter(webConfig{StaticDir: t.TempDir(), MapTileCacheDir: t.TempDir()}, st, nil, nil, nil, nil, nil)
@@ -100,8 +104,9 @@ func TestMapTileProxyUnknownAndDisabledSource(t *testing.T) {
"/api/map/not-a-hash?x=0&y=0&z=0", "/api/map/not-a-hash?x=0&y=0&z=0",
"/api/map/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa?x=0&y=0&z=0", "/api/map/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa?x=0&y=0&z=0",
"/api/map/" + disabled.URLTemplateHash + "?x=0&y=0&z=0", "/api/map/" + disabled.URLTemplateHash + "?x=0&y=0&z=0",
"/api/map/" + proxyDisabled.URLTemplateHash + "?x=0&y=0&z=0",
} }
wantStatus := []int{http.StatusBadRequest, http.StatusNotFound, http.StatusNotFound} wantStatus := []int{http.StatusBadRequest, http.StatusNotFound, http.StatusNotFound, http.StatusNotFound}
for i, url := range cases { for i, url := range cases {
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, url, nil) req := httptest.NewRequest(http.MethodGet, url, nil)
@@ -125,11 +130,11 @@ func TestMapTileProxyUpstreamStatus(t *testing.T) {
})) }))
defer upstream.Close() defer upstream.Close()
row404, err := st.CreateMapTileSource(mapTileSourceInput{Name: "NotFoundTiles", URLTemplate: upstream.URL + "/404/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true}) row404, err := st.CreateMapTileSource(mapTileSourceInput{Name: "NotFoundTiles", URLTemplate: upstream.URL + "/404/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true, ProxyEnabled: true})
if err != nil { if err != nil {
t.Fatalf("CreateMapTileSource(404) error = %v", err) t.Fatalf("CreateMapTileSource(404) error = %v", err)
} }
row500, err := st.CreateMapTileSource(mapTileSourceInput{Name: "StatusTiles", URLTemplate: upstream.URL + "/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true}) row500, err := st.CreateMapTileSource(mapTileSourceInput{Name: "StatusTiles", URLTemplate: upstream.URL + "/{z}/{x}/{y}.png", MaxZoom: 18, Enabled: true, ProxyEnabled: true})
if err != nil { if err != nil {
t.Fatalf("CreateMapTileSource(500) error = %v", err) t.Fatalf("CreateMapTileSource(500) error = %v", err)
} }
@@ -17,6 +17,7 @@ const newSource = ref<MapTileSourcePayload>({
max_zoom: 19, max_zoom: 19,
enabled: true, enabled: true,
is_default: false, is_default: false,
proxy_enabled: true,
}) })
const canPrev = () => page.value > 1 const canPrev = () => page.value > 1
@@ -32,6 +33,7 @@ function editableCopy(item: MapTileSource): MapTileSourcePayload {
max_zoom: item.max_zoom, max_zoom: item.max_zoom,
enabled: item.enabled, enabled: item.enabled,
is_default: item.is_default, is_default: item.is_default,
proxy_enabled: item.proxy_enabled,
} }
} }
@@ -45,6 +47,7 @@ function resetNewSource() {
max_zoom: 19, max_zoom: 19,
enabled: true, enabled: true,
is_default: false, is_default: false,
proxy_enabled: true,
} }
} }
@@ -214,6 +217,7 @@ onMounted(refreshItems)
<label class="field zoom-field">最大缩放<input v-model.number="newSource.max_zoom" type="number" min="1" max="30" /></label> <label class="field zoom-field">最大缩放<input v-model.number="newSource.max_zoom" type="number" min="1" max="30" /></label>
<label class="switch-card"><input v-model="newSource.enabled" type="checkbox" /> <span>启用</span></label> <label class="switch-card"><input v-model="newSource.enabled" type="checkbox" /> <span>启用</span></label>
<label class="switch-card"><input v-model="newSource.is_default" type="checkbox" /> <span>设为默认</span></label> <label class="switch-card"><input v-model="newSource.is_default" type="checkbox" /> <span>设为默认</span></label>
<label class="switch-card"><input v-model="newSource.proxy_enabled" type="checkbox" /> <span>是否代理</span></label>
<div class="form-actions"> <div class="form-actions">
<button class="admin-button" type="submit" :disabled="loading">添加图源</button> <button class="admin-button" type="submit" :disabled="loading">添加图源</button>
</div> </div>
@@ -254,6 +258,7 @@ onMounted(refreshItems)
<label class="field attribution-field">Attribution<input v-model="drafts[item.id].attribution" /></label> <label class="field attribution-field">Attribution<input v-model="drafts[item.id].attribution" /></label>
<label class="field zoom-field">最大缩放<input v-model.number="drafts[item.id].max_zoom" type="number" min="1" max="30" /></label> <label class="field zoom-field">最大缩放<input v-model.number="drafts[item.id].max_zoom" type="number" min="1" max="30" /></label>
<label class="switch-card"><input v-model="drafts[item.id].enabled" type="checkbox" :disabled="item.is_default" /> <span>启用图源</span></label> <label class="switch-card"><input v-model="drafts[item.id].enabled" type="checkbox" :disabled="item.is_default" /> <span>启用图源</span></label>
<label class="switch-card"><input v-model="drafts[item.id].proxy_enabled" type="checkbox" /> <span>是否代理</span></label>
</div> </div>
<div class="source-meta"> <div class="source-meta">
+2
View File
@@ -87,6 +87,7 @@ export interface PublicMapTileSource {
export interface MapTileSource extends PublicMapTileSource { export interface MapTileSource extends PublicMapTileSource {
enabled: boolean enabled: boolean
is_default: boolean is_default: boolean
proxy_enabled: boolean
created_at: string created_at: string
updated_at: string updated_at: string
} }
@@ -98,6 +99,7 @@ export interface MapTileSourcePayload {
max_zoom: number max_zoom: number
enabled: boolean enabled: boolean
is_default: boolean is_default: boolean
proxy_enabled: boolean
} }
export interface MapTileSourceResponse { export interface MapTileSourceResponse {