Skip to content

Commit 06de8fd

Browse files
airslicewilfredmulengaclaudeZTongci
authored
feat(web,server): support custom provider (#146)
Co-authored-by: Wilfred <wilfred.mulenga@gmail.com> Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Tongci.Z <116621270+ZTongci@users.noreply.github.com>
1 parent 8b858ae commit 06de8fd

48 files changed

Lines changed: 5331 additions & 84 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

public/sentinel-sw.js

Lines changed: 701 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
package migration
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"time"
7+
8+
"go.mongodb.org/mongo-driver/bson"
9+
"go.mongodb.org/mongo-driver/mongo"
10+
"go.mongodb.org/mongo-driver/mongo/options"
11+
)
12+
13+
// RevertTileAndTerrainProviders reverts the changes made by UpdateTileAndTerrainProviders
14+
// This is a recovery migration that should NOT be registered in migrations.go
15+
// Use this only when you need to rollback the UpdateTileAndTerrainProviders migration
16+
//
17+
// Tile reversion rules:
18+
// - "cesium_ion" with asset_id 2 → "default" (and remove asset_id field)
19+
// - "cesium_ion" with asset_id 3 → "default_label" (and remove asset_id field)
20+
// - "cesium_ion" with asset_id 4 → "default_road" (and remove asset_id field)
21+
// - "cesium_ion" with asset_id 3812 → "black_marble" (and remove asset_id field)
22+
// - "open_street_map" → "esri_world_topo"
23+
// Terrain reversion rules:
24+
// - "reearth_terrain" → "arcgis"
25+
func RevertTileAndTerrainProviders(ctx context.Context, c DBClient) error {
26+
col := c.WithCollection("property").Client()
27+
28+
// Tile reversions
29+
// Reversion 1: cesium_ion (asset_id: 2) → default
30+
if err := revertCesiumIonToOldType(ctx, col, 2, "default"); err != nil {
31+
return fmt.Errorf("failed to revert cesium_ion (asset_id: 2) to 'default': %w", err)
32+
}
33+
34+
// Reversion 2: cesium_ion (asset_id: 3) → default_label
35+
if err := revertCesiumIonToOldType(ctx, col, 3, "default_label"); err != nil {
36+
return fmt.Errorf("failed to revert cesium_ion (asset_id: 3) to 'default_label': %w", err)
37+
}
38+
39+
// Reversion 3: cesium_ion (asset_id: 4) → default_road
40+
if err := revertCesiumIonToOldType(ctx, col, 4, "default_road"); err != nil {
41+
return fmt.Errorf("failed to revert cesium_ion (asset_id: 4) to 'default_road': %w", err)
42+
}
43+
44+
// Reversion 4: cesium_ion (asset_id: 3812) → black_marble
45+
if err := revertCesiumIonToOldType(ctx, col, 3812, "black_marble"); err != nil {
46+
return fmt.Errorf("failed to revert cesium_ion (asset_id: 3812) to 'black_marble': %w", err)
47+
}
48+
49+
// Reversion 5: open_street_map → esri_world_topo (simple rename)
50+
if err := revertTileSimpleRename(ctx, col, "open_street_map", "esri_world_topo"); err != nil {
51+
return fmt.Errorf("failed to revert tile 'open_street_map': %w", err)
52+
}
53+
54+
// Terrain reversions
55+
// Reversion 7: reearth_terrain → arcgis (simple rename)
56+
if err := revertTerrainSimpleRename(ctx, col, "reearth_terrain", "arcgis"); err != nil {
57+
return fmt.Errorf("failed to revert terrain 'reearth_terrain': %w", err)
58+
}
59+
60+
fmt.Println("[migration] RevertTileAndTerrainProviders completed successfully")
61+
return nil
62+
}
63+
64+
// revertCesiumIonToOldType changes cesium_ion with specific asset_id back to old tile type and removes asset_id field
65+
func revertCesiumIonToOldType(ctx context.Context, col *mongo.Collection, assetID int, oldType string) error {
66+
// Find documents with cesium_ion and the specific asset_id
67+
filter := bson.M{
68+
"items.groups.fields": bson.M{
69+
"$elemMatch": bson.M{
70+
"field": "tile_type",
71+
"value": "cesium_ion",
72+
},
73+
},
74+
"items.groups.fields.field": "cesium_ion_asset_id",
75+
"items.groups.fields.value": float64(assetID),
76+
}
77+
78+
countCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
79+
defer cancel()
80+
n, err := col.CountDocuments(countCtx, filter)
81+
if err != nil {
82+
return fmt.Errorf("count failed for cesium_ion (asset_id: %d): %w", assetID, err)
83+
}
84+
fmt.Printf("[migration] target documents for cesium_ion (asset_id: %d): %d\n", assetID, n)
85+
if n == 0 {
86+
fmt.Printf("[migration] nothing to do for cesium_ion (asset_id: %d)\n", assetID)
87+
return nil
88+
}
89+
90+
updateCtx, cancel2 := context.WithTimeout(ctx, 30*time.Minute)
91+
defer cancel2()
92+
93+
// Step 1: Update tile_type value from cesium_ion to old type
94+
update := bson.M{
95+
"$set": bson.M{
96+
"items.$[i].groups.$[g].fields.$[f].value": oldType,
97+
},
98+
}
99+
arrayFilters := options.ArrayFilters{
100+
Filters: []interface{}{
101+
bson.M{"i.groups": bson.M{"$type": "array"}},
102+
bson.M{
103+
"g.fields": bson.M{
104+
"$elemMatch": bson.M{
105+
"field": "cesium_ion_asset_id",
106+
"value": float64(assetID),
107+
},
108+
},
109+
},
110+
bson.M{
111+
"f.field": "tile_type",
112+
"f.value": "cesium_ion",
113+
},
114+
},
115+
}
116+
opts := options.Update().SetArrayFilters(arrayFilters)
117+
118+
res, err := col.UpdateMany(updateCtx, filter, update, opts)
119+
if err != nil {
120+
return fmt.Errorf("update tile_type failed for cesium_ion (asset_id: %d): %w", assetID, err)
121+
}
122+
fmt.Printf("[migration] cesium_ion (asset_id: %d) → '%s': matched: %d, modified: %d\n", assetID, oldType, res.MatchedCount, res.ModifiedCount)
123+
124+
// Step 2: Remove cesium_ion_asset_id field
125+
filterForRemoval := bson.M{
126+
"$and": bson.A{
127+
bson.M{
128+
"items.groups.fields": bson.M{
129+
"$elemMatch": bson.M{
130+
"field": "tile_type",
131+
"value": oldType,
132+
},
133+
},
134+
},
135+
bson.M{
136+
"items.groups.fields": bson.M{
137+
"$elemMatch": bson.M{
138+
"field": "cesium_ion_asset_id",
139+
"value": float64(assetID),
140+
},
141+
},
142+
},
143+
},
144+
}
145+
146+
updateRemoval := bson.M{
147+
"$pull": bson.M{
148+
"items.$[i].groups.$[g].fields": bson.M{
149+
"field": "cesium_ion_asset_id",
150+
"value": float64(assetID),
151+
},
152+
},
153+
}
154+
arrayFiltersRemoval := options.ArrayFilters{
155+
Filters: []interface{}{
156+
bson.M{"i.groups": bson.M{"$type": "array"}},
157+
bson.M{
158+
"g.fields": bson.M{
159+
"$elemMatch": bson.M{
160+
"field": "cesium_ion_asset_id",
161+
"value": float64(assetID),
162+
},
163+
},
164+
},
165+
},
166+
}
167+
optsRemoval := options.Update().SetArrayFilters(arrayFiltersRemoval)
168+
169+
res2, err := col.UpdateMany(updateCtx, filterForRemoval, updateRemoval, optsRemoval)
170+
if err != nil {
171+
return fmt.Errorf("remove cesium_ion_asset_id failed for asset_id %d: %w", assetID, err)
172+
}
173+
fmt.Printf("[migration] removed cesium_ion_asset_id=%d: matched: %d, modified: %d\n", assetID, res2.MatchedCount, res2.ModifiedCount)
174+
175+
return nil
176+
}
177+
178+
// revertTileSimpleRename changes one tile_type value back to another
179+
func revertTileSimpleRename(ctx context.Context, col *mongo.Collection, currentValue, oldValue string) error {
180+
filter := bson.M{
181+
"items.groups.fields.field": "tile_type",
182+
"items.groups.fields.value": currentValue,
183+
}
184+
185+
countCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
186+
defer cancel()
187+
n, err := col.CountDocuments(countCtx, filter)
188+
if err != nil {
189+
return fmt.Errorf("count failed for tile '%s': %w", currentValue, err)
190+
}
191+
fmt.Printf("[migration] target documents for tile '%s': %d\n", currentValue, n)
192+
if n == 0 {
193+
fmt.Printf("[migration] nothing to do for tile '%s'\n", currentValue)
194+
return nil
195+
}
196+
197+
update := bson.M{
198+
"$set": bson.M{
199+
"items.$[i].groups.$[g].fields.$[f].value": oldValue,
200+
},
201+
}
202+
arrayFilters := options.ArrayFilters{
203+
Filters: []interface{}{
204+
bson.M{"i.groups": bson.M{"$type": "array"}},
205+
bson.M{"g.fields": bson.M{"$type": "array"}},
206+
bson.M{
207+
"f.field": "tile_type",
208+
"f.value": currentValue,
209+
},
210+
},
211+
}
212+
opts := options.Update().SetArrayFilters(arrayFilters)
213+
214+
updateCtx, cancel2 := context.WithTimeout(ctx, 30*time.Minute)
215+
defer cancel2()
216+
217+
res, err := col.UpdateMany(updateCtx, filter, update, opts)
218+
if err != nil {
219+
return fmt.Errorf("update failed for tile '%s': %w", currentValue, err)
220+
}
221+
222+
fmt.Printf("[migration] tile '%s' → '%s': matched: %d, modified: %d\n", currentValue, oldValue, res.MatchedCount, res.ModifiedCount)
223+
return nil
224+
}
225+
226+
// revertTerrainSimpleRename changes one terrainType value back to another
227+
func revertTerrainSimpleRename(ctx context.Context, col *mongo.Collection, currentValue, oldValue string) error {
228+
filter := bson.M{
229+
"items.groups.fields.field": "terrainType",
230+
"items.groups.fields.value": currentValue,
231+
}
232+
233+
countCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
234+
defer cancel()
235+
n, err := col.CountDocuments(countCtx, filter)
236+
if err != nil {
237+
return fmt.Errorf("count failed for terrainType '%s': %w", currentValue, err)
238+
}
239+
fmt.Printf("[migration] target documents for terrainType '%s': %d\n", currentValue, n)
240+
if n == 0 {
241+
fmt.Printf("[migration] nothing to do for terrainType '%s'\n", currentValue)
242+
return nil
243+
}
244+
245+
update := bson.M{
246+
"$set": bson.M{
247+
"items.$[i].groups.$[g].fields.$[f].value": oldValue,
248+
},
249+
}
250+
arrayFilters := options.ArrayFilters{
251+
Filters: []interface{}{
252+
bson.M{"i.groups": bson.M{"$type": "array"}},
253+
bson.M{"g.fields": bson.M{"$type": "array"}},
254+
bson.M{
255+
"f.field": "terrainType",
256+
"f.value": currentValue,
257+
},
258+
},
259+
}
260+
opts := options.Update().SetArrayFilters(arrayFilters)
261+
262+
updateCtx, cancel2 := context.WithTimeout(ctx, 30*time.Minute)
263+
defer cancel2()
264+
265+
res, err := col.UpdateMany(updateCtx, filter, update, opts)
266+
if err != nil {
267+
return fmt.Errorf("update failed for terrainType '%s': %w", currentValue, err)
268+
}
269+
270+
fmt.Printf("[migration] terrainType '%s' → '%s': matched: %d, modified: %d\n", currentValue, oldValue, res.MatchedCount, res.ModifiedCount)
271+
return nil
272+
}

0 commit comments

Comments
 (0)