Compare commits

...

706 Commits

Author SHA1 Message Date
9490a06303 Merge branch 'automated/npins-update-20260123' into 'main'
chore: update npins dependencies

See merge request oceanbox/Poseidon!147
2026-01-23 13:34:02 +01:00
5fb1ae0678 Merge branch 'main' into 'automated/npins-update-20260123'
# Conflicts:
#   nix/sources.json
2026-01-23 13:13:42 +01:00
97c03e216b chore: update npins dependencies
Automated update of Nix dependencies via npins.

    Updated packages:
    +      "hash": "sha256-XH6awru9NnBc/m+2YhRNT8r1PAKEiPGF3gs//F3ods0="
+      "revision": "a1ef738813b15cf8ec759bdff5761b027e3e1d23",
+      "hash": "sha256-Efs3VUPelRduf3PpfPP2ovEB4CXT7vHf8W+xc49RL/U="
2026-01-23 12:11:33 +00:00
semantic-release-bot
bab4490847 chore(release): 1.40.5
## [1.40.5](https://gitlab.com/oceanbox/Poseidon/compare/v1.40.4...v1.40.5) (2026-01-21)

### Bug Fixes

* **xtractor:** Reduce to 4 cores per task ([8e824d4](8e824d4afa))
2026-01-21 12:52:40 +00:00
8e824d4afa fix(xtractor): Reduce to 4 cores per task 2026-01-21 13:49:19 +01:00
semantic-release-bot
777cf1a31d chore(release): 1.40.4
## [1.40.4](https://gitlab.com/oceanbox/Poseidon/compare/v1.40.3...v1.40.4) (2026-01-21)

### Bug Fixes

* **xtractor:** Reduce core requirement to 8 ([efacb2a](efacb2a332))
2026-01-21 09:41:49 +00:00
efacb2a332 fix(xtractor): Reduce core requirement to 8
This means we can run 8 in parallelle on the 64 core nodes.
Since it's very IO heavy.
2026-01-21 10:38:06 +01:00
semantic-release-bot
17c4e9dd22 chore(release): 1.40.3
## [1.40.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.40.2...v1.40.3) (2026-01-20)

### Bug Fixes

* **inbox|xtracto:** Delete/Read msg and allow non-ascii xtractor names ([d8d5e07](d8d5e076ba))
* **multiauth:** Add clientId to redirect on signout ([54c40d7](54c40d7acc))
2026-01-20 14:41:54 +00:00
503ccbb2ad Merge branch 'mrtz/oae' into 'main'
fix(inbox|xtractor): Delete/Read msg and allow non-ascii xtractor names

See merge request oceanbox/Poseidon!146
2026-01-20 15:39:09 +01:00
54c40d7acc fix(multiauth): Add clientId to redirect on signout
Previously we used `id_token_hint`, but it's saved in the cookie.
This will instead require a client_id (which identifies your application),
so Keycloak knows which application you’re requesting a redirect for.
2026-01-20 14:04:46 +01:00
d8d5e076ba fix(inbox|xtracto): Delete/Read msg and allow non-ascii xtractor names 2026-01-20 13:35:48 +01:00
semantic-release-bot
fd2b3fe691 chore(release): 1.40.2
## [1.40.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.40.1...v1.40.2) (2026-01-19)

### Bug Fixes

* **xtract:** Disabled if not allowed to simulate transport ([e429a85](e429a855e5))
2026-01-19 19:06:55 +00:00
6ae7a7dac8 Merge branch 'mrtz/disable-xtract' into 'main'
fix(xtract): Disabled if not allowed to simulate transport

See merge request oceanbox/Poseidon!145
2026-01-19 20:04:00 +01:00
e429a855e5 fix(xtract): Disabled if not allowed to simulate transport 2026-01-19 19:43:24 +01:00
semantic-release-bot
9ed60b7cc8 chore(release): 1.40.1
## [1.40.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.40.0...v1.40.1) (2026-01-19)

### Bug Fixes

* fix tilt build on net10 ([d5cde19](d5cde19250))
2026-01-19 16:15:18 +00:00
Stig Rune Jensen
175df0ce33 Merge branch 'stigrj/fix-net10' into 'main'
fix: fix tilt build on net10

See merge request oceanbox/Poseidon!144
2026-01-19 16:12:37 +00:00
Stig Rune Jensen
d5cde19250 fix: fix tilt build on net10 2026-01-19 16:25:45 +01:00
2e1165d99c Release codex 0.0.1 2026-01-19 16:04:29 +01:00
a100ffa77b atlantis: Revert renaming of atlantis tilt ing.
Sryy!
2026-01-19 15:46:59 +01:00
95c3608d85 Merge branch 'simkir/codex' into 'main'
Codex updates

See merge request oceanbox/Poseidon!142
2026-01-19 14:39:35 +01:00
15d91d87bf codex: Start showing archives for user
Currently only under group/user, since most archive access is only
gained through the group.
2026-01-19 11:10:11 +01:00
f6ec692ebf codex: Require user group objects to be a group type 2026-01-19 11:10:11 +01:00
f41617c08e codex: Switch fga checkbox spinner with FS.FluentUI 2026-01-19 11:10:11 +01:00
2bf0d82a5b codex: Allow for update of group archive permissions 2026-01-19 11:10:11 +01:00
3e61cfb939 codex: Switch delete archive permission button with Fui 2026-01-19 11:04:55 +01:00
21ec3a04ab codex: Add securityContext to pod 2026-01-19 11:04:55 +01:00
5d6fe5572b codex: Add ability to update user permissions
- Active
- Registered
- Disabled
2026-01-19 11:04:31 +01:00
0a543c7b21 sorcerer: Switch to wildcard allow origin in appsettings 2026-01-19 10:36:41 +01:00
b1ba2effe3 sorcerer: Point tilt sorcerer on staging openfga 2026-01-19 10:36:41 +01:00
626ce34dc0 serverpack: Add note about cookie token option 2026-01-19 10:36:41 +01:00
b879555e6a serverpack: Refactor multiauth sso cookie on validate principal 2026-01-19 10:36:41 +01:00
ed08980df3 codex: Do a dns lookup on openfga url on startup 2026-01-19 10:36:41 +01:00
6aee2bbc60 Format atlantis tilt values.yaml to 2 spaces 2026-01-19 10:36:41 +01:00
d4701c958c Add ports to atlantis allow origin tilt appsettings 2026-01-19 10:36:41 +01:00
446d4f4171 Refactor Atlantis server 2026-01-19 10:36:41 +01:00
1ed2a15c4c Add *.yaml to .editorconfig 2026-01-19 10:36:41 +01:00
42b746871a codex: Refactor OpenFga.useObjects 2026-01-19 10:36:41 +01:00
6c20b01cc2 codex: Remove redundant Router 2026-01-19 10:36:41 +01:00
4f879252a0 codex: Show group memberships no user page
And add group user, which shows a link to the group in the page.
2026-01-19 10:36:41 +01:00
semantic-release-bot
11724987b0 chore(release): 1.40.0
# [1.40.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.39.2...v1.40.0) (2026-01-16)

### Bug Fixes

* **Codex:** expose days instead of frames in arcive form ([6cf5262](6cf5262dd5))
* **Codex:** only allow inbounds time intervals on edit archive ([eaea4b2](eaea4b2e21))

### Features

* **Codex:** edit archives ([ec10932](ec109328fb))
2026-01-16 19:06:19 +00:00
d90703453f Merge branch 'ole/update-archive' into 'main'
Ole/update archive

See merge request oceanbox/Poseidon!143
2026-01-16 20:03:17 +01:00
eaea4b2e21 fix(Codex): only allow inbounds time intervals on edit archive 2026-01-16 10:08:40 +01:00
semantic-release-bot
68efc76e8e chore(release): 1.39.2
## [1.39.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.39.1...v1.39.2) (2026-01-15)

### Bug Fixes

* fix net10 issues ([f4943a1](f4943a148b))
2026-01-15 17:00:22 +00:00
6df17c88c7 Merge branch 'mrtz/net10' into 'main'
chore: Bump to net10

See merge request oceanbox/Poseidon!141
2026-01-15 17:57:24 +01:00
ec109328fb feat(Codex): edit archives 2026-01-15 17:52:47 +01:00
Stig Rune Jensen
f4943a148b fix: fix net10 issues 2026-01-15 17:47:02 +01:00
15e348c17b chore: Bump to net10
Also bumps gitlab ci to v4.5
2026-01-15 15:50:29 +01:00
6cf5262dd5 fix(Codex): expose days instead of frames in arcive form 2026-01-15 10:30:56 +01:00
semantic-release-bot
4b229cd7d7 chore(release): 1.39.1
## [1.39.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.39.0...v1.39.1) (2026-01-15)

### Bug Fixes

* **ci:** Remove schedule check ([ab37e88](ab37e88bb0))
2026-01-15 07:36:06 +00:00
e372be192a Release codex 0.0.1-beta.4 2026-01-15 08:32:49 +01:00
ab37e88bb0 fix(ci): Remove schedule check 2026-01-14 19:12:08 +01:00
semantic-release-bot
d38a784326 chore(release): 1.39.0
# [1.39.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.38.5...v1.39.0) (2026-01-14)

### Bug Fixes

* **Codex:** add * user in archmeister on public archives ([cd678a4](cd678a41f6))
* **Codex:** use feliz router guid matching ([7182f7c](7182f7c9f0))
* **Codex:** utc start_time ([492651e](492651e0f3))
* **DataAgent:** use files from parent attribs instead of archive_files ([eac23e7](eac23e7d1a))

### Features

* **Codex:** ability to add FVCOM archives ([d86db7a](d86db7a66c))
2026-01-14 15:55:00 +00:00
4d18b105c8 Merge branch 'ole/add-archive' into 'main'
Ole/add archive

See merge request oceanbox/Poseidon!140
2026-01-14 16:51:53 +01:00
492651e0f3 fix(Codex): utc start_time 2026-01-14 16:42:57 +01:00
cd678a41f6 fix(Codex): add * user in archmeister on public archives 2026-01-14 12:31:55 +01:00
03fbc14b72 Merge branch 'simkir/rider-sdk-script' into 'main'
Add script for updating which dotnet sdk rider uses

See merge request oceanbox/Poseidon!136
2026-01-14 10:34:13 +01:00
d86db7a66c feat(Codex): ability to add FVCOM archives 2026-01-14 10:23:17 +01:00
7182f7c9f0 fix(Codex): use feliz router guid matching 2026-01-14 10:23:16 +01:00
eac23e7d1a fix(DataAgent): use files from parent attribs instead of archive_files 2026-01-14 10:17:44 +01:00
b500fdb211 devel: correct justfile publish path 2026-01-14 10:17:44 +01:00
180aba4fa5 devel: point tilt codex to staging fga 2026-01-14 10:17:44 +01:00
semantic-release-bot
d9d7221e90 chore(release): 1.38.5
## [1.38.5](https://gitlab.com/oceanbox/Poseidon/compare/v1.38.4...v1.38.5) (2026-01-13)

### Bug Fixes

* **Archmaester:** Rollback add archive if openfga fails ([46e86eb](46e86eb5f9))
2026-01-13 12:37:09 +00:00
b3fe6f8b70 Merge branch 'simkir/archive-add-rollback' into 'main'
Rollback add archive if openfga errors adding permissions

See merge request oceanbox/Poseidon!139
2026-01-13 13:34:31 +01:00
4792720d74 Small hipster refactoring 2026-01-13 11:20:54 +01:00
46e86eb5f9 fix(Archmaester): Rollback add archive if openfga fails 2026-01-13 11:20:54 +01:00
9a890f30fc Merge branch 'simkir/codex' into 'main'
Add ability to add group archive exec ticket in Codex

See merge request oceanbox/Poseidon!138
2026-01-12 17:40:44 +01:00
66c44976d8 Release codex 0.0.1-beta.3 2026-01-12 17:33:07 +01:00
608caeeda2 codex: Add ability to add group archive exec ticket
Also fix some state in the group archive permissions view
2026-01-12 17:33:07 +01:00
55bcaaf963 Release codex 0.0.1-beta.2 2026-01-12 14:45:20 +01:00
semantic-release-bot
ce10ea93db chore(release): 1.38.4
## [1.38.4](https://gitlab.com/oceanbox/Poseidon/compare/v1.38.3...v1.38.4) (2026-01-12)

### Bug Fixes

* **nix:** Bump node deps ([e513d87](e513d87d24))
2026-01-12 13:44:08 +00:00
e513d87d24 fix(nix): Bump node deps 2026-01-12 14:41:00 +01:00
semantic-release-bot
08061bc6ce chore(release): 1.38.3
## [1.38.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.38.2...v1.38.3) (2026-01-12)

### Bug Fixes

* **xtractor:** Set maxEndDate to 1 year and format ([5315a05](5315a05fa2))
2026-01-12 13:31:57 +00:00
dd55a0c9df Merge branch 'mrtz/fix-xtractor-2' into 'main'
fix(xtractor): Set maxEndDate to 1 year and format

See merge request oceanbox/Poseidon!137
2026-01-12 14:29:48 +01:00
89f0f768e3 Merge branch 'simkir/codex' into 'main'
Ability to add new view permission to group archive

See merge request oceanbox/Poseidon!133
2026-01-12 14:25:39 +01:00
5315a05fa2 fix(xtractor): Set maxEndDate to 1 year and format 2026-01-12 14:22:34 +01:00
57b28daf4e Add script for updating which dotnet sdk rider uses 2026-01-12 13:51:16 +01:00
semantic-release-bot
0ba060d78c chore(release): 1.38.2
## [1.38.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.38.1...v1.38.2) (2026-01-12)

### Bug Fixes

* **atlas:** Disable Snowflakes ([4cd3673](4cd3673d15))
2026-01-12 11:54:28 +00:00
Stig Rune Jensen
b2077ae317 Merge branch 'mrtz/no-snow' into 'main'
fix(atlas): Disable Snowflakes

See merge request oceanbox/Poseidon!135
2026-01-12 11:51:48 +00:00
4cd3673d15 fix(atlas): Disable Snowflakes 2026-01-12 09:07:44 +01:00
semantic-release-bot
771712ad9a chore(release): 1.38.1
## [1.38.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.38.0...v1.38.1) (2026-01-11)

### Bug Fixes

* **xtractor:** Move to short partition and 16 CPU, also set max duration to 1 year ([56d3476](56d34767d7))
2026-01-11 14:26:39 +00:00
1d6941ecc6 Merge branch 'mrtz/xtract-fixes' into 'main'
fix(xtractor): Move to short partition and 16 CPU, also set max duration to 1 year

See merge request oceanbox/Poseidon!134
2026-01-11 15:23:14 +01:00
56d34767d7 fix(xtractor): Move to short partition and 16 CPU, also set max duration to 1 year 2026-01-11 14:04:01 +01:00
5fbd914e24 codex: Fix fetching archives user's can exec 2026-01-09 16:04:52 +01:00
5f193c559f codex: Add permission to group archive
Hook the Add permission button, and make it post the new tuples.

Still need to properly load the new permissions, so that you do not need
to refresh.
2026-01-09 16:03:03 +01:00
a998483d2c codex: Fix sidebar and content growing 2026-01-09 16:01:48 +01:00
semantic-release-bot
a4159f0fff chore(release): 1.38.0
# [1.38.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.37.1...v1.38.0) (2026-01-07)

### Bug Fixes

* client layout ([efb3292](efb3292d9f))

### Features

* add fluent ui to codex ([d8bf174](d8bf174d3a))
2026-01-07 13:28:36 +00:00
866f3a317b Merge branch 'ole/codex-fui' into 'main'
feat: add fluent ui to codex

See merge request oceanbox/Poseidon!132
2026-01-07 14:25:30 +01:00
4eac05cbb7 chore: bump codex do beta 2026-01-07 14:13:53 +01:00
efb3292d9f fix: client layout 2026-01-07 13:49:26 +01:00
6e822bd5d1 devel: template name into tilt manifest 2026-01-07 13:49:11 +01:00
d8bf174d3a feat: add fluent ui to codex 2026-01-07 13:09:20 +01:00
2f7be7b051 devel: downgrade to fable 4.26 2026-01-05 16:11:47 +01:00
18bb207e4a Merge branch 'ole/update-bootstrap' into 'main'
devel: update bootstrap guide and justfile

See merge request oceanbox/Poseidon!130
2026-01-05 15:00:27 +01:00
c914f4a477 devel(justfile): remove cwd argument 2026-01-05 14:56:35 +01:00
2da1be0c6b devel: update bootstrap guide 2026-01-05 14:54:23 +01:00
eb00b8c19d devel: install node deps in build script 2026-01-05 14:54:23 +01:00
09a9e47348 devel: add net9 to nix shell
until we bump projects to net10, include net9 in the dev shell so we can
run it
2026-01-05 14:52:13 +01:00
156ae2315a chore: remove unused redis templates 2026-01-05 14:52:13 +01:00
semantic-release-bot
cf6bedbd9b chore(release): 1.37.1
## [1.37.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.37.0...v1.37.1) (2026-01-05)

### Bug Fixes

* **xtractor:** Avoid using union types in Dapr Actors ([14c1a57](14c1a57331))
2026-01-05 13:21:22 +00:00
36aa90519e Merge branch 'mrtz/fix-xtractor' into 'main'
fix(xtractor): Avoid using union types in Dapr Actors

See merge request oceanbox/Poseidon!129
2026-01-05 14:16:25 +01:00
14c1a57331 fix(xtractor): Avoid using union types in Dapr Actors
Instead of using union types in the Xtractor we fetch
the individual types instead.
2026-01-05 14:10:26 +01:00
d7f0630693 Merge branch 'automated/npins-update-20260102' into 'main'
chore: update npins dependencies

See merge request oceanbox/Poseidon!128
2026-01-02 20:15:32 +01:00
3b7149f161 chore: update npins dependencies
Automated update of Nix dependencies via npins.

    Updated packages:
    +      "hash": "sha256-IVq6jxkcTuudaj3c78xl2xG2fZSL9gS7JMPFUl3q7j4="
+      "revision": "f0927703b7b1c8d97511c4116eb9b4ec6645a0fa",
+      "hash": "sha256-6MkqajPICgugsuZ92OMoQcgSHnD6sJHwk8AxvMcIgTE="
2026-01-02 18:25:04 +00:00
5545e90160 Merge branch 'automated/npins-update-20251229' into 'main'
chore: update npins dependencies

See merge request oceanbox/Poseidon!127
2025-12-29 18:03:40 +01:00
da5b38d1ea chore: update npins dependencies
Automated update of Nix dependencies via npins.

    Updated packages:
    +      "hash": "sha256-wyT7Pl6tMFbFrs8Lk/TlEs81N6L+VSybPfiIgzU8lbQ="
+      "hash": "sha256-y++BijM+FRkKDhVrL7YXZQiJ0DNVMiRN7yHf6QIXBUI="
+      "hash": "sha256-g5DRB9fAyEv6Xf41Bj9RpVl9th0Zz+v1jgvJVg51W3w="
+      "revision": "b68b780b69702a090c8bb1b973bab13756cc7a27",
+      "hash": "sha256-t3T/xm8zstHRLx+pIHxVpQTiySbKqcQbK+r+01XVKc0="
2025-12-29 17:02:08 +00:00
65928c4064 ci: Add schedueled updater 2025-12-29 17:57:27 +01:00
semantic-release-bot
033b61dd4f chore(release): 1.37.0
# [1.37.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.36.0...v1.37.0) (2025-12-22)

### Features

* Add XtractActor ([a8a187a](a8a187a412))
2025-12-22 12:15:31 +00:00
5725d43b11 Merge branch 'mrtz/plumxcavator' into 'main'
feat: Add XtractActor

Closes #92

See merge request oceanbox/Poseidon!126
2025-12-22 13:09:13 +01:00
a8a187a412 feat: Add XtractActor
Data extraction from archives. It utilizes Excavator
and some python scripts to extract data from the existing
archives and summarizes it in plots and csv files.

Currently it has a basic UI only supporting, name,
start/stop and coordinates as inputs. Written in an
extandable way for future data extraction methods.
2025-12-19 16:46:45 +01:00
semantic-release-bot
f30e16b15e chore(release): 1.36.0
# [1.36.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.35.2...v1.36.0) (2025-12-12)

### Features

* add Saturn.ReverseProxy middleware ([bd74504](bd745042df))
2025-12-12 13:49:34 +00:00
bd745042df feat: add Saturn.ReverseProxy middleware 2025-12-12 14:45:53 +01:00
70878e1423 chore: Bump deps 2025-12-09 12:58:14 +01:00
937b2c367b devel: Fix justfile for client 2025-12-09 10:10:11 +01:00
faa0a8533e Merge branch 'mrtz/just' into 'main'
devel: Migrate from FAKE to Just

See merge request oceanbox/Poseidon!125
2025-12-06 11:33:38 +01:00
1cb9d455db devel: Migrate from FAKE to Just
Also updates dotnetPackage CIs to v4.4
2025-12-06 09:39:59 +01:00
semantic-release-bot
453c9d234c chore(release): 1.35.2
## [1.35.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.35.1...v1.35.2) (2025-12-01)

### Bug Fixes

* **drifters:** reverse toggle on postdrift analysis ([563faa6](563faa6c0b))
2025-12-01 18:27:24 +00:00
369127e081 Merge branch 'stigrj/fix-rev' into 'main'
fix(drifters): reverse toggle on postdrift analysis

See merge request oceanbox/Poseidon!124
2025-12-01 19:23:58 +01:00
Stig Rune Jensen
563faa6c0b fix(drifters): reverse toggle on postdrift analysis 2025-12-01 15:01:16 +01:00
semantic-release-bot
17163ab002 chore(release): 1.35.1
## [1.35.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.35.0...v1.35.1) (2025-12-01)

### Bug Fixes

* **stats:** no stats banner showing when stats are available, closing [#87](https://gitlab.com/oceanbox/Poseidon/issues/87) ([e75ffc4](e75ffc41e5))
* **stats:** set priority order of depth plots, closing [#88](https://gitlab.com/oceanbox/Poseidon/issues/88) ([2887e6a](2887e6a909))
2025-12-01 13:33:48 +00:00
Stig Rune Jensen
6293e9e67a Merge branch 'stigrj/fix-stats' into 'main'
Fix: minor release bugs

Closes #88 and #87

See merge request oceanbox/Poseidon!123
2025-12-01 13:30:10 +00:00
semantic-release-bot
a68ef32614 chore(release): 1.35.0
# [1.35.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.34.2...v1.35.0) (2025-12-01)

### Features

* ❄️ ([a620c26](a620c26812))
2025-12-01 13:11:49 +00:00
4de10614be Merge branch 'mrtz/flakes' into 'main'
feat: ❄️

See merge request oceanbox/Poseidon!122
2025-12-01 14:08:05 +01:00
Stig Rune Jensen
2887e6a909 fix(stats): set priority order of depth plots, closing #88 2025-12-01 13:37:05 +01:00
Stig Rune Jensen
e75ffc41e5 fix(stats): no stats banner showing when stats are available, closing #87 2025-12-01 10:37:03 +01:00
Moritz Jörg
a620c26812 feat: ❄️ 2025-11-30 13:13:45 +01:00
semantic-release-bot
f2bb57b50d chore(release): 1.34.2
## [1.34.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.34.1...v1.34.2) (2025-11-30)

### Bug Fixes

* Temporarily use vtn as only source for wind barbs ([048e803](048e80356b))
2025-11-30 11:36:13 +00:00
8e0cb2105a Merge branch 'mrtz/vtn-arome' into 'main'
fix: Temporarily use vtn as only source for wind barbs

See merge request oceanbox/Poseidon!121
2025-11-30 12:32:35 +01:00
Moritz Jörg
048e80356b fix: Temporarily use vtn as only source for wind barbs 2025-11-30 12:11:14 +01:00
semantic-release-bot
a47fb89143 chore(release): 1.34.1
## [1.34.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.34.0...v1.34.1) (2025-11-29)

### Bug Fixes

* **stats:** find available stats archives ([48d46ed](48d46eda62))
2025-11-29 15:32:26 +00:00
Stig Rune Jensen
1b8167c66e Merge branch 'stigrj/fix-stats' into 'main'
fix(stats): find available stats archives

See merge request oceanbox/Poseidon!120
2025-11-29 15:14:50 +00:00
Stig Rune Jensen
48d46eda62 fix(stats): find available stats archives 2025-11-29 15:35:09 +01:00
semantic-release-bot
36307c822e chore(release): 1.34.0
# [1.34.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.33.11...v1.34.0) (2025-11-29)

### Bug Fixes

* extract data cage interaction matrix, closing [#84](https://gitlab.com/oceanbox/Poseidon/issues/84) ([afc888a](afc888ab60))
* reintroduce active layer selector, closes [#74](https://gitlab.com/oceanbox/Poseidon/issues/74) ([c1fa85f](c1fa85fd1b))
* remove parameter limitations on particle sims ([69b380e](69b380e665))

### Features

* fly-to coordinate button, closes [#85](https://gitlab.com/oceanbox/Poseidon/issues/85) ([a153238](a153238f79))
2025-11-29 11:41:15 +00:00
Stig Rune Jensen
dca32db800 Merge branch 'stigrj/mini-feats' into 'main'
Mini feats

Closes #77, #85, #74, and #84

See merge request oceanbox/Poseidon!119
2025-11-29 11:37:38 +00:00
Stig Rune Jensen
a153238f79 feat: fly-to coordinate button, closes #85 2025-11-28 23:21:32 +01:00
Stig Rune Jensen
c1fa85fd1b fix: reintroduce active layer selector, closes #74 2025-11-28 18:45:36 +01:00
Stig Rune Jensen
69b380e665 fix: remove parameter limitations on particle sims 2025-11-28 18:37:25 +01:00
Stig Rune Jensen
afc888ab60 fix: extract data cage interaction matrix, closing #84 2025-11-28 18:01:02 +01:00
semantic-release-bot
9a4ef08060 chore(release): 1.33.11
## [1.33.11](https://gitlab.com/oceanbox/Poseidon/compare/v1.33.10...v1.33.11) (2025-11-28)

### Bug Fixes

* **ci:** Run archivist ci on coffee ([f68b7f6](f68b7f68c8))
2025-11-28 12:13:21 +00:00
f68b7f68c8 fix(ci): Run archivist ci on coffee 2025-11-28 13:10:00 +01:00
semantic-release-bot
d8143a6b8d chore(release): 1.33.10
## [1.33.10](https://gitlab.com/oceanbox/Poseidon/compare/v1.33.9...v1.33.10) (2025-11-28)

### Bug Fixes

* **ci:** Build on Coffee ([e04d36c](e04d36ca12))
2025-11-28 11:45:23 +00:00
e04d36ca12 fix(ci): Build on Coffee 2025-11-28 12:41:40 +01:00
semantic-release-bot
275ec44a97 chore(release): 1.33.9
## [1.33.9](https://gitlab.com/oceanbox/Poseidon/compare/v1.33.8...v1.33.9) (2025-11-28)

### Bug Fixes

* **nix:** Bump hash for node_modules ([fff7913](fff7913cd5))
2025-11-28 11:06:34 +00:00
fff7913cd5 fix(nix): Bump hash for node_modules 2025-11-28 12:03:15 +01:00
semantic-release-bot
23ba2efe96 chore(release): 1.33.8
## [1.33.8](https://gitlab.com/oceanbox/Poseidon/compare/v1.33.7...v1.33.8) (2025-11-28)

### Bug Fixes

* **ci:** Format .gitlab-ci.yml and move codex to nix runner ([3140c07](3140c07ad0))
* **ci:** Remove unsed var ([759bbc6](759bbc6f60))
* **ci:** Try using v4.4 ([c1be7c4](c1be7c468d))
* **ci:** Use 4.4 for check ([b109dbd](b109dbdcbd))
2025-11-28 10:53:19 +00:00
759bbc6f60 fix(ci): Remove unsed var 2025-11-28 11:50:27 +01:00
3140c07ad0 fix(ci): Format .gitlab-ci.yml and move codex to nix runner 2025-11-28 11:49:22 +01:00
b109dbdcbd fix(ci): Use 4.4 for check 2025-11-28 11:44:03 +01:00
c1be7c468d fix(ci): Try using v4.4 2025-11-28 11:42:22 +01:00
semantic-release-bot
d3797115f7 chore(release): 1.33.7
## [1.33.7](https://gitlab.com/oceanbox/Poseidon/compare/v1.33.6...v1.33.7) (2025-11-28)

### Bug Fixes

* **ci:** Change name to codex ([6d04af6](6d04af6230))
* **ci:** Correct tag for codex pipeline ([7cf5064](7cf50641f9))
* **ci:** Run Codex on Coffee ([dd398bd](dd398bd96b))
2025-11-28 10:32:08 +00:00
6d04af6230 fix(ci): Change name to codex 2025-11-28 11:28:21 +01:00
7cf50641f9 fix(ci): Correct tag for codex pipeline 2025-11-28 11:27:40 +01:00
dd398bd96b fix(ci): Run Codex on Coffee 2025-11-28 11:27:16 +01:00
semantic-release-bot
63d782ade4 chore(release): 1.33.6
## [1.33.6](https://gitlab.com/oceanbox/Poseidon/compare/v1.33.5...v1.33.6) (2025-11-28)

### Bug Fixes

* **maps:** Make FluentUI DatePicker popup inline; closes [#83](https://gitlab.com/oceanbox/Poseidon/issues/83) ([e3a1f56](e3a1f56b87))
2025-11-28 09:09:07 +00:00
e3a1f56b87 fix(maps): Make FluentUI DatePicker popup inline; closes #83
So it's not a portal which puts the elem far out in the dom, which is
outside the FluentProvider?
2025-11-28 10:03:16 +01:00
semantic-release-bot
2f18948cce chore(release): 1.33.5
## [1.33.5](https://gitlab.com/oceanbox/Poseidon/compare/v1.33.4...v1.33.5) (2025-11-27)

### Bug Fixes

* General fixes "stats" menu ([c3c9e8e](c3c9e8e4e2)), closes [#79](https://gitlab.com/oceanbox/Poseidon/issues/79) [#80](https://gitlab.com/oceanbox/Poseidon/issues/80) [#81](https://gitlab.com/oceanbox/Poseidon/issues/81) [#82](https://gitlab.com/oceanbox/Poseidon/issues/82)
* no more DateFlicker (thanks Simen) ([7584bf6](7584bf661f))
* remove goto today in date picker ([f23b3f1](f23b3f1821))
* **stats:** alert banner on missing stats ([5bb2ffd](5bb2ffd67c))
2025-11-27 17:43:29 +00:00
Stig Rune Jensen
4786850431 Merge branch 'mrtz/fixes' into 'main'
fix: General fixes in pipette menu

Closes #79, #80, #81, and #82

See merge request oceanbox/Poseidon!116
2025-11-27 17:39:28 +00:00
Stig Rune Jensen
7584bf661f fix: no more DateFlicker (thanks Simen) 2025-11-27 18:30:27 +01:00
Stig Rune Jensen
f23b3f1821 fix: remove goto today in date picker 2025-11-27 17:26:39 +01:00
Stig Rune Jensen
5bb2ffd67c fix(stats): alert banner on missing stats 2025-11-27 16:23:28 +01:00
c3c9e8e4e2 fix: General fixes "stats" menu
Closes #79
Closes #80
Closes #81
Closes #82
2025-11-27 16:23:28 +01:00
b03908f93e Update node-modules.nix hash 2025-11-27 14:54:53 +01:00
c2e7762df8 Codex: Copy bundle out of nix store for container
Since ASP.NET doesn't know how to follow a symbolic link...
2025-11-27 14:44:14 +01:00
semantic-release-bot
c83beadd45 chore(release): 1.33.4
## [1.33.4](https://gitlab.com/oceanbox/Poseidon/compare/v1.33.3...v1.33.4) (2025-11-27)

### Bug Fixes

* **js:** Remove unused packages ([10a8c42](10a8c42319))
2025-11-27 13:03:14 +00:00
46d6b1277b Merge branch 'mrtz/fluent-cleanup' into 'main'
fix(js): Remove unused packages

See merge request oceanbox/Poseidon!117
2025-11-27 13:59:07 +01:00
Moritz Jörg
10a8c42319 fix(js): Remove unused packages 2025-11-27 13:48:24 +01:00
9ed4165ebc Add ca certs to Codex container 2025-11-27 13:21:50 +01:00
semantic-release-bot
8cc840adc5 chore(release): 1.33.3
## [1.33.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.33.2...v1.33.3) (2025-11-27)

### Bug Fixes

* **nix:** Pre-commit with prek ([3093757](3093757454))
* **nix:** Watch correct lock file ([79c6e2a](79c6e2abd0))
2025-11-27 12:12:12 +00:00
Moritz Jörg
79c6e2abd0 fix(nix): Watch correct lock file 2025-11-27 13:08:08 +01:00
Moritz Jörg
3093757454 fix(nix): Pre-commit with prek 2025-11-26 23:48:12 +01:00
028945bfca chore: Add fluentui/react-calendar-compat package 2025-11-26 23:06:10 +01:00
semantic-release-bot
09556bc5df chore(release): 1.33.2
## [1.33.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.33.1...v1.33.2) (2025-11-26)

### Bug Fixes

* create input display modal on demand ([1fed7ad](1fed7adf80))
* **drifters:** Add datepickers for drifters sims ([785d0d5](785d0d57ae))
* sim duration with restrictions ([a9145f6](a9145f6f79))
* **timeline:** display use local time in timeline ([ccbe076](ccbe07619f))
* **timeline:** Marker uses UTC instead of CET + DST ([a474e7c](a474e7cbd4))
2025-11-26 20:36:23 +00:00
Stig Rune Jensen
94b7f25852 Merge branch 'mrtz/date' into 'main'
Feat: Date/Time Picker

Closes #78

See merge request oceanbox/Poseidon!103
2025-11-26 20:32:24 +00:00
Stig Rune Jensen
2831c8a5cb Merge branch 'main' into 'mrtz/date'
# Conflicts:
#   nix/packages/atlantis-client.nix
#   src/Atlantis/src/Server/packages.lock.json
2025-11-26 20:23:40 +00:00
Stig Rune Jensen
1fed7adf80 fix: create input display modal on demand 2025-11-26 21:19:05 +01:00
Stig Rune Jensen
a9145f6f79 fix: sim duration with restrictions 2025-11-26 20:03:41 +01:00
Stig Rune Jensen
ccbe07619f fix(timeline): display use local time in timeline 2025-11-26 19:20:27 +01:00
95e6096fbb Fix pipeline 2025-11-26 16:02:37 +01:00
semantic-release-bot
65c29879ab chore(release): 1.33.1
## [1.33.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.33.0...v1.33.1) (2025-11-26)

### Bug Fixes

* Add missing * in Drifters ArchiveType FromString ([7054ade](7054ade55d))
* **Atlantis:** Add wildcards in allow origin ([cde779e](cde779ea00))
2025-11-26 13:40:39 +00:00
b794fc3b68 Merge branch 'user-portal' into 'main'
User portal MVP also called *Codex

See merge request oceanbox/Poseidon!97
2025-11-26 14:36:30 +01:00
df7be8d894 Rename Cortex to Codex 2025-11-26 13:41:17 +01:00
a474e7cbd4 fix(timeline): Marker uses UTC instead of CET + DST 2025-11-26 11:28:37 +01:00
5aa83c4bf2 Update node_modules hash 2025-11-26 11:28:20 +01:00
210a04c24d Fix merge conflict 2025-11-26 11:12:52 +01:00
6a35b374c2 Add .gitlab-ci.yml to Cortex 2025-11-26 11:10:37 +01:00
785d0d57ae fix(drifters): Add datepickers for drifters sims 2025-11-25 16:57:54 +01:00
7e376f3609 Rename Almennr to Cortex 2025-11-25 16:48:17 +01:00
ae233cb764 Build Almennr with Nix 2025-11-25 16:48:17 +01:00
0b9751a97b Start on adding permissions within a group archive 2025-11-25 16:48:17 +01:00
c6c5659b2c Rename namespaces from Oceanbox.UserPortal to Oceanbox.Almennr 2025-11-25 16:48:17 +01:00
e1d67df304 Rename UserPortal folder to Almennr 2025-11-25 16:48:17 +01:00
7b00f80ac9 Rename UserPortal solution folder to Almennr 2025-11-25 16:48:17 +01:00
92be7a0201 Allow for deleting group archive permissions 2025-11-25 16:48:17 +01:00
7700924d0e Add missing searchTerm in remoting ArchiveFilter type 2025-11-25 16:48:17 +01:00
6620c44202 Fix upstream merge conflict 2025-11-25 16:48:17 +01:00
ca5c6791d3 wip: Date/Time Picker
Datepickers are now the source of truth, timeline
only used for visualization.

Workaround for correct JS via emit.
2025-11-24 18:01:34 +01:00
semantic-release-bot
d80797bb17 chore(release): 1.33.0
# [1.33.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.32.0...v1.33.0) (2025-11-24)

### Features

* properly display sim parameters, and fix clone, closing [#70](https://gitlab.com/oceanbox/Poseidon/issues/70) ([4980a41](4980a41a44))
2025-11-24 16:39:39 +00:00
Stig Rune Jensen
4d9e78cd69 Merge branch 'stigrj/display-sim-params' into 'main'
Enhance: display sim params

Closes #70

See merge request oceanbox/Poseidon!114
2025-11-24 16:35:20 +00:00
Stig Rune Jensen
4980a41a44 feat: properly display sim parameters, and fix clone, closing #70
wip: refactor transport release model input

wip: refactor watercontact release model input

wip: add show json button

wip: display release sites

wip: flex-grow and display meta controls and submit buttons

wip: flex-grow and display virus/lice controls

wip: flex-grow and display sedimentation model

wip: display header

wip: refactor and flex-grow deposition sites

wip: flex-grow deposition release groups

wip: display deposition groups and fix clone
2025-11-24 16:55:30 +01:00
semantic-release-bot
d5047b0189 chore(release): 1.32.0
# [1.32.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.31.0...v1.32.0) (2025-11-20)

### Bug Fixes

* spell out units in plots ([020a2f2](020a2f2e00))

### Features

* Add Density profile plot and cancelable jobs ([8472708](847270877a)), closes [oceanbox/Poseidon#30](https://gitlab.com/oceanbox/Poseidon/issues/30)
2025-11-20 16:32:03 +00:00
Stig Rune Jensen
8c17a644d5 Merge branch 'mrtz/dens' into 'main'
feat: Add Density profile plot and cancelable jobs

Closes #67

See merge request oceanbox/Poseidon!112
2025-11-20 16:27:18 +00:00
Stig Rune Jensen
c77ce73a69 devel: update tilt settings 2025-11-20 17:19:11 +01:00
Stig Rune Jensen
020a2f2e00 fix: spell out units in plots 2025-11-20 17:07:24 +01:00
847270877a feat: Add Density profile plot and cancelable jobs
Adds a Density Plot to the Pipette, it uses the temp and
salinity at node together with surface pressure.

Also fixes the matrix titles for oceanbox/Poseidon#30 and
adds a cancel button as part of oceanbox/Poseidon#41.
2025-11-20 16:16:53 +01:00
d4dd7945cb Use new cnpg secret 2025-11-20 16:02:35 +01:00
4626333c74 vite: Do not watch fs files 2025-11-20 16:02:08 +01:00
d5341acd28 Add group archive view
Here you can see the groups' specific relations to a single archive
2025-11-20 16:01:40 +01:00
3c2da99235 Update remoting OpenFGA types
Go back to F# style record naming, and local time on creating requests.
Change the object on saving to OpenFGA instead. OpenFGA actually
requires the date time string to be UTC for it to be accepted. So we
have to convert the DateTime kind to UTC before we let OpenFGA serialize
the object.
2025-11-20 10:31:51 +01:00
013b5fea91 Format DataAgent entity ArchiveContext 2025-11-19 17:34:19 +01:00
7ea657b582 Implement form for adding archive to group
This includes all openfga conditions. Like a view term or the exec
ticket.
2025-11-19 17:33:31 +01:00
d1e416c850 Create separate admin api for user portal
The internal admin panel might just be different from a customer admin.
It depends on whether we plan on even exposing this service out, and
instead create a separate admin panel within maps or something
equivalent.
2025-11-19 17:29:32 +01:00
232c095954 Simplify Client.Lib.Utils.debounce
Mutation :(
2025-11-19 17:26:01 +01:00
Stig Rune Jensen
f5a3920eea chore(release): 7.3.0 2025-11-19 13:08:38 +01:00
semantic-release-bot
51cb94a2e0 chore(release): 1.31.0
# [1.31.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.30.1...v1.31.0) (2025-11-19)

### Features

* **DataAgent:** offline eval function when you have a dto ([1982770](19827701aa))
2025-11-19 11:48:26 +00:00
Stig Rune Jensen
e0b5522b8f Merge branch 'stigrj/offline-eval' into 'main'
feat(DataAgent): offline eval function when you have a dto

See merge request oceanbox/Poseidon!113
2025-11-19 11:44:34 +00:00
Stig Rune Jensen
19827701aa feat(DataAgent): offline eval function when you have a dto 2025-11-19 11:55:43 +01:00
48dcee7d7f Debounce text input instead of useEffect 2025-11-18 11:38:39 +01:00
02b6b36f95 Start on add archive to group
The add form shows a list of all archives that you can search and
filter. To do id search, I had to switch to Dapper because of EFCore. It
didn't allow me to convert the archive Guid to a string.
2025-11-18 10:53:16 +01:00
8f38f19dd0 Move some client public files around 2025-11-14 16:39:09 +01:00
04bde9e221 Delete user from OpenFGA db directly 2025-11-14 16:38:40 +01:00
3d948d3ba9 Stop archive lists from flickering on changing page 2025-11-14 16:32:45 +01:00
67c23b8707 Make group member lists rerender on add 2025-11-14 16:31:09 +01:00
fbe0e59175 Update archive flex box styling 2025-11-14 16:30:57 +01:00
1f86f950d6 Add None case to add group to archive select 2025-11-14 16:30:26 +01:00
ffce84bb37 Change Archmaester conn. to transient DI
Before I created a new datasource for every query. This is probably very
wrong. Npgsql does some internal tracking of connections and pooling, so
we probably interfered with this by creating new instances all the time.
Now, we create one data source instance at the beginning, and then in
the transient service it creates a connection per request.
2025-11-14 16:26:09 +01:00
2308d50310 Add button to add user to group
Adds both to the database and OpenFGA
2025-11-12 17:08:45 +01:00
1f5dd53673 Add button to delete user
This removes their entries from Archmaester and disables the user in
OpenFGA
2025-11-12 16:10:59 +01:00
f8940c9220 devel: Bump Atlantis lockfile 2025-11-11 17:39:18 +01:00
9566bce0bd Merge branch 'mrtz/dragonfly' into 'main'
devel(tilt): Migrate to a Redis Operator

See merge request oceanbox/Poseidon!111
2025-11-11 17:18:55 +01:00
semantic-release-bot
2f9b292ee4 chore(release): 1.30.1
## [1.30.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.30.0...v1.30.1) (2025-11-11)

### Bug Fixes

* Remove implicit FSharp.Core, Lib and Nuget for better CPM compat ([f8ab41b](f8ab41bbda))
2025-11-11 16:13:52 +00:00
54d8c3fe09 devel(tilt): Migrate to a Redis Operator 2025-11-11 17:10:37 +01:00
ddd84b2af3 Merge branch 'mrtz/dbp' into 'main'
fix: Remove implicit FSharp.Core, Lib and Nuget for better cpm compat

See merge request oceanbox/Poseidon!108
2025-11-11 17:10:05 +01:00
f8ab41bbda fix: Remove implicit FSharp.Core, Lib and Nuget for better CPM compat
With this commit all fsproj's dependent on the FSharp.Core
version defined in Directory.Packages.props. Can be
overwritten if needed with `VersionOverride=9.0.202`.
2025-11-11 17:02:07 +01:00
2f4a458964 Add Read and Check OpenFGA API 2025-11-11 11:03:40 +01:00
semantic-release-bot
799fbe67e5 chore(release): 1.30.0
# [1.30.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.29.0...v1.30.0) (2025-11-10)

### Bug Fixes

* don't fetch tz contour on update timeframe ([beedeb8](beedeb836b))
* switch to element path and use haversine distance ([61b4323](61b4323803))

### Features

* add Chaikin curve smoothing algorithm ([d4766f2](d4766f249b))
* sea distance circle, closing [#73](https://gitlab.com/oceanbox/Poseidon/issues/73) ([fc41c91](fc41c91d41))
2025-11-10 18:05:17 +00:00
33d94c5d94 Merge branch 'stigrj/sea-distance' into 'main'
feat: compute sea distance radius

Closes #73

See merge request oceanbox/Poseidon!109
2025-11-10 19:00:33 +01:00
Stig Rune Jensen
beedeb836b fix: don't fetch tz contour on update timeframe 2025-11-10 18:52:40 +01:00
d4766f249b feat: add Chaikin curve smoothing algorithm 2025-11-10 18:52:40 +01:00
Stig Rune Jensen
fc41c91d41 feat: sea distance circle, closing #73 2025-11-10 18:52:20 +01:00
679ab2f945 Add group claims even if "expires_at" token is missing 2025-11-10 16:40:36 +01:00
13b257f7ff More fga queries, delete archive, and add archive group 2025-11-10 16:37:07 +01:00
bfc25a2894 Add Fargo to UserPortal 2025-11-07 15:11:07 +01:00
9f112aedd8 Make auth redirect work 2025-11-07 15:01:20 +01:00
Stig Rune Jensen
61b4323803 fix: switch to element path and use haversine distance 2025-11-07 11:09:19 +01:00
semantic-release-bot
271f40cdbc chore(release): 1.29.0
# [1.29.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.28.0...v1.29.0) (2025-11-06)

### Bug Fixes

* move map type picker to the usual place ([917058b](917058b7be))
* naming ([5be346e](5be346e0fc))
* switch to node path and use lonlat coord in api ([d8f38c4](d8f38c496d))
* update fvcomkit ([da6a199](da6a1995c1))

### Features

* calculation of sea distance, closes [#51](https://gitlab.com/oceanbox/Poseidon/issues/51) ([1b4f4ef](1b4f4ef360))
2025-11-06 13:46:18 +00:00
Stig Rune Jensen
2bf628103c Merge branch 'stigrj/sea-distance' into 'main'
Feat: calculation of sea distance

Closes #51

See merge request oceanbox/Poseidon!107
2025-11-06 13:41:54 +00:00
Stig Rune Jensen
5be346e0fc fix: naming 2025-11-06 14:28:00 +01:00
Stig Rune Jensen
d8f38c496d fix: switch to node path and use lonlat coord in api 2025-11-06 13:54:20 +01:00
Stig Rune Jensen
917058b7be fix: move map type picker to the usual place 2025-11-05 18:58:58 +01:00
Stig Rune Jensen
1b4f4ef360 feat: calculation of sea distance, closes #51 2025-11-05 18:58:32 +01:00
Stig Rune Jensen
da6a1995c1 fix: update fvcomkit 2025-11-05 18:17:06 +01:00
b030e8eeb7 Build in dev mode 2025-11-05 16:38:19 +01:00
6c8bb8b95a Fix login and auth 2025-11-05 16:37:48 +01:00
semantic-release-bot
a9c6ebc0de chore(release): 1.28.0
# [1.28.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.27.2...v1.28.0) (2025-11-05)

### Bug Fixes

* Don't build Sorcerer Client ([a627b08](a627b08df2))

### Features

* Centralize Nuget Package Management ([b062f66](b062f66cf9))
2025-11-05 11:23:08 +00:00
b50fe38a06 Merge branch 'mrtz/cpm' into 'main'
feat: Centralize Nuget Package Management

See merge request oceanbox/Poseidon!106
2025-11-05 12:19:22 +01:00
a627b08df2 fix: Don't build Sorcerer Client 2025-11-04 17:06:42 +01:00
b062f66cf9 feat: Centralize Nuget Package Management 2025-11-04 16:53:24 +01:00
99c0279a1f Show org. domains 2025-11-03 16:36:59 +01:00
ba3906da71 Refactor stuff
And try getting auth to work. Also create my first hooks!
2025-11-03 16:14:43 +01:00
semantic-release-bot
db87ef5df1 chore(release): 1.27.2
## [1.27.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.27.1...v1.27.2) (2025-11-03)

### Bug Fixes

* cage interaction matrix and labeling, closes [#72](https://gitlab.com/oceanbox/Poseidon/issues/72) ([6d5a724](6d5a72412b))
2025-11-03 11:16:07 +00:00
Stig Rune Jensen
dacc30da0d Merge branch 'stigrj/fix-cage-interaction' into 'main'
fix: cage interaction matrix and labeling, closes #72

Closes #72

See merge request oceanbox/Poseidon!105
2025-11-03 11:12:25 +00:00
Stig Rune Jensen
6d5a72412b fix: cage interaction matrix and labeling, closes #72 2025-11-03 11:18:24 +01:00
semantic-release-bot
748ba286ab chore(release): 1.27.1
## [1.27.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.27.0...v1.27.1) (2025-10-31)

### Bug Fixes

* **Atlantis:** add sanity check for sim names, closes [#66](https://gitlab.com/oceanbox/Poseidon/issues/66) ([637d22e](637d22e0f0))
* **Atlantis:** minor tweaks in ui naming and layout ([187a5e3](187a5e36e3))
* **Atlantis:** switch to chart-trend icon ([9b6a3ef](9b6a3ef133))
* **Atlantis:** use Conc instead of activeLayer ([039df74](039df744d3))
* **Atlantis:** use tab for statistics ([3a9c616](3a9c616f6b))
2025-10-31 13:40:36 +00:00
Stig Rune Jensen
49d50b4bb1 Merge branch 'stigrj/misc-fixes' into 'main'
Fix: misc UI fixes

Closes #66

See merge request oceanbox/Poseidon!104
2025-10-31 13:34:58 +00:00
Stig Rune Jensen
039df744d3 fix(Atlantis): use Conc instead of activeLayer 2025-10-31 14:24:58 +01:00
Stig Rune Jensen
3a9c616f6b fix(Atlantis): use tab for statistics 2025-10-31 13:38:12 +01:00
570a2237f1 List more user objects 2025-10-30 14:11:06 +01:00
50b5524c13 Drifters list and generalize fga user queries
And show users that can view a specific archive
2025-10-30 13:58:23 +01:00
b2c475101c Show model area polygons and impl. group endpoints 2025-10-30 11:17:52 +01:00
Stig Rune Jensen
187a5e36e3 fix(Atlantis): minor tweaks in ui naming and layout 2025-10-30 10:39:27 +01:00
cde779ea00 fix(Atlantis): Add wildcards in allow origin 2025-10-29 18:04:21 +01:00
8106bb9380 Fix missing id in archive filter 2025-10-29 18:04:04 +01:00
df56303949 Add OpenFGA object list endpoints
Show what archives users can exec
2025-10-29 17:36:18 +01:00
79238c2a8f Add more admin and OpenFGA endpoints 2025-10-29 16:57:11 +01:00
Stig Rune Jensen
637d22e0f0 fix(Atlantis): add sanity check for sim names, closes #66 2025-10-29 15:21:26 +01:00
ac82e2c7ba Start on adding tilt and moving admin queries to UserPortal 2025-10-29 13:07:31 +01:00
5b429bd94f Merge branch 'mrtz/docs' into 'main'
docs: Update tilt documentation

See merge request oceanbox/Poseidon!102
2025-10-29 10:21:23 +01:00
754477b679 docs: Update tilt documentation 2025-10-29 10:20:30 +01:00
c9bed6623c Merge branch 'mrtz/nix-docs' into 'main'
docs(nix): Explain how and why

See merge request oceanbox/Poseidon!101
2025-10-28 17:50:24 +01:00
993e5680bb docs(nix): Explain how and why 2025-10-28 15:33:58 +01:00
cf8ec277cb Add Archives page
Here all archives are fetching at once. Added some simple pagination and
variant filters too.
2025-10-28 13:54:33 +01:00
Stig Rune Jensen
9b6a3ef133 fix(Atlantis): switch to chart-trend icon 2025-10-28 08:35:05 +01:00
semantic-release-bot
2476d9f83c chore(release): 1.27.0
# [1.27.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.26.4...v1.27.0) (2025-10-27)

### Bug Fixes

* **Atlantis:** fix buttons on sim setup ([c6eee07](c6eee07317))
* **Atlantis:** fix style in point samples section ([e85a114](e85a114a23))
* **Atlantis:** read sample radius from string ([0bd66f8](0bd66f89fd))
* **Mapster:** Do not disable timeline when not in OceanControls ([aaa5627](aaa56271b0))

### Features

* **Atlantis:** add alternative unit for downwelling, closes [#47](https://gitlab.com/oceanbox/Poseidon/issues/47) ([234cd19](234cd19af7))
* **Atlantis:** introduce average point values within circle ([a7cb34d](a7cb34d7ba))
* **Atlantis:** time series from point samples, closes [#29](https://gitlab.com/oceanbox/Poseidon/issues/29) ([1d1ddf2](1d1ddf2fef))
* **Hipster:** add slurm account and comment ([40414db](40414db1f6))
2025-10-27 16:10:27 +00:00
Stig Rune Jensen
e9c21c1292 Merge branch 'stigrj/downwelling' into 'main'
Feat: improved downwelling and point samling

Closes #29 and #47

See merge request oceanbox/Poseidon!100
2025-10-27 16:06:36 +00:00
Stig Rune Jensen
c6eee07317 fix(Atlantis): fix buttons on sim setup 2025-10-27 16:55:50 +01:00
aaa56271b0 fix(Mapster): Do not disable timeline when not in OceanControls 2025-10-27 16:55:50 +01:00
4280431d0f Add back nodejs to shell.nix
We run without bun in Atlantis/Tiltfile, and it doesn't seem to work
with bun, so continue using node for now.
2025-10-27 16:55:50 +01:00
Stig Rune Jensen
e85a114a23 fix(Atlantis): fix style in point samples section 2025-10-27 16:55:50 +01:00
Stig Rune Jensen
1d1ddf2fef feat(Atlantis): time series from point samples, closes #29 2025-10-27 16:55:50 +01:00
Stig Rune Jensen
0bd66f89fd fix(Atlantis): read sample radius from string 2025-10-27 16:55:50 +01:00
Stig Rune Jensen
e886589332 refactor: cleanup sorcerer drifters api 2025-10-27 16:55:50 +01:00
Stig Rune Jensen
a7cb34d7ba feat(Atlantis): introduce average point values within circle 2025-10-27 16:55:50 +01:00
Stig Rune Jensen
1edf1c2e55 refactor: switch from list to array types 2025-10-27 16:55:50 +01:00
Stig Rune Jensen
234cd19af7 feat(Atlantis): add alternative unit for downwelling, closes #47 2025-10-27 16:55:50 +01:00
Stig Rune Jensen
40414db1f6 feat(Hipster): add slurm account and comment 2025-10-27 16:55:50 +01:00
semantic-release-bot
f160e341ce chore(release): 1.26.4
## [1.26.4](https://gitlab.com/oceanbox/Poseidon/compare/v1.26.3...v1.26.4) (2025-10-27)

### Bug Fixes

* Create TMP directory for atlantis and add errorhandler ([0473f7b](0473f7b765))
2025-10-27 15:07:04 +00:00
5a4ef2bd70 chore: Bump npm deps hash 2025-10-27 16:02:58 +01:00
0473f7b765 fix: Create TMP directory for atlantis and add errorhandler 2025-10-27 15:58:32 +01:00
semantic-release-bot
7fb92d18c7 chore(release): 1.26.3
## [1.26.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.26.2...v1.26.3) (2025-10-27)

### Bug Fixes

* **Atlas:** Fix top nav logo offset ([c5b85e9](c5b85e9ad4))
* **Atlas:** Show loading when waiting for model area archives ([9181b43](9181b43732))
* **Mapster:** Add back stats download button ([86425a2](86425a2292))
* **Mapster:** Add timeseries help text on stats ([0dbd11e](0dbd11ed68))
* **Mapster:** Catch potential timeout error fetching time series ([214fb65](214fb650c1))
* **Mapster:** Choose better warn condition for missing post drift ([17181f6](17181f6ca2))
* **Mapster:** Fix plotly chart not auto ranging properly ([c862cb1](c862cb172f))
* **Mapster:** Handle grid search case for sub grids/models ([a046e7d](a046e7d849))
* **Mapster:** Handle probing errors and show toast ([db37040](db37040209))
* **Mapster:** Make loading clearer when loading ([215409c](215409c7c8))
* **Mapster:** Make notifier list an array ([3b4ec02](3b4ec021af))
* **Mapster:** More sane stat selection in depth plots ([fa63ade](fa63ade2e1))
* **Mapster:** Redirect to /signin if entering map unauthenticated ([7176919](7176919e94))
* **Mapster:** Remove Esc listener from side nav stats controls ([59ce5b9](59ce5b98b8))
* **Mapster:** Show warning when non-transport sims are missing post ([9bd0562](9bd0562b47))
* **Mapster:** Sidebar styling updates ([f7fbc5d](f7fbc5d70d))
* **Mapster:** Update field probing data extraction view ([9eed6af](9eed6af711))
* Use custom plotly bundle in react-plotly.js ([51fc750](51fc7503b5))
2025-10-27 14:49:12 +00:00
Simen Kirkvik
7054ade55d fix: Add missing * in Drifters ArchiveType FromString 2025-10-27 15:46:53 +01:00
Simen Kirkvik
0a54ae3dcc Add new pages and fetch more info 2025-10-27 15:46:53 +01:00
6016cda9ef Add error handling 2025-10-27 15:46:53 +01:00
Simen Kirkvik
4257ec5598 Initial User portal commit 2025-10-27 15:46:53 +01:00
009008e0d7 Merge branch 'simkir/stats' into 'main'
Stats refactoring

Closes #46 and #20

See merge request oceanbox/Poseidon!75
2025-10-27 15:44:42 +01:00
semantic-release-bot
554bb6b184 chore(release): 1.26.2
## [1.26.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.26.1...v1.26.2) (2025-10-27)

### Bug Fixes

* **tmp:** Add prints to debug ([b5c21bb](b5c21bb62c))
2025-10-27 14:13:48 +00:00
b5c21bb62c fix(tmp): Add prints to debug 2025-10-27 15:08:57 +01:00
3b0de79bd8 Update stats layer even if not shown 2025-10-27 14:50:43 +01:00
ed916c1362 Enable surface/bottom toggling of stat fields 2025-10-27 14:25:57 +01:00
semantic-release-bot
77c8b95af4 chore(release): 1.26.1
## [1.26.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.26.0...v1.26.1) (2025-10-27)

### Bug Fixes

* Don't use nodejs in dev ([90bab5e](90bab5ecc0)), closes [oceanbox/Poseidon#1](https://gitlab.com/oceanbox/Poseidon/issues/1)
* Typo and build archivist with netcdf runtimeDeps ([8482921](8482921420))
2025-10-27 12:47:53 +00:00
8482921420 fix: Typo and build archivist with netcdf runtimeDeps 2025-10-27 13:43:25 +01:00
d3d1851142 Make rose plot style more consistent with the others 2025-10-25 15:42:07 +02:00
7a93caecb1 Sort depth selection by surface first 2025-10-25 15:33:51 +02:00
7ebaaa419f Make toolbox not fixed for dynamic sizing
So that we do not need the hard-coded left padding on the main box to
the right of the left nav bar.
2025-10-25 15:18:45 +02:00
4850f9d4c3 Remove none case in field metric selection
Do some juggling to handle cases when entering probing when stats are
selected
2025-10-25 15:08:36 +02:00
7075cc50b9 Update stat fields when period updates 2025-10-25 14:43:13 +02:00
2a1761e5eb Disable timeline if viewing stat fields 2025-10-25 14:37:04 +02:00
c6c4eb57a9 Format Mapster.fs 2025-10-25 14:28:04 +02:00
6d92b9b929 Fix bug where changing prop type resets stat metrics
Though I want to reset theme between changing views. I.e.: Changing from
depth profile to time series and back.
2025-10-25 14:25:35 +02:00
90a5c83ca1 Add StatRemoveMetric msg to simplify field toggle
Since setting and removing will fetch props
2025-10-25 14:23:11 +02:00
aadb8d6b50 Add toggle for viewing stat fields 2025-10-25 14:22:09 +02:00
90bab5ecc0 fix: Don't use nodejs in dev
Closes oceanbox/Poseidon#1
2025-10-25 13:05:49 +02:00
0cf2b641cd Fix timeline depth selection resetting stat view 2025-10-24 16:46:02 +02:00
6183d235ac Add back stat field viewing into Oceanography page 2025-10-24 16:46:02 +02:00
79ae757e7d Convert more spectrum pickers into fluent select 2025-10-24 16:46:02 +02:00
4782cde597 Remove unused elmish model and component from stats controls 2025-10-24 16:46:02 +02:00
e934101db7 Make more html functions and hook components
Also try make all hook components use HMR tokens
2025-10-24 16:46:02 +02:00
dbea593b1c Rename Lib.React toReact -> fromJsx 2025-10-24 16:46:02 +02:00
8f0c414775 Remove unused packages and pages 2025-10-24 16:46:00 +02:00
17181f6ca2 fix(Mapster): Choose better warn condition for missing post drift
Together with some small refactorings.

As mentioned in the comment, waiting for the particle filter to be
fetched acts as a proxy for when enough information is gathered. Should
probably just have a flag for when available post drifts has been
fetched, like with the filter.
2025-10-24 16:45:23 +02:00
8792637774 Restructure info and color modules
- Add titles to the sidebar pages
- Move more essential controls out of accordions
- Add some more descriptions of options
2025-10-24 16:45:23 +02:00
506b76fc00 Turn map layer selection into picker 2025-10-24 16:45:23 +02:00
edfd519b8e Allow hiding instant trace in depth probing 2025-10-24 16:45:23 +02:00
5520ab8d30 devel: Make lit hot module reload work again
Stop react page reload after every change because it does not understand
lit. Maybe, idk. But it works. kinda
2025-10-24 16:45:23 +02:00
8f06be26ef Comments, style, and unused imports 2025-10-24 16:45:23 +02:00
a627d509ac Refactor contour plots data fetching and trace creation 2025-10-24 16:45:23 +02:00
1fa65def25 Update info layer aquaculture lookup styling 2025-10-24 16:45:23 +02:00
d805997470 Tweak atlas model hover popup styling 2025-10-24 16:45:23 +02:00
1e1e429c9a Bump vite react plugin 2025-10-24 16:45:22 +02:00
9eed6af711 fix(Mapster): Update field probing data extraction view
Decided not to move it to the side bar as it involves sharing plot data
with the rest of the app. Much easier to just extract it directly in the
plot, but move the button down into the dialog footer.
2025-10-24 16:45:22 +02:00
a046e7d849 fix(Mapster): Handle grid search case for sub grids/models 2025-10-24 16:45:22 +02:00
db37040209 fix(Mapster): Handle probing errors and show toast
Should do this for almost everything.
2025-10-24 16:45:22 +02:00
217fd54ec4 Remove extra Client lib fsproj entries
Idk...
2025-10-24 16:45:22 +02:00
17c8c12f55 Include fetching depths in probe point searching
So that - again - the sidebar isn't the one fetching data for the
top-level model.
2025-10-24 16:45:22 +02:00
3ff2a0f63c wip: start moving drifter plot extraction into side bar 2025-10-24 16:45:22 +02:00
edcf95f71e Move side effect out of InitParticles cmd 2025-10-24 16:45:22 +02:00
aafc0d55bf Remove margin from plotly heatmap
Looks really weird when the names are short...
2025-10-24 16:45:22 +02:00
215409c7c8 fix(Mapster): Make loading clearer when loading
Add Elmish cmd that does point grid node searching for you. It doesn't
make a whole lot of sense that it's this random place in the sidebar
that is doing the search for you. This will then set loading state,
making it obvious when probing is taking some time. Also, seperate some
setting of probing members into seperate messages, so we do not have
race condition -problems if you send the whole probing struct into an
async function.
2025-10-24 16:45:22 +02:00
59d5557bdd Format PlotBox.fs 2025-10-24 16:45:22 +02:00
22ea4b29b8 Remove unused dispatch as argument to plotbox component 2025-10-24 16:45:22 +02:00
59ce5b98b8 fix(Mapster): Remove Esc listener from side nav stats controls
This did give us the ability to close the stat plotbox dialog with
enter, but it's listening from a very strange place. Remove for clarity,
and I will put it somewhere more sensible.
2025-10-24 16:45:22 +02:00
51fc7503b5 fix: Use custom plotly bundle in react-plotly.js 2025-10-24 16:45:22 +02:00
143b55634e Handle PlotBox dialog close event 2025-10-24 16:45:22 +02:00
3ba59a39bf Add timings on Sorcerer opening netcdf files and timeseries 2025-10-24 16:45:22 +02:00
0dbd11ed68 fix(Mapster): Add timeseries help text on stats 2025-10-24 16:45:22 +02:00
f7fbc5d70d fix(Mapster): Sidebar styling updates
Basically remove all hard-coded sizes, and slap a flex box on it
instead. :)
2025-10-24 16:45:22 +02:00
7e3a520dad Fix plotly trace naming
With multiple traces present at the same time, it's nice to see which
standard deviations belongs to another. E.g.: when you have chosen two
different depths and have both deviations present in the graph.
2025-10-24 16:45:22 +02:00
9a26a258b6 Refactor colorPalette format function 2025-10-24 16:45:22 +02:00
94432247b3 Revert Sorcerer Period month indexing change
Month is stored as indexed at zero, but the user gives and gets the
month where Jan. starts at 1.
2025-10-24 16:45:22 +02:00
86425a2292 fix(Mapster): Add back stats download button 2025-10-24 16:45:22 +02:00
9bd0562b47 fix(Mapster): Show warning when non-transport sims are missing post 2025-10-24 16:45:22 +02:00
1a7777acd1 Reset stat metrics on switching probing tabs 2025-10-24 16:45:22 +02:00
214fb650c1 fix(Mapster): Catch potential timeout error fetching time series
Since they can take over a minute to download
2025-10-24 16:45:22 +02:00
e9b3c4adba Formatting and moving things around 2025-10-24 16:45:22 +02:00
fa63ade2e1 fix(Mapster): More sane stat selection in depth plots 2025-10-24 16:45:22 +02:00
eab1b42f76 Switch colormapAccordion to use FluentUI 2025-10-24 16:45:22 +02:00
c5b85e9ad4 fix(Atlas): Fix top nav logo offset 2025-10-24 16:45:04 +02:00
79daa83aca Some styling tweaks in postdrift sidebar 2025-10-24 16:45:04 +02:00
761946660c wip: Fix some rebase errors
Also try removing all side effects from elmish model. Seems this caused
some slow-down aswell, maybe. I thinks it is a good idea to keep
functions away from the update, and rather put them in elmish commands.
Not entierly sure how F# async startImmediate interacts in JS.
2025-10-24 16:45:04 +02:00
723064ae52 Add FromInt to Sorcerer Period type 2025-10-24 16:45:04 +02:00
7db499b1c7 Format Mapster Postdrift 2025-10-24 16:45:04 +02:00
c28664b9ee Add network matrix hover labels
Showing "To" and "From" the locations when hovering the matrix
2025-10-24 16:45:04 +02:00
f250e8bf16 Update plotly heatmap bindings 2025-10-24 16:45:04 +02:00
b6818cd7fa Refactor Fiskeridir url into function 2025-10-24 16:45:04 +02:00
3055b8d489 Update styling and do not always show all probing 2025-10-24 16:45:04 +02:00
819964ec87 Add range input and prop controls for line probing 2025-10-24 16:45:03 +02:00
0898faec4d Add back stats to time series
Hacky solution, so if we need more there at some point, I might redo it.
2025-10-24 16:44:20 +02:00
3b4ec021af fix(Mapster): Make notifier list an array 2025-10-24 16:44:20 +02:00
3ddd22edd1 Move things around
- Move all plotting related things into Plots folder
- Rename Probing -> ProbingControls

    As there were only UI elements there

- Use Probing.fs as a library for fetching archive stats and information

    Actual probing of the models
2025-10-24 16:44:20 +02:00
cd4431db1b Remove time from model
Calculate from archive and frame instead.
2025-10-24 16:44:20 +02:00
b7ba8eb771 Add back current rose plots 2025-10-24 16:44:20 +02:00
f74aacf53b Restructure to add back time series plots 2025-10-24 16:44:20 +02:00
cacff8d4df Allow for removing depth stat traces/lines 2025-10-24 16:44:20 +02:00
b028e54c52 Working stat retrieval when probing points
Finally figured out why rendering React was so weird. The
Lit.React.toLit function works in funny ways. It returns a function that
accepts the react component inputs. When we wrapped this function to
call our react components, they always unmounted and mounted every time
our lit components rerendered. Which is not good. By making the react
components only take 1 argument, and return simply the function toLit
returns, things seem to work nicely.

The depth plots has also been hoisted into an Elmish model. This hides
away some of the nasty state/book -keeping of different traces. Now
the data fetching and rendering works smoothlyish for adding and
removing traces willy nilly.

I think removing stat traces do not work atm. however. Then its on to
all the other charts and traces. Hurray. At least I now have a semi-good
way of doing things. And things don't feel broken, finally.
2025-10-24 16:44:20 +02:00
34774e57de wip: Lift depth plots into elmish model
Refactor some common functions into Util files. Elmish makes keeping
track of the plot states much easier. Ran into problems of stale
closures.

Since so many of the plots use the same functions, like fetching depths
from indices, I created Mapster/Plots/Utils.fs. I think more of the
plotting files can go there eventually.

The controls are coming together. You can add and remove the stat traces
alongside the "moment" depth trace you've chosen by probing.
2025-10-24 16:44:20 +02:00
9181b43732 fix(Atlas): Show loading when waiting for model area archives
When the modal pops up, that is.
2025-10-24 16:44:20 +02:00
1e12fa52aa wip: Try merging stat probing and normal probing
So, choose whatever stat you want to see together with the
moment/instance probing. This way you have all probing controls in the
same place, and you can pick and choose whatever you want to look at.

Currently only one prop can be viewed at a time, but can consider
finding some controls for viewing multiple at once.
2025-10-24 16:44:20 +02:00
7176919e94 fix(Mapster): Redirect to /signin if entering map unauthenticated
Which was mostly the app crashing since we were checking sessionStorage
for keys without testing if they were actually there.
2025-10-24 16:44:20 +02:00
5ee01e58d8 Format Client/Lib 2025-10-24 16:44:20 +02:00
ce495ebcb8 wip: Combine all probing controls
- Move all charts into same dialog
- Show one chart at a time with controls in sidebar to choose what to
  inspect

TODO:
- Proper controls for adding statistics views into selected probing

    I.e.: You've chosen to look at the temp at a point. You see by
    default the temp at each layer at that exact moment. Add so that you
    can choose to see the avg. for the month you're on, or any month,
    and add the error bars/region to the same graph you're already
    viewing.
2025-10-24 16:44:20 +02:00
b91a528108 wip: Move stats to Oceanography page
Unify and refactor. Move Stats probing state into top-level modal. The
sidebar and the stat modals interact. Also try making more intuitive
controls for probing in general. Letting you choose what you want to
probe, instead of the page decide this.

TODO: Make the stat modals not cover the sidebar, so that you can select
options while the modal is open. This also eliminates all the code
duplication with a sidebar inside the modal.
2025-10-24 16:44:20 +02:00
333ddd0ba3 wip: Continue experimenting with stats charts 2025-10-24 16:44:20 +02:00
291c077536 wip: Fix FluentUI calendar month select
Currently not used, though
2025-10-24 16:44:20 +02:00
362718dd9c wip: Start on stats measure
Trying some things with sampling data. Started with just plotting bottom
bathymetry, and a way of sampling along the measuring line. Hopefully
that will make it easy to pull down whatever stats after the fact.

Also, ruined the plotly import (sry @mrtz). Imported Plot through
react-plotly.js directly, which didn't work with the aliasing. Will try
to fix later.
2025-10-24 16:44:19 +02:00
7e12df8cc0 Format and refactor some things
Try to use arrays where possible. Pull state into elimish models
when possible.
2025-10-24 16:43:19 +02:00
c862cb172f fix(Mapster): Fix plotly chart not auto ranging properly 2025-10-24 16:43:19 +02:00
6cdafc22b4 Try looking at year selection in stats
It's currently not supported... Will need some reworking of how we index
files... Hmmm
2025-10-24 16:43:19 +02:00
750d1a0803 Add fsautocomplete to shell.nix 2025-10-24 16:43:19 +02:00
ff7ce98672 Start on Stats cleanup
Adding fluentui calendar, etc
2025-10-24 16:43:17 +02:00
semantic-release-bot
03eab47d23 chore(release): 1.26.0
# [1.26.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.25.5...v1.26.0) (2025-10-23)

### Features

* **fga:** Add start_time to ReadChanges ([9388d37](9388d3758d))
2025-10-23 12:09:05 +00:00
1b12faa61a Merge branch 'mrtz/openfga-date' into 'main'
feat(fga): Add start_time to ReadChanges

See merge request oceanbox/Poseidon!99
2025-10-23 14:05:03 +02:00
9388d3758d feat(fga): Add start_time to ReadChanges
This commit limits readchanges from reading things
older than 2 months, which should be enough context.
2025-10-23 13:56:44 +02:00
7f6ad2b853 devel(nix): Fix hash 2025-10-22 14:51:34 +02:00
semantic-release-bot
4467957b8f chore(release): 1.25.5
## [1.25.5](https://gitlab.com/oceanbox/Poseidon/compare/v1.25.4...v1.25.5) (2025-10-22)

### Bug Fixes

* **nix:** Build Archivist SIF in pipeline ([16f9686](16f968657b))
2025-10-22 12:44:12 +00:00
cafe1204ef Merge branch 'mrtz/nix-cleanup' into 'main'
fix(nix): Build Archivist SIF in pipeline

See merge request oceanbox/Poseidon!98
2025-10-22 14:40:18 +02:00
16f968657b fix(nix): Build Archivist SIF in pipeline 2025-10-22 14:29:21 +02:00
semantic-release-bot
8274bb982a chore(release): 1.25.4
## [1.25.4](https://gitlab.com/oceanbox/Poseidon/compare/v1.25.3...v1.25.4) (2025-10-15)

### Bug Fixes

* reduce number of cpus per drifter job ([b293044](b293044258))
2025-10-15 20:45:09 +00:00
b3da5b2a96 Merge branch 'stigrj/reduce-cpus' into 'main'
fix: reduce number of cpus per drifter job

See merge request oceanbox/Poseidon!96
2025-10-15 22:40:55 +02:00
Stig Rune Jensen
b293044258 fix: reduce number of cpus per drifter job 2025-10-15 22:05:05 +02:00
semantic-release-bot
4db102157f chore(release): 1.25.3
## [1.25.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.25.2...v1.25.3) (2025-10-15)

### Bug Fixes

* **Atlantis:** set limitations on simulation parameters ([407a278](407a278e28))
2025-10-15 19:26:19 +00:00
16a4ccbc6b Merge branch 'stigrj/sim-limitations' into 'main'
fix(Atlantis): set limitations on simulation parameters

See merge request oceanbox/Poseidon!95
2025-10-15 21:21:51 +02:00
Stig Rune Jensen
407a278e28 fix(Atlantis): set limitations on simulation parameters 2025-10-15 19:29:27 +02:00
semantic-release-bot
8134999600 chore(release): 1.25.2
## [1.25.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.25.1...v1.25.2) (2025-10-15)

### Bug Fixes

* Send Drifters to Inbox instantly and Track Plumes ([48b5da9](48b5da9da3))
* Submit as queued if there is a queue ([d64ee45](d64ee45d9e))
2025-10-15 15:11:13 +00:00
Stig Rune Jensen
f29aafe943 Merge branch 'mrtz/first-in-inbox' into 'main'
Send Drifters to Inbox instantly as queued and Track Plumes

See merge request oceanbox/Poseidon!94
2025-10-15 15:06:51 +00:00
d64ee45d9e fix: Submit as queued if there is a queue 2025-10-15 16:58:19 +02:00
48b5da9da3 fix: Send Drifters to Inbox instantly and Track Plumes 2025-10-15 14:37:18 +02:00
semantic-release-bot
4882fa51e1 chore(release): 1.25.1
## [1.25.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.25.0...v1.25.1) (2025-10-12)

### Bug Fixes

* Cleanup ([f721077](f72107751f))
* Release ([cb6265d](cb6265dcb1))
2025-10-12 16:41:38 +00:00
Moritz Jörg
f72107751f fix: Cleanup 2025-10-12 18:37:25 +02:00
Moritz Jörg
cb6265dcb1 fix: Release 2025-10-12 18:34:11 +02:00
Stig Rune Jensen
92ef67a75f devel: update nix hash 2025-10-10 16:39:57 +02:00
semantic-release-bot
2e2ae7a0be chore(release): 1.25.0
# [1.25.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.24.5...v1.25.0) (2025-10-10)

### Bug Fixes

* **Atlantis:** avoid positive release depth, closes [#42](https://gitlab.com/oceanbox/Poseidon/issues/42) ([16643c3](16643c3460))
* **Atlantis:** clear point samples on deselect ([8174354](81743540d0))

### Features

* **Atlantis:** add point samples to any sim type, closes [#54](https://gitlab.com/oceanbox/Poseidon/issues/54) ([9a41f0a](9a41f0a2f3))
* **Atlantis:** add toggle button for showing releases, closes [#53](https://gitlab.com/oceanbox/Poseidon/issues/53) ([f8045f6](f8045f6dcc))
* **Atlantis:** copy release sites, closes [#56](https://gitlab.com/oceanbox/Poseidon/issues/56) ([9bb9d85](9bb9d855e4))
* **Atlantis:** fetch time series data from plot, closes [#55](https://gitlab.com/oceanbox/Poseidon/issues/55) ([a332fe7](a332fe72a2))
2025-10-10 14:17:21 +00:00
209f668879 Merge branch 'stigrj/time-series-download' into 'main'
Feat: mini improvements

Closes #53, #54, #55, #56, and #42

See merge request oceanbox/Poseidon!93
2025-10-10 16:13:49 +02:00
semantic-release-bot
f3e1d98704 chore(release): 1.24.5
## [1.24.5](https://gitlab.com/oceanbox/Poseidon/compare/v1.24.4...v1.24.5) (2025-10-10)

### Bug Fixes

* **mapster:** Set static artifact for plume downloads ([5476583](5476583cb2))
2025-10-10 14:03:46 +00:00
Stig Rune Jensen
f8045f6dcc feat(Atlantis): add toggle button for showing releases, closes #53 2025-10-10 16:00:51 +02:00
Stig Rune Jensen
9a41f0a2f3 feat(Atlantis): add point samples to any sim type, closes #54 2025-10-10 16:00:51 +02:00
Stig Rune Jensen
81743540d0 fix(Atlantis): clear point samples on deselect 2025-10-10 16:00:51 +02:00
Stig Rune Jensen
a332fe72a2 feat(Atlantis): fetch time series data from plot, closes #55 2025-10-10 16:00:51 +02:00
Stig Rune Jensen
9bb9d855e4 feat(Atlantis): copy release sites, closes #56 2025-10-10 16:00:51 +02:00
Stig Rune Jensen
16643c3460 fix(Atlantis): avoid positive release depth, closes #42 2025-10-10 16:00:51 +02:00
Stig Rune Jensen
eacaa05236 Merge branch 'mrtz/plume-download' into 'main'
fix(mapster): Set static artifact for plume downloads

See merge request oceanbox/Poseidon!92
2025-10-10 14:00:05 +00:00
5476583cb2 fix(mapster): Set static artifact for plume downloads
Also removes some unused code inside mapster/plumes.fs and
now shows a green snackbar for `Simulation finished`.
2025-10-10 14:20:59 +02:00
semantic-release-bot
cb5f059661 chore(release): 1.24.4
## [1.24.4](https://gitlab.com/oceanbox/Poseidon/compare/v1.24.3...v1.24.4) (2025-10-03)

### Bug Fixes

* broken vite version ([5e84185](5e84185950))
* update drifters.api version ([22339c6](22339c6ade))
2025-10-03 15:37:47 +00:00
23498f5b04 Merge branch 'fix/update-drifters' into 'main'
Fix: update drifters

See merge request oceanbox/Poseidon!91
2025-10-03 17:33:48 +02:00
Stig Rune Jensen
5e84185950 fix: broken vite version 2025-10-03 17:17:38 +02:00
Stig Rune Jensen
22339c6ade fix: update drifters.api version 2025-10-03 11:04:09 +02:00
Stig Rune Jensen
690ce80957 devel: reintroduce linker fix in archivist.def 2025-10-03 11:02:37 +02:00
semantic-release-bot
d7b373630c chore(release): 1.24.3
## [1.24.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.24.2...v1.24.3) (2025-10-02)

### Bug Fixes

* Remove client from archivist cli and use correct path ([60a47b0](60a47b0308))
2025-10-02 14:27:50 +00:00
60a47b0308 fix: Remove client from archivist cli and use correct path 2025-10-02 16:23:50 +02:00
semantic-release-bot
bbb3ebc97c chore(release): 1.24.2
## [1.24.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.24.1...v1.24.2) (2025-10-02)

### Bug Fixes

* remove tz contours on deselect ([a5b348e](a5b348e439))
2025-10-02 13:39:15 +00:00
Stig Rune Jensen
a5b348e439 fix: remove tz contours on deselect 2025-10-02 15:34:57 +02:00
semantic-release-bot
aebcfb2e1e chore(release): 1.24.1
## [1.24.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.24.0...v1.24.1) (2025-10-02)

### Bug Fixes

* Update lockfiles ([904845f](904845fe0a))
2025-10-02 13:09:17 +00:00
904845fe0a fix: Update lockfiles 2025-10-02 15:05:18 +02:00
semantic-release-bot
94ccb3583d chore(release): 1.24.0
# [1.24.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.23.3...v1.24.0) (2025-10-02)

### Bug Fixes

* **Atlantis:** sync import of sp-picker ([6151eb8](6151eb86f0))
* Build Archivist with correct archmaester interface ([d182900](d182900b02))

### Features

* **Atlantis:** add multiple transition zone contours, closes [#34](https://gitlab.com/oceanbox/Poseidon/issues/34) ([c75d137](c75d13711a))
* **Atlantis:** first take on cage interaction matrix, closes [#33](https://gitlab.com/oceanbox/Poseidon/issues/33) ([9ea31be](9ea31be332))
* **Atlantis:** point values section to sedimentation fields, closes [#43](https://gitlab.com/oceanbox/Poseidon/issues/43) ([7fff70b](7fff70b7d5))
* **Atlantis:** separate groups in accumulate, closes [#44](https://gitlab.com/oceanbox/Poseidon/issues/44) ([bda39de](bda39dea78))
* **Sorcerer:** add api for field2d contours ([598fd5a](598fd5a7b0))
2025-10-02 12:16:24 +00:00
Stig Rune Jensen
4ecd386f47 Merge branch 'feat/acc-v2' into 'main'
Feat: accumulation v2

Closes #34, #43, #33, and #44

See merge request oceanbox/Poseidon!88
2025-10-02 12:13:04 +00:00
d182900b02 fix: Build Archivist with correct archmaester interface 2025-10-02 13:48:34 +02:00
Stig Rune Jensen
c6f2a04b07 devel: tweak archivist singularity build 2025-10-02 13:22:47 +02:00
Stig Rune Jensen
c75d13711a feat(Atlantis): add multiple transition zone contours, closes #34 2025-10-02 13:22:47 +02:00
Stig Rune Jensen
7fff70b7d5 feat(Atlantis): point values section to sedimentation fields, closes #43 2025-10-02 13:22:47 +02:00
Stig Rune Jensen
43d36ded42 devel: update fsi 2025-10-02 13:22:47 +02:00
Stig Rune Jensen
9ea31be332 feat(Atlantis): first take on cage interaction matrix, closes #33 2025-10-02 13:22:47 +02:00
Stig Rune Jensen
6151eb86f0 fix(Atlantis): sync import of sp-picker 2025-10-02 13:22:47 +02:00
Stig Rune Jensen
bda39dea78 feat(Atlantis): separate groups in accumulate, closes #44 2025-10-02 13:22:47 +02:00
Stig Rune Jensen
63294887b1 devel: update archivist singularity file 2025-10-02 13:22:47 +02:00
Stig Rune Jensen
598fd5a7b0 feat(Sorcerer): add api for field2d contours 2025-10-02 13:22:47 +02:00
Moritz Jörg
582d0377ab devel: Bump lockfile and add docs on how to generate secrets 2025-09-30 14:07:34 +02:00
704242e120 devel: Rekey secrets 2025-09-30 12:18:28 +02:00
5a3474a282 Merge branch 'mrtz/agenix' into 'main'
devel(nix): Don't load secrets in direnv

See merge request oceanbox/Poseidon!89
2025-09-29 21:31:19 +02:00
Moritz Jörg
3e984d7ee2 devel(nix): Don't load secrets in direnv
Secrets are no generated manually using a shell passthrough.
In the root of the repo run `nix-shell -A agenix-gen`,
and check with `echo $NETRC`
2025-09-29 21:27:27 +02:00
semantic-release-bot
e3a2d2a1fb chore(release): 1.23.3
## [1.23.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.23.2...v1.23.3) (2025-09-29)

### Bug Fixes

* **Atlantis:** Go back to how secrets were fetched ([ea335bc](ea335bc3fb))
2025-09-29 14:04:07 +00:00
ea335bc3fb fix(Atlantis): Go back to how secrets were fetched
Dapr _is_ waiting for our app to start, so it doesn't work waiting for
dapr to start.
2025-09-29 15:58:21 +02:00
semantic-release-bot
c0ac950062 chore(release): 1.23.2
## [1.23.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.23.1...v1.23.2) (2025-09-29)

### Bug Fixes

* **Atlantis:** Ensure dapr is healthy before running ([e344647](e34464782a))
* **Atlantis:** Use correct DI settings type in FgaActor ([be1f942](be1f942413))
2025-09-29 12:12:46 +00:00
e34464782a fix(Atlantis): Ensure dapr is healthy before running
This enabled us to use dapr for secrets and config management.
2025-09-29 14:08:10 +02:00
be1f942413 fix(Atlantis): Use correct DI settings type in FgaActor 2025-09-29 14:06:44 +02:00
semantic-release-bot
3749e2d87d chore(release): 1.23.1
## [1.23.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.23.0...v1.23.1) (2025-09-27)

### Bug Fixes

* **Atlantis:** Overwrite slurm token with value from secrets ([94c8acc](94c8accf45))
2025-09-27 18:22:24 +00:00
94c8accf45 fix(Atlantis): Overwrite slurm token with value from secrets 2025-09-27 20:18:13 +02:00
semantic-release-bot
07858a5c7e chore(release): 1.23.0
# [1.23.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.22.8...v1.23.0) (2025-09-27)

### Bug Fixes

* add slurm-access-token stub to tilt ([190b227](190b227d97))
* change slurm secret token key to SLURM_JWT ([b4c8de2](b4c8de2e09))

### Features

* move slurmrestd from basic auth to jwt ([0922a8f](0922a8fa08))
2025-09-27 15:30:56 +00:00
1a0ca268ab Merge branch 'slurm-jwt' into 'main'
Move slurmrestd from basic auth to jwt

See merge request oceanbox/Poseidon!87
2025-09-27 17:27:14 +02:00
fe21779363 Update package.lock files 2025-09-27 17:20:10 +02:00
b7a3d3a3ce Update ServerPack dapr 1.15 -> 1.16 2025-09-27 17:20:10 +02:00
60981b6edf Update DataAgent dapr 1.15 -> 1.16 2025-09-27 17:20:10 +02:00
845ea0cbc2 Update Petimeter dapr 1.15 -> 1.16 2025-09-27 17:20:07 +02:00
1742b99f19 Update Hipster dapr 1.15 -> 1.16 2025-09-27 17:20:03 +02:00
61140b656c Update Server.Common dapr 1.15 -> 1.16 2025-09-27 17:16:24 +02:00
a6ee8e24fd Update Archmaester dapr 1.15 -> 1.16 2025-09-27 17:10:13 +02:00
443bf8e9bd Properly set DOTNET_ROOT 2025-09-27 17:05:07 +02:00
a3d98f24b3 Update Sorcerer dapr 1.15 -> 1.16 2025-09-27 17:04:38 +02:00
143f0ca988 Update Atlantis dapr 1.15 -> 1.16 2025-09-27 17:03:03 +02:00
b4ef9d9c93 Add back analytics web id 2025-09-27 16:32:03 +02:00
4dc956c9e6 Move more items into new settings 2025-09-27 16:31:43 +02:00
c3cc338b20 Comments 2025-09-27 15:53:34 +02:00
79f02c18b7 Remove excessive printing of lists 2025-09-27 15:53:34 +02:00
9737cb154b Allow rbac to access slurm-access-token secret 2025-09-27 15:53:34 +02:00
4c6a93ae60 Refactor settings into common settings class
This is to try handling async requests better. So, trying to avoid a lot
of side effects in a global module. We also have some async dependencies
on dapper and external stores, so try wrapping settings in a class to
accomplish this. Inspired by the F# code conventions article[^1] on ms
learn.

[^1]: https://learn.microsoft.com/en-us/dotnet/fsharp/style-guide/conventions
2025-09-27 15:50:37 +02:00
a48e7f2f22 Merge branch 'main' of gitlab.com:oceanbox/Poseidon 2025-09-26 12:30:36 +02:00
190b227d97 fix: add slurm-access-token stub to tilt 2025-09-26 12:18:52 +02:00
b4c8de2e09 fix: change slurm secret token key to SLURM_JWT 2025-09-26 12:05:44 +02:00
0922a8fa08 feat: move slurmrestd from basic auth to jwt 2025-09-26 11:22:27 +02:00
semantic-release-bot
ce3d0321c2 chore(release): 1.22.8
## [1.22.8](https://gitlab.com/oceanbox/Poseidon/compare/v1.22.7...v1.22.8) (2025-09-22)

### Bug Fixes

* **dataagent:** Add privateassets and bump api to match version ([93c1661](93c166144e))
2025-09-22 09:01:20 +00:00
93c166144e fix(dataagent): Add privateassets and bump api to match version 2025-09-22 10:57:21 +02:00
semantic-release-bot
09f11c2e4d chore(release): 1.22.7
## [1.22.7](https://gitlab.com/oceanbox/Poseidon/compare/v1.22.6...v1.22.7) (2025-09-20)

### Bug Fixes

* **dataagent:** Actual bump in version ([b9b9720](b9b97205c2))
2025-09-20 14:14:11 +00:00
b9b97205c2 fix(dataagent): Actual bump in version 2025-09-20 16:10:10 +02:00
91e73b7904 chore(dataagent): Bump to 7.1.1 2025-09-19 18:17:19 +02:00
semantic-release-bot
6501f45238 chore(release): 1.22.6
## [1.22.6](https://gitlab.com/oceanbox/Poseidon/compare/v1.22.5...v1.22.6) (2025-09-19)

### Bug Fixes

* **dataagent:** Add back weird Targets ([0586f2f](0586f2f7d3))
2025-09-19 15:54:35 +00:00
7894dba21a Merge branch 'mrtz/dataagent-weirdness' into 'main'
fix(dataagent): Add back weird Targets

See merge request oceanbox/Poseidon!85
2025-09-19 17:50:58 +02:00
0586f2f7d3 fix(dataagent): Add back weird Targets 2025-09-19 17:39:33 +02:00
semantic-release-bot
352421d5ee chore(release): 1.22.5
## [1.22.5](https://gitlab.com/oceanbox/Poseidon/compare/v1.22.4...v1.22.5) (2025-09-19)

### Bug Fixes

* **mapster:** Add authorization for plume usage ([7b90371](7b90371f84))
2025-09-19 06:19:41 +00:00
Stig Rune Jensen
05f6e35d72 Merge branch 'mrtz/plume-auth' into 'main'
fix(mapster): Add authorization for plume usage

See merge request oceanbox/Poseidon!84
2025-09-19 06:16:22 +00:00
7b90371f84 fix(mapster): Add authorization for plume usage 2025-09-18 17:58:19 +02:00
semantic-release-bot
a3bb3285e1 chore(release): 1.22.4
## [1.22.4](https://gitlab.com/oceanbox/Poseidon/compare/v1.22.3...v1.22.4) (2025-09-16)

### Bug Fixes

* Add archivist binary to shell and allow dirs in add ([ebc12b9](ebc12b9ef9))
* **Atlantis:** Remove heuristic caching tags instead of cache control ([f6ce5d2](f6ce5d26c3))
* Don't validate files in parser ([29eeb17](29eeb17306))
2025-09-16 15:03:47 +00:00
1e76a29131 Merge branch 'fix/browser-caching' into 'main'
Remove heuristic caching headers instead of setting the cache control one

See merge request oceanbox/Poseidon!83
2025-09-16 17:00:05 +02:00
f6ce5d26c3 fix(Atlantis): Remove heuristic caching tags instead of cache control 2025-09-16 15:26:41 +02:00
295fb9c116 Merge branch 'mrtz/list-archivist' into 'main'
fix: Add archivist binary to shell and allow dirs in add

See merge request oceanbox/Poseidon!82
2025-09-16 09:17:25 +02:00
29eeb17306 fix: Don't validate files in parser 2025-09-16 09:16:12 +02:00
ebc12b9ef9 fix: Add archivist binary to shell and allow dirs in add 2025-09-15 17:02:06 +02:00
semantic-release-bot
7a4a9f4f05 chore(release): 1.22.3
## [1.22.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.22.2...v1.22.3) (2025-09-15)

### Bug Fixes

* Load agenix secrets in direnv ([b2d8dec](b2d8dec9a4))
2025-09-15 13:22:40 +00:00
d76fd50321 Merge branch 'mrtz/bump-sdslite' into 'main'
refactor(nix): Change nix deps handling and bump sdslite

See merge request oceanbox/Poseidon!65
2025-09-15 15:19:01 +02:00
5fb18877c4 refactor(nix): Change nix deps handling and bump sdslite
Uses dotnet tools from nixpkgs instead of nuget.

New deps handling produces a lot of lockfiles.

Added runtimeid to avoid hash missmatch
2025-09-15 15:01:44 +02:00
046dd4ac11 Merge branch 'mrtz/fix-agenix' into 'main'
fix: Load agenix secrets in direnv

See merge request oceanbox/Poseidon!81
2025-09-15 14:59:30 +02:00
semantic-release-bot
e162a8a320 chore(release): 1.22.2
## [1.22.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.22.1...v1.22.2) (2025-09-15)

### Bug Fixes

* **Umami:** Add some initial event tracking ([ace083a](ace083af9e))
2025-09-15 12:58:54 +00:00
cd5a397c3a Merge branch 'umami-submit-job-event' into 'main'
Add some initial event tracking

See merge request oceanbox/Poseidon!80
2025-09-15 14:55:26 +02:00
b2d8dec9a4 fix: Load agenix secrets in direnv
With this we avoid loading the `use nix` hook everytime
we enter the shell. Only one password entry needed :)
2025-09-15 14:11:26 +02:00
a4f1752e72 Update drifters submit umami tracking 2025-09-12 16:34:07 +02:00
ace083af9e fix(Umami): Add some initial event tracking
Mostly for drifters to test things
2025-09-12 16:04:14 +02:00
semantic-release-bot
7216ddd67b chore(release): 1.22.1
## [1.22.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.22.0...v1.22.1) (2025-09-12)

### Bug Fixes

* Typo rename URL -> WEB_ID ([2e260d1](2e260d1184))
2025-09-12 10:59:00 +00:00
2e260d1184 fix: Typo rename URL -> WEB_ID 2025-09-12 12:56:18 +02:00
semantic-release-bot
c222803ef1 chore(release): 1.22.0
# [1.22.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.21.11...v1.22.0) (2025-09-12)

### Features

* **Atlantis:** Add user id to umami payload as unique id ([5a6c725](5a6c7258a4))
2025-09-12 10:55:16 +00:00
05bdb97e1d Merge branch 'umami-unique-id' into 'main'
Add user id to umami payload as unique id

See merge request oceanbox/Poseidon!79
2025-09-12 12:52:09 +02:00
5a6c7258a4 feat(Atlantis): Add user id to umami payload as unique id
Also supply analytics id as env. var. for the atlantis server.
2025-09-12 12:42:40 +02:00
902a28932d ci: Don't build singularity images 2025-09-12 11:05:10 +02:00
semantic-release-bot
3519a0df5c chore(release): 1.21.11
## [1.21.11](https://gitlab.com/oceanbox/Poseidon/compare/v1.21.10...v1.21.11) (2025-09-11)

### Bug Fixes

* Change from plausible to umami ([c2a99ac](c2a99ac1ee))
2025-09-11 14:01:25 +00:00
c2a99ac1ee fix: Change from plausible to umami 2025-09-11 15:57:20 +02:00
semantic-release-bot
550e3f153b chore(release): 1.21.10
## [1.21.10](https://gitlab.com/oceanbox/Poseidon/compare/v1.21.9...v1.21.10) (2025-09-11)

### Bug Fixes

* **Atlantis:** Configure static files manually ([5402af7](5402af7f8f))
* **Atlantis:** Use correct barentswatch secret ([9cd9518](9cd9518a38))
2025-09-11 13:17:14 +00:00
0c3388cf08 Merge branch 'fix/static-file-caching' into 'main'
Configure static files manually

See merge request oceanbox/Poseidon!76
2025-09-11 15:13:52 +02:00
2a074357e0 Merge branch 'fix/barentswatch-token' into 'main'
Use correct barentswatch secret

See merge request oceanbox/Poseidon!74
2025-09-11 15:13:42 +02:00
5402af7f8f fix(Atlantis): Configure static files manually
It seems to not work setting static files in both `app_config` and using
`use_static`. `use_static` is most likely overwriting our settings. But
if we do not use it, we just have to remember to change the web root
path to be the same as the static files path.
2025-09-11 14:44:33 +02:00
9cd9518a38 fix(Atlantis): Use correct barentswatch secret
A little copy pasta slip up there :^) Also add some more logging.
Finally, add some common helper functions to the common project.
2025-09-09 13:27:59 +02:00
semantic-release-bot
704c310b83 chore(release): 1.21.9
## [1.21.9](https://gitlab.com/oceanbox/Poseidon/compare/v1.21.8...v1.21.9) (2025-09-09)

### Bug Fixes

* Add no-cache header to served content ([1247d4d](1247d4d092))
* **Slurm:** Catch decode errors in handleSlurmEvents ([ad91a3d](ad91a3dd23))
2025-09-09 08:20:43 +00:00
Jonas Juselius
12fe518235 Merge branch 'fix/slurm-event-catch-decode-error' into 'main'
Catch decode errors in handleSlurmEvents

See merge request oceanbox/Poseidon!71
2025-09-09 10:18:03 +02:00
Jonas Juselius
03733df79d Merge branch 'mrtz/no-cache' into 'main'
fix: Add no-cache header to served content

See merge request oceanbox/Poseidon!72
2025-09-09 10:17:25 +02:00
Moritz Jörg
1247d4d092 fix: Add no-cache header to served content
This should invalidate the browser cache for new releases.
2025-09-09 09:57:36 +02:00
ad91a3dd23 fix(Slurm): Catch decode errors in handleSlurmEvents
We do not want unacked rabbitmq messages just because a message was
badly formatted
2025-09-09 09:44:18 +02:00
semantic-release-bot
8ad5cdb3ee chore(release): 1.21.8
## [1.21.8](https://gitlab.com/oceanbox/Poseidon/compare/v1.21.7...v1.21.8) (2025-09-08)

### Bug Fixes

* Add closing bracket vite atlantis ([2c6a30a](2c6a30a4f6))
2025-09-08 12:57:27 +00:00
2c6a30a4f6 fix: Add closing bracket vite atlantis 2025-09-08 14:53:38 +02:00
semantic-release-bot
4a893d8a56 chore(release): 1.21.7
## [1.21.7](https://gitlab.com/oceanbox/Poseidon/compare/v1.21.6...v1.21.7) (2025-09-08)

### Bug Fixes

* Add hash to vite output artifacts ([8e72010](8e72010740))
2025-09-08 12:30:42 +00:00
9ba2c73e5c Merge branch 'mrtz/hash-js' into 'main'
fix: Add hash to vite output artifacts

See merge request oceanbox/Poseidon!70
2025-09-08 14:27:05 +02:00
Moritz Jörg
8e72010740 fix: Add hash to vite output artifacts 2025-09-08 12:49:14 +02:00
semantic-release-bot
6affdada5d chore(release): 1.21.6
## [1.21.6](https://gitlab.com/oceanbox/Poseidon/compare/v1.21.5...v1.21.6) (2025-09-05)

### Bug Fixes

* **Atlantis:** convert release span from sec to hr ([e7ae2ee](e7ae2ee38a))
* **Mapster:** Redirect to Atlas if no archive is chosen ([047d747](047d74723e))
* **Mapster:** Test session storage on loading map ([3a7df65](3a7df65f0a))
2025-09-05 13:56:57 +00:00
bf92311c11 Merge branch 'fix-empty-session-storage' into 'main'
Test session storage variables on entering Mapster

See merge request oceanbox/Poseidon!69
2025-09-05 15:53:41 +02:00
ac25329204 Merge branch 'fix/clone' into 'main'
fix(Atlantis): convert release span from sec to hr

See merge request oceanbox/Poseidon!68
2025-09-05 15:52:41 +02:00
047d74723e fix(Mapster): Redirect to Atlas if no archive is chosen
So sessionStorage["archive_id"] = nullOrUndefined
2025-09-05 14:25:16 +02:00
3a7df65f0a fix(Mapster): Test session storage on loading map
If your session has expired, your session storage is naturally also
gone. So do some testing to see whether the values are actually there on
the initial loading of the model.
2025-09-05 14:19:57 +02:00
Stig Rune Jensen
e7ae2ee38a fix(Atlantis): convert release span from sec to hr 2025-09-05 14:04:56 +02:00
semantic-release-bot
e73ac386a4 chore(release): 1.21.5
## [1.21.5](https://gitlab.com/oceanbox/Poseidon/compare/v1.21.4...v1.21.5) (2025-09-02)

### Bug Fixes

* Clean up package.json ([27df77b](27df77bfeb))
* Ignore buildkit builder ([6e31e77](6e31e77f03))
2025-09-02 17:28:46 +00:00
6e31e77f03 fix: Ignore buildkit builder 2025-09-02 19:24:58 +02:00
8c7334f121 Merge branch 'mrtz/test' into 'main'
fix: Clean up package.json

See merge request oceanbox/Poseidon!67
2025-09-02 19:15:21 +02:00
27df77bfeb fix: Clean up package.json 2025-09-02 19:14:16 +02:00
fd76b47789 chore: Remove generate GHA 2025-09-02 18:58:11 +02:00
semantic-release-bot
ce01aac316 chore(release): 1.21.4
## [1.21.4](https://gitlab.com/oceanbox/Poseidon/compare/v1.21.3...v1.21.4) (2025-09-02)

### Bug Fixes

* **Mapster:** Fix aquaculture locations not showing ([4806dd9](4806dd97de))
2025-09-02 14:53:06 +00:00
dae8ceacd6 Merge branch 'fix/aquaculture-locations' into 'main'
Fix aquaculture locations not showing

See merge request oceanbox/Poseidon!66
2025-09-02 16:50:05 +02:00
4806dd97de fix(Mapster): Fix aquaculture locations not showing
The projection the sites are returned in seems to have changed. So
quickly change them on receiving them.
2025-09-02 15:36:55 +02:00
d58deea636 Merge branch 'mrtz/fun' into 'main'
Remove generate bun.nix and move to lon for pinning

See merge request oceanbox/Poseidon!63
2025-08-28 14:48:13 +02:00
Moritz Jörg
e77895c2eb refactor: Test client build with IFD, without generation
Replaces the `bun2nix` bun.nix generated file with `mkDerivation` containing
the node modules. This should decrease the nix evaluation time and decrease
the bundle size.

Also switches to [lon](https://github.com/nikstur/lon) instead of npins
for pinning nixpkgs, since it uses the nix library directly instead of
shelling out to `nix-shell` or `nix-build`.
2025-08-28 14:31:48 +02:00
semantic-release-bot
12f215c6a0 chore(release): 1.21.3
## [1.21.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.21.2...v1.21.3) (2025-08-27)

### Bug Fixes

* Default to Debug and rename to env ([f441a15](f441a15f69))
2025-08-27 09:40:51 +00:00
f441a15f69 fix: Default to Debug and rename to env 2025-08-27 11:38:41 +02:00
semantic-release-bot
8827130729 chore(release): 1.21.2
## [1.21.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.21.1...v1.21.2) (2025-08-27)

### Bug Fixes

* Add Error propagation and diagnosticlogging ([b2cce31](b2cce311b5))
2025-08-27 09:32:03 +00:00
b2cce311b5 fix: Add Error propagation and diagnosticlogging 2025-08-27 11:29:42 +02:00
semantic-release-bot
a3212fa80f chore(release): 1.21.1
## [1.21.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.21.0...v1.21.1) (2025-08-27)

### Bug Fixes

* 4GB maxRequestBodySize in prod and update fsi file ([cee62b1](cee62b15e6))
2025-08-27 08:22:58 +00:00
cee62b15e6 fix: 4GB maxRequestBodySize in prod and update fsi file 2025-08-27 10:20:47 +02:00
semantic-release-bot
ba481964e4 chore(release): 1.21.0
# [1.21.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.20.2...v1.21.0) (2025-08-27)
2025-08-27 08:19:05 +00:00
cd72635051 Merge branch 'mrtz/kestrelbody' into 'main'
minor: Configure MaxReqeustBodySize in Kestrel

See merge request oceanbox/Poseidon!64
2025-08-27 10:17:02 +02:00
7205dfe7ca minor: Configure MaxReqeustBodySize in Kestrel 2025-08-27 09:59:09 +02:00
Moritz Jörg
8e05f95ecc fun: Faster builds without IFD 2025-08-22 20:02:42 +02:00
semantic-release-bot
4596fd750f chore(release): 1.20.2
## [1.20.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.20.1...v1.20.2) (2025-08-22)

### Bug Fixes

* **Mapster:** Fix ArchiveDialog table squeeze ([90b17a5](90b17a59a2))
* **Mapster:** Fix InboxDialog table squeeze ([124a900](124a900b44))
2025-08-22 15:28:18 +00:00
Stig Rune Jensen
55c798d7f7 Merge branch 'simkir/table-fixes' into 'main'
Fix dialog table squeezes

See merge request oceanbox/Poseidon!62
2025-08-22 15:26:14 +00:00
124a900b44 fix(Mapster): Fix InboxDialog table squeeze 2025-08-22 17:09:14 +02:00
90b17a59a2 fix(Mapster): Fix ArchiveDialog table squeeze
Spectrum table has gotten new slots it seems?
2025-08-22 17:04:24 +02:00
015f24669e Add fsharp_multi_line_lambda_closing_newline to .editorconfig 2025-08-22 16:54:41 +02:00
semantic-release-bot
e439057c5b chore(release): 1.20.1
## [1.20.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.20.0...v1.20.1) (2025-08-22)

### Bug Fixes

* Downgrade vite plugin for correct js bundle ([d35f666](d35f666664))
2025-08-22 13:24:17 +00:00
db21ab46d4 Merge branch 'mrtz/fix-js' into 'main'
fix: Downgrade vite plugin for correct js bundle

See merge request oceanbox/Poseidon!61
2025-08-22 15:22:15 +02:00
d35f666664 fix: Downgrade vite plugin for correct js bundle 2025-08-22 15:01:28 +02:00
semantic-release-bot
957d9e72a4 chore(release): 1.20.0
# [1.20.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.19.4...v1.20.0) (2025-08-22)

### Features

* add custom depth separator in conc analysis ([62b09de](62b09deb8d))
2025-08-22 11:38:45 +00:00
d7dff3dbc3 Merge branch 'feat/depth-selector' into 'main'
feat: add custom depth separator in conc analysis

See merge request oceanbox/Poseidon!60
2025-08-22 13:36:44 +02:00
Stig Rune Jensen
62b09deb8d feat: add custom depth separator in conc analysis 2025-08-22 13:17:25 +02:00
semantic-release-bot
9db2ec0d64 chore(release): 1.19.4
## [1.19.4](https://gitlab.com/oceanbox/Poseidon/compare/v1.19.3...v1.19.4) (2025-08-21)

### Bug Fixes

* Don't run linter ([1780867](1780867a89))
* **nix:** Format, lint and remove dead code ([7d378a5](7d378a52d1))
2025-08-21 12:54:12 +00:00
3bd402937f Merge branch 'mrtz/ci-maybe' into 'main'
Add check for branch commits, format and lint nix

See merge request oceanbox/Poseidon!59
2025-08-21 14:50:09 +02:00
1780867a89 fix: Don't run linter 2025-08-21 14:32:09 +02:00
7d378a52d1 fix(nix): Format, lint and remove dead code 2025-08-21 14:22:35 +02:00
5ecb1b0945 chore: Add GHA 2025-08-21 13:48:08 +02:00
semantic-release-bot
62b1431ed8 chore(release): 1.19.3
## [1.19.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.19.2...v1.19.3) (2025-08-21)

### Bug Fixes

* Add fable temp files to gitignore ([8d9843d](8d9843d467))
2025-08-21 11:15:18 +00:00
e48555d88e Merge branch 'mrtz/fix-container' into 'main'
fix: Add fable temp files to gitignore

See merge request oceanbox/Poseidon!58
2025-08-21 13:13:57 +02:00
8d9843d467 fix: Add fable temp files to gitignore 2025-08-21 12:25:17 +02:00
semantic-release-bot
0e6dbda05f chore(release): 1.19.2
## [1.19.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.19.1...v1.19.2) (2025-08-20)

### Bug Fixes

* Add libnetcdf as runtime dependency for containers ([6e7fa4b](6e7fa4b9c7))
* Add memorysize ([f626796](f6267962b3))
* Include nix files for CI ([7983d94](7983d948f1))
2025-08-20 08:43:20 +00:00
e89237e493 Merge branch 'mrtz/ci-fix' into 'main'
fix: Add memorysize

See merge request oceanbox/Poseidon!57
2025-08-20 10:42:00 +02:00
f6267962b3 fix: Add memorysize 2025-08-20 10:15:16 +02:00
7983d948f1 fix: Include nix files for CI 2025-08-20 10:12:01 +02:00
5df2fba6b7 Merge branch 'mrtz/netcdf' into 'main'
fix: Add libnetcdf as runtime dependency for containers

See merge request oceanbox/Poseidon!56
2025-08-20 10:07:18 +02:00
6e7fa4b9c7 fix: Add libnetcdf as runtime dependency for containers 2025-08-20 10:04:14 +02:00
semantic-release-bot
396699f1f1 chore(release): 1.19.1
## [1.19.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.19.0...v1.19.1) (2025-08-19)

### Bug Fixes

* **Atlas:** Fix squashed archive table in modal ([3593a44](3593a4494d))
2025-08-19 19:01:23 +00:00
Jonas Juselius
9f776e5e01 Merge branch 'fix-atlas-modal' into 'main'
Fix squashed archive table in modal

See merge request oceanbox/Poseidon!54
2025-08-19 21:00:01 +02:00
semantic-release-bot
2c0b264ebd chore(release): 1.19.0
# [1.19.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.18.3...v1.19.0) (2025-08-19)

### Features

* Add OpenLayer FullScreen Controls ([2a3bb68](2a3bb68e3e))
2025-08-19 16:24:02 +00:00
d230509984 Merge branch 'mrtz/fullscreen' into 'main'
feat: Add OpenLayer FullScreen Controls

See merge request oceanbox/Poseidon!55
2025-08-19 18:22:33 +02:00
2a3bb68e3e feat: Add OpenLayer FullScreen Controls
Allows users to fullscreen the map both in mapsters
and atlas.
2025-08-19 17:50:08 +02:00
3593a4494d fix(Atlas): Fix squashed archive table in modal 2025-08-19 14:11:16 +02:00
semantic-release-bot
1b55c435a9 chore(release): 1.18.3
## [1.18.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.18.2...v1.18.3) (2025-08-18)

### Bug Fixes

* Add .env to gitignore and generate server docs ([a37e602](a37e602131))
2025-08-18 12:02:56 +00:00
e184e997c0 Merge branch 'mrtz/ci-2' into 'main'
fix: Add .env to gitignore & fix nuget build

See merge request oceanbox/Poseidon!53
2025-08-18 14:01:37 +02:00
a37e602131 fix: Add .env to gitignore and generate server docs 2025-08-18 13:54:21 +02:00
semantic-release-bot
cacc02d94f chore(release): 1.18.2
## [1.18.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.18.1...v1.18.2) (2025-08-18)

### Bug Fixes

* **nix:** Add `debug` as argstr and match version with fsproj's ([82c43bb](82c43bb5f0))
2025-08-18 09:33:02 +00:00
60c9d32114 Merge branch 'mrtz/ci' into 'main'
Build exposed packages via gitlab-ci

See merge request oceanbox/Poseidon!52
2025-08-18 11:31:38 +02:00
Moritz Jörg
82c43bb5f0 fix(nix): Add debug as argstr and match version with fsproj's 2025-08-18 11:10:27 +02:00
c9a846b5fc chore: Inherit deps from top-level baseShell
Remove busybox and shell from containers
2025-08-17 14:37:49 +02:00
0a104a5195 refactor: Only build exposed packages via gitlab-ci 2025-08-17 14:37:49 +02:00
371a628907 devel: fix NETRC check in shell.nix 2025-08-17 09:14:28 +02:00
semantic-release-bot
4a791f8473 chore(release): 1.18.1
## [1.18.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.18.0...v1.18.1) (2025-08-14)

### Bug Fixes

* Encrypt netrc with agenix and add to git ([4490ddf](4490ddf47c))
* Encrypt netrc with agenix and add to git ([68ef2f7](68ef2f7b72))
* Encrypt netrc with agenix and add to git ([270e5c0](270e5c02fc))
* fix loading of ssh keys ([842df2f](842df2fdb6))
2025-08-14 08:32:26 +00:00
49c8eb6280 Merge branch 'mrtz/oxc' into 'main'
chore: Switch from vite to rolldown-vite

See merge request oceanbox/Poseidon!51
2025-08-14 10:25:48 +02:00
1d454a0413 chore: Switch from vite to rolldown-vite
Improves our build performance, down to 1.4s and
`oxc` also decreases our bundlesize to under 3MB.

It exposes the same API so changes are minimal.
2025-08-14 09:53:27 +02:00
Moritz Jörg
ff11bd34b0 Merge pull request #2 from oceanbox-io/mrtz/secrets
fix: Encrypt netrc with agenix and add to git
2025-08-13 17:18:35 +02:00
05fec47e1d Merge branch 'mrtz/secrets' into 'main'
fix: Encrypt netrc with agenix and add to git

See merge request oceanbox/Poseidon!50
2025-08-13 14:35:08 +02:00
bb0f2bac3a docs: How to run agenix for secrets 2025-08-13 14:34:18 +02:00
842df2fdb6 fix: fix loading of ssh keys 2025-08-13 13:24:55 +02:00
e25d5ffb5c Merge branch 'mrtz/secrets' of gitlab.com:oceanbox/Poseidon into mrtz/secrets 2025-08-13 12:50:45 +02:00
4490ddf47c fix: Encrypt netrc with agenix and add to git
The `agenix-gen` shellHook is triggered once you
enter the repo.

Please add your ssh-key to the secrets.nix file and
run `agenix -r` to rekey the secrets.
2025-08-13 12:49:48 +02:00
2cdee9deb9 Merge remote-tracking branch 'refs/remotes/origin/mrtz/secrets' into mrtz/secrets 2025-08-13 12:41:26 +02:00
68ef2f7b72 fix: Encrypt netrc with agenix and add to git
The `agenix-gen` shellHook is triggered once you
enter the repo.

Please add your ssh-key to the secrets.nix file and
run `agenix -r` to rekey the secrets.
2025-08-13 12:37:21 +02:00
270e5c02fc fix: Encrypt netrc with agenix and add to git
The `agenix-gen` shellHook is triggered once you
enter the repo.

Please add your ssh-key to the secrets.nix file and
run `agenix -r` to rekey the secrets.
2025-08-13 11:25:20 +02:00
semantic-release-bot
95bcca6626 chore(release): 1.18.0
# [1.18.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.17.1...v1.18.0) (2025-08-12)

### Bug Fixes

* Cleanup ([786165c](786165c845))
* Drop netrc and add nupkg build ([d27cf41](d27cf41487))
* Revert tilt instances and add additional docs ([0402785](0402785fc7))

### Features

* Build with Nix ([ebe6b70](ebe6b70883))
* migrate to nix-actions and modernize build infrastructure ([27e54a7](27e54a7e1d))
2025-08-12 08:27:24 +00:00
202ec86739 Merge branch 'mrtz/nix' into 'main'
Build with Nix

See merge request oceanbox/Poseidon!47
2025-08-12 10:20:36 +02:00
7aab840067 Merge branch 'main' into 'mrtz/nix'
# Conflicts:
#   .envrc
2025-08-12 10:13:38 +02:00
Moritz Jörg
0402785fc7 fix: Revert tilt instances and add additional docs 2025-08-12 09:54:16 +02:00
Moritz Jörg
27e54a7e1d feat: migrate to nix-actions and modernize build infrastructure
This commit series completes a major infrastructure overhaul:

- Migrate from manual GitHub Actions YAML to nix-actions workflow generation
- Add automated dependency update workflow with scheduled runs
- Add comprehensive Nix package definitions for all components (atlantis, sorcerer, archivist, etc.)
- Create containerized builds with proper Docker support
- Wrap scripts inside nix for better dependency management and shellcheck

The build system now uses pure Nix expressions for both local development
and CI/CD, providing better reproducibility and maintainability.
2025-08-12 09:49:22 +02:00
9b69b91199 ci: Create matrix for parallel runs 2025-08-08 15:25:34 +02:00
786165c845 fix: Cleanup 2025-08-08 15:01:59 +02:00
d27cf41487 fix: Drop netrc and add nupkg build 2025-08-08 13:17:15 +02:00
semantic-release-bot
7ac255d870 chore(release): 1.17.1
## [1.17.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.17.0...v1.17.1) (2025-08-07)

### Bug Fixes

* **Sorcerer:** remove duplicate release sites in transition zone ([33c28cc](33c28ccb76))
2025-08-07 10:09:23 +00:00
58593aabae Merge branch 'fix/transition-zone' into 'main'
fix(Sorcerer): remove duplicate release sites in transition zone

See merge request oceanbox/Poseidon!49
2025-08-07 12:04:00 +02:00
Stig Rune Jensen
33c28ccb76 fix(Sorcerer): remove duplicate release sites in transition zone 2025-08-07 10:42:10 +02:00
semantic-release-bot
7517226194 chore(release): 1.17.0
# [1.17.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.16.4...v1.17.0) (2025-08-06)

### Bug Fixes

* **Atlantis:** send correct aid for aze contour download ([43ff8c5](43ff8c52e4))

### Features

* Rewrite Archivist CLI with Fargo ([fa063a9](fa063a9508))
2025-08-06 14:55:19 +00:00
9fdc22f99f Merge branch 'fix/aze-download' into 'main'
Fix: aze contour download

See merge request oceanbox/Poseidon!48
2025-08-06 16:39:29 +02:00
Stig Rune Jensen
c1a147363a dev: add missing quote in .envrc 2025-08-06 16:26:14 +02:00
Stig Rune Jensen
43ff8c52e4 fix(Atlantis): send correct aid for aze contour download 2025-08-06 16:25:11 +02:00
b2cbc4de09 chore: Add secrets (don't look) 2025-08-04 11:02:34 +02:00
ebe6b70883 feat: Build with Nix 2025-08-04 10:53:50 +02:00
89e750d9ea Merge branch 'mrtz/cli-cleanup' into 'main'
chore: formatting and ordering of imports

See merge request oceanbox/Poseidon!45
2025-07-31 09:40:46 +02:00
Moritz Jörg
b5873e254b chore: formatting and ordering of imports 2025-07-31 09:40:05 +02:00
Jonas Juselius
0e8c89a732 Merge branch 'mrtz/fargo-archivist' into 'main'
Convert Archivist to Fargo

See merge request oceanbox/Poseidon!44
2025-07-31 07:35:42 +02:00
fa063a9508 feat: Rewrite Archivist CLI with Fargo
Changes the AssemblyName to `archivist`
2025-07-30 14:38:48 +02:00
e66430cb32 wip: Start work on Fargo port of Archivist 2025-07-29 15:50:24 +02:00
e9a73e5da8 Merge branch 'revert-eedcd333' into 'main'
Revert "wip: Start work on Fargo port of Archivist"

See merge request oceanbox/Poseidon!43
2025-07-10 17:39:01 +02:00
9dbdea3f72 Revert "wip: Start work on Fargo port of Archivist"
This reverts commit eedcd333d0
2025-07-10 17:38:35 +02:00
eedcd333d0 wip: Start work on Fargo port of Archivist 2025-07-10 16:50:56 +02:00
semantic-release-bot
57d00a75bf chore(release): 1.16.4
## [1.16.4](https://gitlab.com/oceanbox/Poseidon/compare/v1.16.3...v1.16.4) (2025-07-09)

### Bug Fixes

* Don't restore ([cb9d678](cb9d678bce))
* Remove compiler check ([18c5399](18c5399625))
2025-07-09 16:05:15 +00:00
98f70d2774 Merge branch 'mrtz/fix-actors' into 'main'
fix: Inbox actor

See merge request oceanbox/Poseidon!42
2025-07-09 17:52:31 +02:00
cb9d678bce fix: Don't restore 2025-07-09 17:44:42 +02:00
18c5399625 fix: Remove compiler check 2025-07-09 17:25:58 +02:00
semantic-release-bot
c4bc3d6509 chore(release): 1.16.3
## [1.16.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.16.2...v1.16.3) (2025-07-09)

### Bug Fixes

* Check for fable compiler in Dapr.Actor interfaces ([aa6c01c](aa6c01c5f2))
* Plume cleanup ([0e080a7](0e080a70e1))
* Use Ordinal StringComparison for fgaToken ([dbef031](dbef03187c))
2025-07-09 13:28:59 +00:00
7ce6827add Merge branch 'mrtz/if-check' into 'main'
fix: Check for fable compiler in Dapr.Actor interfaces

See merge request oceanbox/Poseidon!41
2025-07-09 15:24:03 +02:00
aa6c01c5f2 fix: Check for fable compiler in Dapr.Actor interfaces 2025-07-09 15:18:46 +02:00
f36d11884c Merge branch 'mrtz/plumes++' into 'main'
fix: Plume cleanup

See merge request oceanbox/Poseidon!40
2025-07-09 15:01:43 +02:00
dbef03187c fix: Use Ordinal StringComparison for fgaToken 2025-07-09 14:36:30 +02:00
0e080a70e1 fix: Plume cleanup 2025-07-09 10:55:14 +02:00
semantic-release-bot
100c1d9e96 chore(release): 1.16.2
## [1.16.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.16.1...v1.16.2) (2025-07-08)

### Bug Fixes

* Don't restore before Release build ([c4134f8](c4134f8b27))
2025-07-08 17:52:08 +00:00
c4134f8b27 fix: Don't restore before Release build 2025-07-08 19:40:01 +02:00
semantic-release-bot
72c4e824a3 chore(release): 1.16.1
## [1.16.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.16.0...v1.16.1) (2025-07-08)

### Bug Fixes

* Make inboxTypeFromString case insensitive ([fe3370f](fe3370f50f))
* Remove unused code and bump slurmrestd ([a6c2c94](a6c2c949c2))
2025-07-08 12:06:53 +00:00
645531287f Merge branch 'mrtz/sim-fix' into 'main'
fix: Remove unused code and bump slurmrestd

See merge request oceanbox/Poseidon!39
2025-07-08 13:59:48 +02:00
fe3370f50f fix: Make inboxTypeFromString case insensitive 2025-07-08 13:50:21 +02:00
a6c2c949c2 fix: Remove unused code and bump slurmrestd
Server now defines an empty constant to avoid failing after the client
is ran.
2025-07-08 12:57:39 +02:00
semantic-release-bot
e2313efc45 chore(release): 1.16.0
# [1.16.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.15.3...v1.16.0) (2025-07-07)

### Bug Fixes

* Add working parts of plume ([0e256e6](0e256e6218))
2025-07-07 17:48:14 +00:00
f12e90355d Merge branch 'mrtz/inbox-new' into 'main'
fix: Add working parts of plume

See merge request oceanbox/Poseidon!38
2025-07-07 19:41:07 +02:00
0e256e6218 fix: Add working parts of plume 2025-07-07 18:49:32 +02:00
Jonas Juselius
56c01cc148 Merge branch 'mrtz/inbox' into 'main'
minor: Async Plume UI

See merge request oceanbox/Poseidon!37
2025-07-07 17:41:48 +02:00
d41c664b64 minor: Async Plume UI 2025-07-07 16:22:36 +02:00
semantic-release-bot
c7af875f8e chore(release): 1.15.3
## [1.15.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.15.2...v1.15.3) (2025-07-03)

### Bug Fixes

* **atlantis:** disable probing on no stats ([a464a5a](a464a5ac06))
* **plume:** adjust plume stop when changing start or duration ([13ac803](13ac803269))
* **plume:** allow cancel plume when running ([a400bf9](a400bf9e0b))
* **plume:** notify on fail ([e69897f](e69897fc94))
2025-07-03 17:43:50 +00:00
a1773ca99d Merge branch 'fix/cancel-plume' into 'main'
Fix: plume duration

See merge request oceanbox/Poseidon!36
2025-07-03 19:36:41 +02:00
Stig Rune Jensen
13ac803269 fix(plume): adjust plume stop when changing start or duration 2025-07-03 15:17:39 +02:00
Stig Rune Jensen
a464a5ac06 fix(atlantis): disable probing on no stats 2025-07-03 12:56:11 +02:00
Stig Rune Jensen
e69897fc94 fix(plume): notify on fail 2025-07-03 12:56:11 +02:00
Stig Rune Jensen
a400bf9e0b fix(plume): allow cancel plume when running 2025-07-03 12:56:06 +02:00
semantic-release-bot
0345568e6b chore(release): 1.15.2
## [1.15.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.15.1...v1.15.2) (2025-07-03)

### Bug Fixes

* misc wonk to fix pipeline build ([fd78702](fd78702ab4))
2025-07-03 08:19:50 +00:00
f67b0602ed Merge branch 'main' of gitlab.com:oceanbox/Poseidon 2025-07-03 10:11:20 +02:00
fd78702ab4 fix: misc wonk to fix pipeline build 2025-07-03 10:11:10 +02:00
semantic-release-bot
adb7853b63 chore(release): 1.15.1
## [1.15.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.15.0...v1.15.1) (2025-07-02)

### Bug Fixes

* Disable spinner after job is completed ([558c482](558c4829d9))
2025-07-02 19:07:17 +00:00
b125be1a1c Merge branch 'mrtz/fix-plume-spinner' into 'main'
fix: Disable spinner after job is completed

See merge request oceanbox/Poseidon!35
2025-07-02 21:01:30 +02:00
558c4829d9 fix: Disable spinner after job is completed 2025-07-02 20:55:14 +02:00
semantic-release-bot
0cc43ce6d6 chore(release): 1.15.0
# [1.15.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.14.5...v1.15.0) (2025-07-02)

### Bug Fixes

* add missing fetch fields button for downwelling ([44f12a8](44f12a825d))
* **atlantis:** show color bar on stats properties ([3046ec5](3046ec563d))

### Features

* Add Initial Plume UI ([1c14274](1c14274cdb))
* add traits input for plume ([d603453](d60345339b))
* **plume:** add download button for plume result ([1cbd34c](1cbd34c102))
2025-07-02 18:20:25 +00:00
Stig Rune Jensen
6815e46775 Merge branch 'mrtz/plumes' into 'main'
feat: Add Plume UI

See merge request oceanbox/Poseidon!31
2025-07-02 18:13:09 +00:00
Stig Rune Jensen
1cbd34c102 feat(plume): add download button for plume result 2025-07-02 20:05:20 +02:00
Stig Rune Jensen
d60345339b feat: add traits input for plume 2025-07-02 14:00:18 +02:00
semantic-release-bot
c482f51829 chore(release): 1.14.5
## [1.14.5](https://gitlab.com/oceanbox/Poseidon/compare/v1.14.4...v1.14.5) (2025-07-01)

### Bug Fixes

* add missing fetch fields button for downwelling ([2f39571](2f395712d5))
2025-07-02 14:00:18 +02:00
Stig Rune Jensen
44f12a825d fix: add missing fetch fields button for downwelling 2025-07-02 14:00:18 +02:00
semantic-release-bot
716f7527d0 chore(release): 1.14.4
## [1.14.4](https://gitlab.com/oceanbox/Poseidon/compare/v1.14.3...v1.14.4) (2025-07-01)

### Bug Fixes

* **atlantis:** show color bar on stats properties ([59ac2aa](59ac2aa3c9))
2025-07-02 14:00:18 +02:00
Stig Rune Jensen
3046ec563d fix(atlantis): show color bar on stats properties 2025-07-02 14:00:18 +02:00
semantic-release-bot
a582dba95b chore(release): 1.14.3
## [1.14.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.14.2...v1.14.3) (2025-06-30)

### Bug Fixes

* **sorcerer:** Only run sorcerer on c-x nodes ([41d792d](41d792dc2a))
2025-07-02 14:00:18 +02:00
4c615ddad9 wip: Initial UI -> Api setup 2025-07-02 11:35:59 +02:00
semantic-release-bot
7fa65dae37 chore(release): 1.14.5
## [1.14.5](https://gitlab.com/oceanbox/Poseidon/compare/v1.14.4...v1.14.5) (2025-07-01)

### Bug Fixes

* add missing fetch fields button for downwelling ([2f39571](2f395712d5))
2025-07-01 12:46:47 +00:00
2d39ecfedf Merge branch 'fix/misc' into 'main'
fix: add missing fetch fields button for downwelling

See merge request oceanbox/Poseidon!34
2025-07-01 14:41:35 +02:00
Stig Rune Jensen
2f395712d5 fix: add missing fetch fields button for downwelling 2025-07-01 14:14:04 +02:00
semantic-release-bot
b5531dd527 chore(release): 1.14.4
## [1.14.4](https://gitlab.com/oceanbox/Poseidon/compare/v1.14.3...v1.14.4) (2025-07-01)

### Bug Fixes

* **atlantis:** show color bar on stats properties ([59ac2aa](59ac2aa3c9))
2025-07-01 09:37:03 +00:00
7cf9cab032 Merge branch 'fix/stat-color' into 'main'
fix(atlantis): show color bar on stats properties

See merge request oceanbox/Poseidon!33
2025-07-01 11:31:51 +02:00
1c14274cdb feat: Add Initial Plume UI 2025-07-01 10:26:58 +02:00
Stig Rune Jensen
59ac2aa3c9 fix(atlantis): show color bar on stats properties 2025-07-01 10:11:16 +02:00
semantic-release-bot
b29a29a2d2 chore(release): 1.14.3
## [1.14.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.14.2...v1.14.3) (2025-06-30)

### Bug Fixes

* **sorcerer:** Only run sorcerer on c-x nodes ([41d792d](41d792dc2a))
2025-06-30 07:37:14 +00:00
40c5d7559b Merge branch 'mrtz/sorcerer' into 'main'
fix(sorcerer): Only run sorcerer on c-x nodes

See merge request oceanbox/Poseidon!32
2025-06-30 09:31:59 +02:00
41d792dc2a fix(sorcerer): Only run sorcerer on c-x nodes 2025-06-30 08:55:19 +02:00
semantic-release-bot
258d1c1868 chore(release): 1.14.2
## [1.14.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.14.1...v1.14.2) (2025-06-27)

### Bug Fixes

* **atlantis:** Replace plotly with plotly-basic-dist-min ([36e12f3](36e12f39cc))
* **atlantis:** Switch to custom bundle ([ec87726](ec87726347))
2025-06-27 18:52:26 +00:00
Jonas Juselius
10a6c109c6 Merge branch 'mrtz/bundlesize' into 'main'
fix(atlantis): Replace plotly with our own bundle

See merge request oceanbox/Poseidon!29
2025-06-27 17:39:21 +02:00
ec87726347 fix(atlantis): Switch to custom bundle 2025-06-27 12:39:41 +02:00
36e12f39cc fix(atlantis): Replace plotly with plotly-basic-dist-min
It's a basic and minified version of plotly only containing
`bar`, `pie` and `scatter` charts. Reduces our bundlesize
by approximately 50%.
2025-06-27 08:46:24 +02:00
semantic-release-bot
c9ea587cfa chore(release): 1.14.1
## [1.14.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.14.0...v1.14.1) (2025-06-25)

### Bug Fixes

* **atlantis:** multiple select archive management ([8bd0c71](8bd0c7146f))
2025-06-25 12:32:36 +00:00
6870e882a0 Merge branch 'fix/archive-select' into 'main'
fix(atlantis): multiple select archive management

See merge request oceanbox/Poseidon!30
2025-06-25 14:26:55 +02:00
Stig Rune Jensen
8bd0c7146f fix(atlantis): multiple select archive management 2025-06-25 12:15:47 +00:00
semantic-release-bot
135f665881 chore(release): 1.14.0
# [1.14.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.13.1...v1.14.0) (2025-06-23)

### Bug Fixes

* Allow sentry in tilt and prod ([cab6de5](cab6de560d))
* Build Catalog and Archivist Client ([7d34824](7d348248bb))
* **sentry:** Only run in Prod/Staging ([712b7d2](712b7d2bf2))

### Features

* **atlantis:** Add Sentry to Client and Server ([39dbfa5](39dbfa5592))
* **atlantis:** Instrument server with sentry ([d47359b](d47359b840))
* **sorcerer:** Instrument sorcerer server ([92d3583](92d3583e0b))
2025-06-23 19:18:19 +00:00
77717eb32a Merge branch 'mrtz/sentry' into 'main'
feat(atlantis|sorcerer): Add Sentry to Client and Server

See merge request oceanbox/Poseidon!27
2025-06-23 20:48:18 +02:00
712b7d2bf2 fix(sentry): Only run in Prod/Staging 2025-06-23 17:52:07 +02:00
cab6de560d fix: Allow sentry in tilt and prod 2025-06-23 17:22:38 +02:00
7d348248bb fix: Build Catalog and Archivist Client 2025-06-23 12:56:35 +02:00
92d3583e0b feat(sorcerer): Instrument sorcerer server 2025-06-23 12:00:50 +02:00
d47359b840 feat(atlantis): Instrument server with sentry
Also switches to MSBuildCracker for Fable (default in 5.0)
to avoid problems with temp.csproj.
2025-06-22 13:20:46 +02:00
Moritz Jörg
8570ee37e0 chore: Unify nix shell and update fvcom packages 2025-06-22 11:21:15 +02:00
Moritz Jörg
39dbfa5592 feat(atlantis): Add Sentry to Client and Server 2025-06-22 11:20:41 +02:00
339 changed files with 45935 additions and 14114 deletions

View File

@@ -1,38 +0,0 @@
open Fake.Core
open Fake.IO
open Farmer
open Farmer.Builders
open Helpers
initializeContext()
let packPath = Path.getFullName "packages"
Target.create "Clean" (fun _ -> Shell.cleanDir packPath)
Target.create "InstallClient" (fun _ ->
run bun "install" "."
run dotnet "tool restore" "."
)
Target.create "Run" ignore
Target.create "Format" (fun _ ->
run dotnet "fantomas . -r" "src"
)
open Fake.Core.TargetOperators
let dependencies = [
"Clean"
==> "InstallClient"
"Run"
==> "InstallClient"
"Format"
]
[<EntryPoint>]
let main args = runOrDefault args

View File

@@ -1,127 +0,0 @@
module Helpers
open Fake.Core
let initializeContext () =
let execContext = Context.FakeExecutionContext.Create false "build.fsx" [ ]
Context.setExecutionContext (Context.RuntimeContext.Fake execContext)
module Proc =
module Parallel =
open System
let locker = obj()
let colors =
[| ConsoleColor.Blue
ConsoleColor.Yellow
ConsoleColor.Magenta
ConsoleColor.Cyan
ConsoleColor.DarkBlue
ConsoleColor.DarkYellow
ConsoleColor.DarkMagenta
ConsoleColor.DarkCyan |]
let print color (colored: string) (line: string) =
lock locker
(fun () ->
let currentColor = Console.ForegroundColor
Console.ForegroundColor <- color
Console.Write colored
Console.ForegroundColor <- currentColor
Console.WriteLine line)
let onStdout index name (line: string) =
let color = colors.[index % colors.Length]
if isNull line then
print color $"{name}: --- END ---" ""
else if String.isNotNullOrEmpty line then
print color $"{name}: " line
let onStderr name (line: string) =
let color = ConsoleColor.Red
if isNull line |> not then
print color $"{name}: " line
let redirect (index, (name, createProcess)) =
createProcess
|> CreateProcess.redirectOutputIfNotRedirected
|> CreateProcess.withOutputEvents (onStdout index name) (onStderr name)
let printStarting indexed =
for (index, (name, c: CreateProcess<_>)) in indexed do
let color = colors.[index % colors.Length]
let wd =
c.WorkingDirectory
|> Option.defaultValue ""
let exe = c.Command.Executable
let args = c.Command.Arguments.ToStartInfo
print color $"{name}: {wd}> {exe} {args}" ""
let run cs =
cs
|> Seq.toArray
|> Array.indexed
|> fun x -> printStarting x; x
|> Array.map redirect
|> Array.Parallel.map Proc.run
let createProcess exe arg dir =
CreateProcess.fromRawCommandLine exe arg
|> CreateProcess.withWorkingDirectory dir
|> CreateProcess.ensureExitCode
let dotnet = createProcess "dotnet"
let fable = createProcess "fable"
let bun =
let bunPath =
match ProcessUtils.tryFindFileOnPath "bun" with
| Some path -> path
| None ->
"bun was not found in path. Please install it and make sure it's available from your path. " +
"See https://safe-stack.github.io/docs/quickstart/#install-pre-requisites for more info"
|> failwith
createProcess bunPath
let bunx = createProcess "bunx"
type BundleMode =
| Prod
| Devel
| Watch
with
override this.ToString() =
match this with
| Prod -> "production"
| Devel -> "development"
| Watch -> "watch"
let viteCmd (m: BundleMode) outDir =
match m with
| Prod -> $"vite build -c ../../vite.config.js -m {m} --emptyOutDir --outDir {outDir}/public"
| Devel -> $"vite build -c ../../vite.config.js -m {m} --minify false --sourcemap true --emptyOutDir --outDir {outDir}/public"
| Watch -> "vite -c ../../vite.config.js"
let run proc arg dir =
proc arg dir
|> Proc.run
|> ignore
let runParallel processes =
processes
|> Proc.Parallel.run
|> ignore
let runOrDefault args =
try
match args with
| [| target |] -> Target.runOrDefault target
| _ ->
Target.runOrDefault "Run"
0
with e ->
printfn "%A" e
1

View File

@@ -1,34 +0,0 @@
{
"version": 1,
"isRoot": true,
"tools": {
"fable": {
"version": "4.24.0",
"commands": [
"fable"
],
"rollForward": false
},
"femto": {
"version": "0.19.0",
"commands": [
"femto"
],
"rollForward": false
},
"fantomas": {
"version": "7.0.0",
"commands": [
"fantomas"
],
"rollForward": false
},
"dotnet-outdated-tool": {
"version": "4.6.7",
"commands": [
"dotnet-outdated"
],
"rollForward": false
}
}
}

View File

@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/sdk:9.0
FROM mcr.microsoft.com/dotnet/sdk:9.0.301
# Bun version
ARG BUN_INSTALL=/usr/local
@@ -26,4 +26,4 @@ ENV PATH="/root/.dotnet/tools:${PATH}"
# Copy endpoint specific user settings into container to specify
# .NET Core should be used as the runtime.
COPY settings.vscode.json /root/.vscode-remote/data/Machine/settings.json
COPY settings.vscode.json /root/.vscode-remote/data/Machine/settings.json

View File

@@ -9,8 +9,23 @@ insert_final_newline = false
[*.js]
indent_size = 2
max_line_length = 80
[*.scss]
indent_size = 2
max_line_length = 80
[*.nix]
indent_size = 2
max_line_length= 80
[*.scss]
indent_size = 2
max_line_length = 80
[*.yaml]
indent_size = 2
[*.fs]
max_line_length= 120
@@ -21,6 +36,7 @@ fsharp_space_before_uppercase_invocation = true
fsharp_blank_lines_around_nested_multiline_expressions = false
fsharp_newline_between_type_definition_and_members = false
fsharp_multiline_bracket_style = stroustrup
fsharp_multi_line_lambda_closing_newline = true
fsharp_array_or_list_multiline_formatter = character_width
fsharp_max_array_or_list_width = 70

17
.envrc
View File

@@ -1 +1,16 @@
use nix
#!/usr/bin/env bash
export NPINS_DIRECTORY="nix"
export APP_ENV=$USER
# the shebang is ignored, but nice for editors
watch_file nix/sources.json
# Load .env file if it exists
dotenv_if_exists
# Activate development shell
use nix
# HACK: Workaround for direnv bug
unset TMP TMPDIR TEMP TEMPDIR

14
.gitignore vendored
View File

@@ -5,23 +5,31 @@
.idea/
.ionide/
.certs/
.config/
obj/
bin/
packages/
node_modules/
release.cmd
release.sh
*.orig
*.DotSettings.user
.pre-commit-config.yaml
deploy/
dist/
build/
*.db
.env
build.fsx.lock
*.sif
result*
result-*
netrc
.deps*
*.key
_*.yaml
tilt/base/_manifest.yaml
NuGet.Config
sync.list
packages.lock.json
package-lock.json
*.nupkg
*.fable-temp*
.env

View File

@@ -1,28 +1,51 @@
# yaml-language-server: $schema=https://gitlab.com/gitlab-org/gitlab/-/raw/master/app/assets/javascripts/editor/schema/ci.json
variables:
SDK_VERSION: 9.0
SKIP_TESTS: "true"
SKIP_TESTS: "true"
default:
tags:
- nix
include:
- project: oceanbox/gitlab-ci
ref: v4.1
file: template/Base.gitlab-ci.yml
- local: '/src/Atlantis/.gitlab-ci.yml'
rules:
- changes:
- 'src/Atlantis/**/*'
- local: '/src/Sorcerer/.gitlab-ci.yml'
rules:
- changes:
- 'src/Sorcerer/**/*'
- local: '/src/Interfaces/.gitlab-ci.yml'
rules:
- changes:
- 'src/Interfaces/**/*'
- local: '/src/DataAgent/.gitlab-ci.yml'
rules:
- changes:
- 'src/DataAgent/**/*'
- local: '/src/ServerPack/.gitlab-ci.yml'
rules:
- changes:
- 'src/ServerPack/**/*'
- project: oceanbox/gitlab-ci
ref: v4.5
file: template/Base.gitlab-ci.yml
- local: "/src/Atlantis/.gitlab-ci.yml"
rules:
- changes:
- "src/Atlantis/**/*"
- "nix/packages/atlantis.nix"
- "nix/packages/atlantis-client.nix"
- "nix/containers.nix"
- local: "/src/Sorcerer/.gitlab-ci.yml"
rules:
- changes:
- "src/Sorcerer/**/*"
- "nix/packages/sorcerer.nix"
- "nix/containers.nix"
- local: "/src/Archivist/.gitlab-ci.yml"
rules:
- changes:
- "src/Archivist/**/*"
- "nix/packages/archivist.nix"
- local: "/src/Interfaces/.gitlab-ci.yml"
rules:
- changes:
- "src/Interfaces/**/*"
- "nix/packages/api.nix"
- local: "/src/DataAgent/.gitlab-ci.yml"
rules:
- changes:
- "src/DataAgent/**/*"
- "nix/packages/dataagent.nix"
- local: "/src/ServerPack/.gitlab-ci.yml"
rules:
- changes:
- "src/ServerPack/**/*"
- "nix/packages/serverpack.nix"
- local: "/src/Codex/.gitlab-ci.yml"
rules:
- changes:
- "src/Codex/**/*"
- "nix/packages/node-modules.nix"
- "nix/packages/sources.nix"

View File

@@ -15,7 +15,7 @@ plugins:
- "src/Atlantis/src/**.fsproj"
- "src/Sorcerer/src/**.fsproj"
- "src/DataAgent/src/**.fsproj"
- "src/ServerPack/src/*.fsproj"
- "src/ServerPack/src/**.fsproj"
- "src/Interfaces/**.fsproj"
- - '@semantic-release/exec'
- generateNotesCmd: "echo ${nextRelease.version} > VERSION"
@@ -29,7 +29,7 @@ plugins:
- "src/Atlantis/src/**.fsproj"
- "src/Sorcerer/src/**.fsproj"
- "src/DataAgent/src/**.fsproj"
- "src/ServerPack/src/*.fsproj"
- "src/ServerPack/src/**.fsproj"
- "src/Interfaces/**.fsproj"
analyzeCommits:

View File

@@ -1,17 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include=".build/Helpers.fs" />
<Compile Include=".build/Build.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Fake.Core.Target" Version="6.1.3" />
<PackageReference Include="Fake.DotNet.Cli" Version="6.1.3" />
<PackageReference Include="Fake.IO.FileSystem" Version="6.1.3" />
<PackageReference Include="Farmer" Version="1.9.6" />
<PackageReference Update="FSharp.Core" Version="9.0.100" />
</ItemGroup>
</Project>

35
Directory.Build.props Normal file
View File

@@ -0,0 +1,35 @@
<Project>
<!-- Make F# support Central Package Management -->
<PropertyGroup>
<DisableImplicitSystemValueTupleReference>true</DisableImplicitSystemValueTupleReference>
<DisableImplicitFSharpCoreReference>true</DisableImplicitFSharpCoreReference>
</PropertyGroup>
<PropertyGroup>
<!-- <DebugType Condition=" '$(DebugType)' == '' ">embedded</DebugType> -->
<!-- <DebugType>embedded</DebugType> -->
<!-- <Deterministic>true</Deterministic> -->
<!-- <NetCoreTargetingPackRoot>[UNDEFINED]</NetCoreTargetingPackRoot> -->
<!-- Warnings and Errors -->
<!--
FS0025: Incomplete pattern matches on this expression.
FS1182: Unused variables
FS1178: Does not support structural equality
FS3390: Malformed XML doc comment
-->
<!-- <WarnOn>FS3388,FS3559</WarnOn> -->
<!-- <WarnOn>1182;3390;1178;$(WarnOn)</WarnOn> -->
<!-- <TreatWarningsAsErrors>true</TreatWarningsAsErrors> -->
<WarningsAsErrors>FS0025</WarningsAsErrors>
<NoWarn>NU1603;MSB3277</NoWarn>
<!-- Restore with Lockfiles -->
<!-- https://www.gresearch.co.uk/blog/article/improve-nuget-restores-with-static-graph-evaluation/ -->
<RestoreUseStaticGraphEvaluation>true</RestoreUseStaticGraphEvaluation>
<DisableImplicitNuGetFallbackFolder>true</DisableImplicitNuGetFallbackFolder>
<!-- Performance -->
<ServerGarbageCollection>true</ServerGarbageCollection>
<OtherFlags>$(OtherFlags) --test:GraphBasedChecking --test:ParallelOptimization --test:ParallelIlxGen</OtherFlags>
</PropertyGroup>
</Project>

109
Directory.Packages.props Normal file
View File

@@ -0,0 +1,109 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<!-- Common -->
<PackageVersion Include="FSharp.Core" Version="9.0.303" />
<PackageVersion Include="Fargo.CmdLine" Version="1.7.5" />
<PackageVersion Include="FSharpPlus" Version="1.7.0" />
<PackageVersion Include="FSharp.Data" Version="6.4.1" />
<PackageVersion Include="FsToolkit.ErrorHandling" Version="5.0.1" />
<PackageVersion Include="Thoth.Json.Giraffe" Version="6.0.0" />
<PackageVersion Include="Thoth.Json.Net" Version="12.0.0" />
<PackageVersion Include="Serilog" Version="4.2.0" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0"/>
<PackageVersion Include="Drifters.Api" Version="6.22.0" />
<!-- Client -->
<PackageVersion Include="Fable.Browser.IndexedDB" Version="2.2.0" />
<PackageVersion Include="Fable.Browser.ResizeObserver" Version="1.0.0" />
<PackageVersion Include="Fable.Browser.WebGL" Version="1.3.0" />
<PackageVersion Include="Fable.Core" Version="4.4.0"/>
<PackageVersion Include="Fable.Elmish" Version="4.2.0" />
<PackageVersion Include="Fable.Fetch" Version="2.7.0" />
<PackageVersion Include="Fable.FontAwesome.Free" Version="3.0.0"/>
<PackageVersion Include="Fable.Lit.Elmish" Version="1.6.2-oceanbox" />
<PackageVersion Include="Fable.Lit.React" Version="1.6.2-oceanbox" />
<PackageVersion Include="Fable.Lit" Version="1.6.2-oceanbox" />
<PackageVersion Include="Fable.OpenLayers" Version="2.19.0" />
<PackageVersion Include="Fable.Promise" Version="3.2.0" />
<PackageVersion Include="Fable.React" Version="9.4.0" />
<PackageVersion Include="Fable.Remoting.Client" Version="7.32.0" />
<PackageVersion Include="Fable.Remoting.MsgPack" Version="1.24.0" />
<PackageVersion Include="Fable.SignalR.Elmish" Version="2.1.0" />
<PackageVersion Include="Fable.SimpleHttp" Version="3.6.0" />
<PackageVersion Include="Feliz.Router" Version="4.0.0"/>
<PackageVersion Include="Feliz" Version="2.9.0" />
<PackageVersion Include="Feliz.UseElmish" Version="2.5.0" />
<PackageVersion Include="Feliz.CompilerPlugins" Version="2.2.0" />
<PackageVersion Include="Matplotlib.ColorMaps" Version="3.0.1" />
<PackageVersion Include="Thoth.Fetch" Version="3.0.1" />
<PackageVersion Include="Thoth.Json" Version="10.4.1"/>
<PackageVersion Include="FS.FluentUI" Version="3.0.0"/>
<!-- Serverpack -->
<PackageVersion Include="OpenFga.Sdk" Version="0.7.0"/>
<PackageVersion Include="FSharp.SystemTextJson" Version="1.3.13"/>
<PackageVersion Include="Saturn.OpenTelemetry" Version="0.6.0-alpha"/>
<!-- Atlantis -->
<PackageVersion Include="Argu" Version="6.2.5" />
<PackageVersion Include="AspNetCore.Serilog.RequestLoggingMiddleware" Version="1.0.2" />
<PackageVersion Include="Azure.Extensions.AspNetCore.DataProtection.Blobs" Version="1.5.0" />
<PackageVersion Include="Azure.Extensions.AspNetCore.DataProtection.Keys" Version="1.4.0" />
<PackageVersion Include="Azure.Identity" Version="1.13.2" />
<PackageVersion Include="Azure.Security.KeyVault.Secrets" Version="4.7.0" />
<PackageVersion Include="Dapr.Actors" Version="1.16.0" />
<PackageVersion Include="Dapr.Actors.AspNetCore" Version="1.16.0" />
<PackageVersion Include="Dapr.AspNetCore" Version="1.16.0" />
<PackageVersion Include="Dapr.Client" Version="1.16.0" />
<PackageVersion Include="Fable.Remoting.DotnetClient" Version="3.35.0" />
<PackageVersion Include="Fable.Remoting.Giraffe" Version="5.21.0" />
<PackageVersion Include="Fable.Remoting.Server" Version="5.39.0" />
<PackageVersion Include="Fable.SignalR.Saturn" Version="2.1.0" />
<PackageVersion Include="Giraffe" Version="7.0.2" />
<PackageVersion Include="IdentityModel.AspNetCore" Version="4.3.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="9.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="9.0.2" />
<PackageVersion Include="Microsoft.Extensions.Http.Polly" Version="9.0.2" />
<PackageVersion Include="Oceanbox.FvcomKit" Version="5.13.0" />
<PackageVersion Include="prometheus-net.AspNetCore" Version="8.2.1" />
<PackageVersion Include="Saturn" Version="0.17.0" />
<PackageVersion Include="SecurityCodeScan" Version="3.5.4">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageVersion>
<PackageVersion Include="Sentry.AspNetCore" Version="5.11.0" />
<PackageVersion Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageVersion Include="Serilog.Enrichers.CorrelationId" Version="3.0.1" />
<PackageVersion Include="Serilog.Expressions" Version="5.0.0" />
<PackageVersion Include="Serilog.Sinks.OpenTelemetry" Version="4.1.1" />
<PackageVersion Include="System.Text.Encodings.Web" Version="9.0.2" />
<!-- Sorcerer -->
<PackageVersion Include="MessagePack" Version="3.1.3" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="ProjNet.FSharp" Version="5.2.0" />
<!-- Dapperizer -->
<PackageVersion Include="Oceanbox.SDSLite" Version="2.8.0" />
<PackageVersion Include="Dapper.FSharp" Version="4.9.0"/>
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="9.0.2"/>
<PackageVersion Include="NetTopologySuite" Version="2.5.0"/>
<PackageVersion Include="Npgsql" Version="9.0.2" />
<PackageVersion Include="Npgsql.NetTopologySuite" Version="9.0.2"/>
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.2"/>
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite" Version="9.0.2"/>
<!-- Entity -->
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="9.0.1"/>
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageVersion>
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageVersion>
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.1" />
<!-- Analyzers -->
<PackageVersion Include="G-Research.FSharp.Analyzers" Version="0.19.0" />
<PackageVersion Include="Ionide.Analyzers" Version="0.14.9" />
</ItemGroup>
</Project>

21
LICENSE
View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2020 Serit Tromsø AS
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -24,6 +24,10 @@
<Project Path="src/Atlantis/src/Server/Petimeter/Petimeter.fsproj" />
<Project Path="src/Atlantis/src/Server/Server.fsproj" />
</Folder>
<Folder Name="/Codex/">
<Project Path="src\Codex\src\Client\Codex.Client.fsproj" />
<Project Path="src\Codex\src\Server\Codex.Server.fsproj" />
</Folder>
<Folder Name="/DataAgent/">
<Project Path="src/DataAgent/src/Entity/Entity.csproj" />
</Folder>
@@ -31,6 +35,7 @@
<Project Path="src/DataAgent/src/DataAgent/Oceanbox.DataAgent.fsproj" />
</Folder>
<Folder Name="/Interfaces/">
<Project Path="src/Interfaces/Api/Poseidon.Api.fsproj" />
<Project Path="src/Interfaces/Archmaester/Archmaester.Api.fsproj" />
<Project Path="src/Interfaces/Atlantis/Atlantis.Api.fsproj" />
<Project Path="src/Interfaces/Hipster/Hipster.Api.fsproj" />
@@ -46,7 +51,6 @@
<File Path="global.json" />
</Folder>
<Folder Name="/Sorcerer/">
<Project Path="src/Sorcerer/src/Client/Client.fsproj" />
<Project Path="src/Sorcerer/src/Server/Sorcerer.fsproj" />
</Folder>
</Solution>

106
README.md
View File

@@ -1,15 +1,21 @@
[![built with nix](https://img.shields.io/badge/built%20with-nix-%235277C3?logo=nixos)](https://nixos.org/)
# Atlantis
# Poseidon
Oceanbox's Single Page Application used for Visualizations and Analysis.
Oceanbox's comprehensive platform for oceanic data visualization, analysis, and processing.
## Documentation
- **[Nix Build System](nix/README.md)** - Comprehensive guide to the Nix-based build system, packages, containers, and workflows
- **[Scripts](scripts/README.md)** - Available utility scripts for development and deployment
## Bootstrapping Guide
To bootstrap Atlantis for development, build and run it using Tilt.
Some setup is required:
### k8s access
### Kubernetes Access
To run our application on the kubernetes cluster, Tilt needs access.
You need to authenticate with `oidc`, using your microsoft account.
@@ -40,21 +46,21 @@ Next, configure the required contexts to use `oidc` (also in your `~/.kube/confi
```yaml
---
- context:
cluster: ekman
namespace: sorcerer
user: oidc
cluster: ekman
namespace: sorcerer
user: oidc
name: ekman
- context:
cluster: oceanbox
user: oidc
cluster: oceanbox
namespace: atlantis
user: oidc
name: oceanbox
---
```
Finally, **you must be granted the necessary priveleges in Entra to access the clusters.**
Verify that you have access with `kubectl`:
```sh
```shell
kubectl --context oceanbox -n default get pods
```
@@ -63,26 +69,7 @@ kubectl --context oceanbox -n default get pods
Required helm manifests are hosted in a separate repository: <https://gitlab.com/oceanbox/manifests>.
Clone it into a directory _in the same parent directory as this repository._
The Bitnami respository must also be added to helm:
```sh
helm repo add bitnami https://charts.bitnami.com/bitnami
```
### DNS
Some DNS masking is required.
Add the following to your NixOS configuration:
```nix
services.dnsmasq = {
enable = true;
settings.address = [
"/.local/127.0.0.1"
"/.local.oceanbox.io/127.0.0.1"
];
};
```
You'll have to run `helm dependency update` in the atlantis directory within the manifest repo to download the charts.
### NuGet
@@ -97,28 +84,45 @@ To retrieve packages from the private Oceanbox nuget registry, configure it with
</packageSources>
<packageSourceCredentials>
<oceanbox>
<add key="Username" value="oceanbox-nuget" />
<add key="ClearTextPassword" value="<...>" />
<add key="Username" value="<Your-GitLab-Username>" />
<add key="ClearTextPassword" value="<Your-GitLab-PAT>" />
</oceanbox>
</packageSourceCredentials>
<packageSourceMapping>
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
<packageSource key="oceanbox">
<package pattern="Oceanbox.*" />
<package pattern="ProjNet.FSharp" />
<package pattern="Drifters.Api" />
<package pattern="Fable.Lit" />
<package pattern="Fable.Lit.*" />
<package pattern="Fable.SignalR" />
<package pattern="Fable.SignalR.*" />
<package pattern="Fable.OpenLayers" />
<package pattern="Matplotlib.*" />
</packageSource>
</packageSourceMapping>
</configuration>
```
Substitute `<...>` for the corresponding secret.
Substitute with your own gitlab username and PAT in the credentials.
Now, we should be able to `restore`:
```sh
dotnet tool restore
dotnet restore
```shell
dotnet restore Poseidon.slnx
```
for `dotnet-tools` we use nix, so entering the shell using `nix-shell` or `direnv` is enough.
### Mkcert
To generate certificates correctly, vite needs the `mkcert` binary in a predefined path in our home directory.
`mkcert` is included in our dev shell, so we can create a symlink to its location in the nix store:
```sh
```fish
which mkcert | xargs -I{} ln -s {} ~/.vite-plugin-mkcert
```
@@ -127,12 +131,16 @@ which mkcert | xargs -I{} ln -s {} ~/.vite-plugin-mkcert
### Docker Login
In order for Tilt to push the images it builds to the oceanbox registry, we must use `docker login` to authenticate with it.
First, create a personal access token in your gitlab account.
It should have the `read_registry` and `write_registry` scopes set.
First, create a personal access token in your gitlab account. It should have the `read_registry` and `write_registry` scopes set.
```fish
set -x TOKEN glpat-xxxx
```
Then, supply it to `docker login`:
```sh
docker login registry.gitlab.com/oceanbox
```shell
echo "$TOKEN" | docker login gitlab.com -u <user> --password-stdin
```
When prompted, authenticate with your gitlab username and the PAT you just created.
@@ -141,7 +149,7 @@ When prompted, authenticate with your gitlab username and the PAT you just creat
A namespace must be created for your tilt application to live in on the cluster.
```sh
```shell
kubectl create ns "$APP_NAMESPACE" --context oceanbox
```
@@ -149,7 +157,7 @@ kubectl create ns "$APP_NAMESPACE" --context oceanbox
In the project root, run tilt with the following command:
```sh
```shell
tilt up --context oceanbox
```
@@ -157,9 +165,21 @@ You should now be able to access the Atlantis client (with HMR) on <atlantis.loc
### Trust Root Certificate
> [!note]
> You'll need to run `just run-client` in `src/Atlantis` to generate the certificates in `~/.vite-plugin-mkcert/certs`
In order for your browser to allow you to access the web application, you must add the root certificate generated by `mkcert` to the list of trusted authorities in your browser:
1. In firefox, navigate to settings and search for _"Certificates"._
2. Click on _"View Certificates",_ then _"Import..."_ in the _"Authorities"_ tab.
3. Select the root certificate; `~/.vite-plugin-mkcert/certs/rootCA.pem`.
- Make sure to check _"This certificate can identify websites"._
### Add `user` to OpenFGA
Ask [sales](support@oceanbox.io) to add your `azure-ad-user` to OpenFGA.
### CORS for Sorcerer
Add the `url` of your instance to the CORS list of Sorcerer
[here](https://gitlab.com/oceanbox/manifests/-/blob/main/values/sorcerer/kustomize/prod/appsettings.json?ref_type=heads#L52).

View File

@@ -1,5 +1,993 @@
# Changelog
## [1.40.5](https://gitlab.com/oceanbox/Poseidon/compare/v1.40.4...v1.40.5) (2026-01-21)
### Bug Fixes
* **xtractor:** Reduce to 4 cores per task ([8e824d4](https://gitlab.com/oceanbox/Poseidon/commit/8e824d4afa0b03f59e006d3a0d50fb216e71483e))
## [1.40.4](https://gitlab.com/oceanbox/Poseidon/compare/v1.40.3...v1.40.4) (2026-01-21)
### Bug Fixes
* **xtractor:** Reduce core requirement to 8 ([efacb2a](https://gitlab.com/oceanbox/Poseidon/commit/efacb2a3322de0ced45db4eec240846f4e371a75))
## [1.40.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.40.2...v1.40.3) (2026-01-20)
### Bug Fixes
* **inbox|xtracto:** Delete/Read msg and allow non-ascii xtractor names ([d8d5e07](https://gitlab.com/oceanbox/Poseidon/commit/d8d5e076baf8b559200f2da91237f9874678b216))
* **multiauth:** Add clientId to redirect on signout ([54c40d7](https://gitlab.com/oceanbox/Poseidon/commit/54c40d7accc4bbc43f66dda0df647ccac482a2b0))
## [1.40.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.40.1...v1.40.2) (2026-01-19)
### Bug Fixes
* **xtract:** Disabled if not allowed to simulate transport ([e429a85](https://gitlab.com/oceanbox/Poseidon/commit/e429a855e5bd00493e2f99647092aebce9c99a2a))
## [1.40.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.40.0...v1.40.1) (2026-01-19)
### Bug Fixes
* fix tilt build on net10 ([d5cde19](https://gitlab.com/oceanbox/Poseidon/commit/d5cde19250847f7b091cfa5f65eb703405c202b6))
# [1.40.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.39.2...v1.40.0) (2026-01-16)
### Bug Fixes
* **Codex:** expose days instead of frames in arcive form ([6cf5262](https://gitlab.com/oceanbox/Poseidon/commit/6cf5262dd5c98517a3c767f410c858fe32c07bd5))
* **Codex:** only allow inbounds time intervals on edit archive ([eaea4b2](https://gitlab.com/oceanbox/Poseidon/commit/eaea4b2e215669cec19f2a8cec122ba670a7a202))
### Features
* **Codex:** edit archives ([ec10932](https://gitlab.com/oceanbox/Poseidon/commit/ec109328fbf5f237a52ef77cbd44dff571deee5f))
## [1.39.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.39.1...v1.39.2) (2026-01-15)
### Bug Fixes
* fix net10 issues ([f4943a1](https://gitlab.com/oceanbox/Poseidon/commit/f4943a148b72fb7e10a745cc3e806b9c4bdd76d8))
## [1.39.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.39.0...v1.39.1) (2026-01-15)
### Bug Fixes
* **ci:** Remove schedule check ([ab37e88](https://gitlab.com/oceanbox/Poseidon/commit/ab37e88bb0f669a7aa94bf831f95f8c60dc28804))
# [1.39.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.38.5...v1.39.0) (2026-01-14)
### Bug Fixes
* **Codex:** add * user in archmeister on public archives ([cd678a4](https://gitlab.com/oceanbox/Poseidon/commit/cd678a41f64a64c6f3616f32bafeaae6715c08a4))
* **Codex:** use feliz router guid matching ([7182f7c](https://gitlab.com/oceanbox/Poseidon/commit/7182f7c9f094d65c884e8e02d4aaa89561ca5e82))
* **Codex:** utc start_time ([492651e](https://gitlab.com/oceanbox/Poseidon/commit/492651e0f34035d8c61174aa50336222bcfd979c))
* **DataAgent:** use files from parent attribs instead of archive_files ([eac23e7](https://gitlab.com/oceanbox/Poseidon/commit/eac23e7d1a541f2e90374a2add689846d9e7b642))
### Features
* **Codex:** ability to add FVCOM archives ([d86db7a](https://gitlab.com/oceanbox/Poseidon/commit/d86db7a66ca5ecb6a9ad45ce3d47be3a98d56bb8))
## [1.38.5](https://gitlab.com/oceanbox/Poseidon/compare/v1.38.4...v1.38.5) (2026-01-13)
### Bug Fixes
* **Archmaester:** Rollback add archive if openfga fails ([46e86eb](https://gitlab.com/oceanbox/Poseidon/commit/46e86eb5f961a45fba2da1525c1472bdca79ab47))
## [1.38.4](https://gitlab.com/oceanbox/Poseidon/compare/v1.38.3...v1.38.4) (2026-01-12)
### Bug Fixes
* **nix:** Bump node deps ([e513d87](https://gitlab.com/oceanbox/Poseidon/commit/e513d87d249843423f0e0a62275afe45e6c73a46))
## [1.38.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.38.2...v1.38.3) (2026-01-12)
### Bug Fixes
* **xtractor:** Set maxEndDate to 1 year and format ([5315a05](https://gitlab.com/oceanbox/Poseidon/commit/5315a05fa255c6164d3ef73c0f5e20cdb4c632d0))
## [1.38.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.38.1...v1.38.2) (2026-01-12)
### Bug Fixes
* **atlas:** Disable Snowflakes ([4cd3673](https://gitlab.com/oceanbox/Poseidon/commit/4cd3673d15783afa72ca3358e6bd8c3a8cfbfd16))
## [1.38.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.38.0...v1.38.1) (2026-01-11)
### Bug Fixes
* **xtractor:** Move to short partition and 16 CPU, also set max duration to 1 year ([56d3476](https://gitlab.com/oceanbox/Poseidon/commit/56d34767d7a2e0bc6aadaa5987344c6e7a58698a))
# [1.38.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.37.1...v1.38.0) (2026-01-07)
### Bug Fixes
* client layout ([efb3292](https://gitlab.com/oceanbox/Poseidon/commit/efb3292d9f4a8fccf2cebbdd75b3d3ffc186fa11))
### Features
* add fluent ui to codex ([d8bf174](https://gitlab.com/oceanbox/Poseidon/commit/d8bf174d3aa181169365c178b4335052e13eabc5))
## [1.37.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.37.0...v1.37.1) (2026-01-05)
### Bug Fixes
* **xtractor:** Avoid using union types in Dapr Actors ([14c1a57](https://gitlab.com/oceanbox/Poseidon/commit/14c1a57331f981b1e1e0793426448ea261002e6d))
# [1.37.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.36.0...v1.37.0) (2025-12-22)
### Features
* Add XtractActor ([a8a187a](https://gitlab.com/oceanbox/Poseidon/commit/a8a187a412c13d3e9d21cbbcfc2e1813c0e38dfe))
# [1.36.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.35.2...v1.36.0) (2025-12-12)
### Features
* add Saturn.ReverseProxy middleware ([bd74504](https://gitlab.com/oceanbox/Poseidon/commit/bd745042dfa51fbbef7bf7b55d31b8b57e6ad0a4))
## [1.35.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.35.1...v1.35.2) (2025-12-01)
### Bug Fixes
* **drifters:** reverse toggle on postdrift analysis ([563faa6](https://gitlab.com/oceanbox/Poseidon/commit/563faa6c0bd20a7d3f184bba66ae5df340e7ef4e))
## [1.35.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.35.0...v1.35.1) (2025-12-01)
### Bug Fixes
* **stats:** no stats banner showing when stats are available, closing [#87](https://gitlab.com/oceanbox/Poseidon/issues/87) ([e75ffc4](https://gitlab.com/oceanbox/Poseidon/commit/e75ffc41e5d298e2ecf92c5c4d11e0f930f633ab))
* **stats:** set priority order of depth plots, closing [#88](https://gitlab.com/oceanbox/Poseidon/issues/88) ([2887e6a](https://gitlab.com/oceanbox/Poseidon/commit/2887e6a90951f4aaa088ad14b3d2bbcb6fd25b93))
# [1.35.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.34.2...v1.35.0) (2025-12-01)
### Features
* ❄️ ([a620c26](https://gitlab.com/oceanbox/Poseidon/commit/a620c26812d3ec7517c34e7931e03b411f725907))
## [1.34.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.34.1...v1.34.2) (2025-11-30)
### Bug Fixes
* Temporarily use vtn as only source for wind barbs ([048e803](https://gitlab.com/oceanbox/Poseidon/commit/048e80356b59cfcd408bf6784bbc2e22aebe25c6))
## [1.34.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.34.0...v1.34.1) (2025-11-29)
### Bug Fixes
* **stats:** find available stats archives ([48d46ed](https://gitlab.com/oceanbox/Poseidon/commit/48d46eda62160d3efe1423238a25f26a439c6b88))
# [1.34.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.33.11...v1.34.0) (2025-11-29)
### Bug Fixes
* extract data cage interaction matrix, closing [#84](https://gitlab.com/oceanbox/Poseidon/issues/84) ([afc888a](https://gitlab.com/oceanbox/Poseidon/commit/afc888ab604f4759e3787e3feab568a322109b34))
* reintroduce active layer selector, closes [#74](https://gitlab.com/oceanbox/Poseidon/issues/74) ([c1fa85f](https://gitlab.com/oceanbox/Poseidon/commit/c1fa85fd1b955e810bd76973e5873a7ab4cb8f18))
* remove parameter limitations on particle sims ([69b380e](https://gitlab.com/oceanbox/Poseidon/commit/69b380e6659521c9f9ab489fa75d1221cc9b7db2))
### Features
* fly-to coordinate button, closes [#85](https://gitlab.com/oceanbox/Poseidon/issues/85) ([a153238](https://gitlab.com/oceanbox/Poseidon/commit/a153238f79e2b2c87ac3553acce00bb17c1529f4))
## [1.33.11](https://gitlab.com/oceanbox/Poseidon/compare/v1.33.10...v1.33.11) (2025-11-28)
### Bug Fixes
* **ci:** Run archivist ci on coffee ([f68b7f6](https://gitlab.com/oceanbox/Poseidon/commit/f68b7f68c8fd95d5d593702632cc2e9a7b36007a))
## [1.33.10](https://gitlab.com/oceanbox/Poseidon/compare/v1.33.9...v1.33.10) (2025-11-28)
### Bug Fixes
* **ci:** Build on Coffee ([e04d36c](https://gitlab.com/oceanbox/Poseidon/commit/e04d36ca124693b471ac973cd4d0f39f42f0fec0))
## [1.33.9](https://gitlab.com/oceanbox/Poseidon/compare/v1.33.8...v1.33.9) (2025-11-28)
### Bug Fixes
* **nix:** Bump hash for node_modules ([fff7913](https://gitlab.com/oceanbox/Poseidon/commit/fff7913cd5280f0732bb23bc1cb6ed5282631b90))
## [1.33.8](https://gitlab.com/oceanbox/Poseidon/compare/v1.33.7...v1.33.8) (2025-11-28)
### Bug Fixes
* **ci:** Format .gitlab-ci.yml and move codex to nix runner ([3140c07](https://gitlab.com/oceanbox/Poseidon/commit/3140c07ad078d3b8b3082d2f86eaa73f4ba08969))
* **ci:** Remove unsed var ([759bbc6](https://gitlab.com/oceanbox/Poseidon/commit/759bbc6f60e5b364dd83653193d1501ce86c9eef))
* **ci:** Try using v4.4 ([c1be7c4](https://gitlab.com/oceanbox/Poseidon/commit/c1be7c468dd9a689eb0b1b61797841c398fadc02))
* **ci:** Use 4.4 for check ([b109dbd](https://gitlab.com/oceanbox/Poseidon/commit/b109dbdcbdfa3315e099b2edf72eb10518732c8f))
## [1.33.7](https://gitlab.com/oceanbox/Poseidon/compare/v1.33.6...v1.33.7) (2025-11-28)
### Bug Fixes
* **ci:** Change name to codex ([6d04af6](https://gitlab.com/oceanbox/Poseidon/commit/6d04af6230b536c1cbadf9abd2b59cf13609b451))
* **ci:** Correct tag for codex pipeline ([7cf5064](https://gitlab.com/oceanbox/Poseidon/commit/7cf50641f986ab81b030e678ff7db0de83acae98))
* **ci:** Run Codex on Coffee ([dd398bd](https://gitlab.com/oceanbox/Poseidon/commit/dd398bd96b0d79c8585a3156edcd2158982744d9))
## [1.33.6](https://gitlab.com/oceanbox/Poseidon/compare/v1.33.5...v1.33.6) (2025-11-28)
### Bug Fixes
* **maps:** Make FluentUI DatePicker popup inline; closes [#83](https://gitlab.com/oceanbox/Poseidon/issues/83) ([e3a1f56](https://gitlab.com/oceanbox/Poseidon/commit/e3a1f56b87c20ad9aada6108c92854b9d12671df))
## [1.33.5](https://gitlab.com/oceanbox/Poseidon/compare/v1.33.4...v1.33.5) (2025-11-27)
### Bug Fixes
* General fixes "stats" menu ([c3c9e8e](https://gitlab.com/oceanbox/Poseidon/commit/c3c9e8e4e2b473564d1f6434a28dd934457189e9)), closes [#79](https://gitlab.com/oceanbox/Poseidon/issues/79) [#80](https://gitlab.com/oceanbox/Poseidon/issues/80) [#81](https://gitlab.com/oceanbox/Poseidon/issues/81) [#82](https://gitlab.com/oceanbox/Poseidon/issues/82)
* no more DateFlicker (thanks Simen) ([7584bf6](https://gitlab.com/oceanbox/Poseidon/commit/7584bf661f2fd1cbf95e473a3334efcd69c77392))
* remove goto today in date picker ([f23b3f1](https://gitlab.com/oceanbox/Poseidon/commit/f23b3f18213682e0b41c1db13b72e3c0bb0534b6))
* **stats:** alert banner on missing stats ([5bb2ffd](https://gitlab.com/oceanbox/Poseidon/commit/5bb2ffd67c16078854b9f86a6728958c3923626c))
## [1.33.4](https://gitlab.com/oceanbox/Poseidon/compare/v1.33.3...v1.33.4) (2025-11-27)
### Bug Fixes
* **js:** Remove unused packages ([10a8c42](https://gitlab.com/oceanbox/Poseidon/commit/10a8c42319bbceebbf413d330fc72071f428e2dd))
## [1.33.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.33.2...v1.33.3) (2025-11-27)
### Bug Fixes
* **nix:** Pre-commit with prek ([3093757](https://gitlab.com/oceanbox/Poseidon/commit/309375745443add8347ad6fca7b2dfe86ed1b7a7))
* **nix:** Watch correct lock file ([79c6e2a](https://gitlab.com/oceanbox/Poseidon/commit/79c6e2abd0e26d220c10e532dffe53511f5dea0a))
## [1.33.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.33.1...v1.33.2) (2025-11-26)
### Bug Fixes
* create input display modal on demand ([1fed7ad](https://gitlab.com/oceanbox/Poseidon/commit/1fed7adf8009282184cacb4ddabf91274ee22b60))
* **drifters:** Add datepickers for drifters sims ([785d0d5](https://gitlab.com/oceanbox/Poseidon/commit/785d0d57ae6cc53b59ad1d8708e5f513aca8dd77))
* sim duration with restrictions ([a9145f6](https://gitlab.com/oceanbox/Poseidon/commit/a9145f6f79aa30cbe529d7a797b38dd2abe0a6cd))
* **timeline:** display use local time in timeline ([ccbe076](https://gitlab.com/oceanbox/Poseidon/commit/ccbe07619f31c1b7511c8a8f4ca11fe3af8cbd53))
* **timeline:** Marker uses UTC instead of CET + DST ([a474e7c](https://gitlab.com/oceanbox/Poseidon/commit/a474e7cbd4a894b9c1f5f20420dc728f93bc2e07))
## [1.33.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.33.0...v1.33.1) (2025-11-26)
### Bug Fixes
* Add missing * in Drifters ArchiveType FromString ([7054ade](https://gitlab.com/oceanbox/Poseidon/commit/7054ade55dffd058f0d622447da08587633b815c))
* **Atlantis:** Add wildcards in allow origin ([cde779e](https://gitlab.com/oceanbox/Poseidon/commit/cde779ea00f0666e6b548c7d711873b64cb294d9))
# [1.33.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.32.0...v1.33.0) (2025-11-24)
### Features
* properly display sim parameters, and fix clone, closing [#70](https://gitlab.com/oceanbox/Poseidon/issues/70) ([4980a41](https://gitlab.com/oceanbox/Poseidon/commit/4980a41a4456be53c8a0193f1fbc40e0238c0111))
# [1.32.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.31.0...v1.32.0) (2025-11-20)
### Bug Fixes
* spell out units in plots ([020a2f2](https://gitlab.com/oceanbox/Poseidon/commit/020a2f2e000ed907ad1eb7768e47f6b053038e4e))
### Features
* Add Density profile plot and cancelable jobs ([8472708](https://gitlab.com/oceanbox/Poseidon/commit/847270877abc3bd983a400a29a0b4bebae033b06)), closes [oceanbox/Poseidon#30](https://gitlab.com/oceanbox/Poseidon/issues/30)
# [1.31.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.30.1...v1.31.0) (2025-11-19)
### Features
* **DataAgent:** offline eval function when you have a dto ([1982770](https://gitlab.com/oceanbox/Poseidon/commit/19827701aad9625d55f3bab4d48cb44122d512ae))
## [1.30.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.30.0...v1.30.1) (2025-11-11)
### Bug Fixes
* Remove implicit FSharp.Core, Lib and Nuget for better CPM compat ([f8ab41b](https://gitlab.com/oceanbox/Poseidon/commit/f8ab41bbda9957727de63970a80f3c69e8715098))
# [1.30.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.29.0...v1.30.0) (2025-11-10)
### Bug Fixes
* don't fetch tz contour on update timeframe ([beedeb8](https://gitlab.com/oceanbox/Poseidon/commit/beedeb836b4353a5253362c11c3f772f65ad503d))
* switch to element path and use haversine distance ([61b4323](https://gitlab.com/oceanbox/Poseidon/commit/61b432380310a9cbc5c1383854d71055034a49dd))
### Features
* add Chaikin curve smoothing algorithm ([d4766f2](https://gitlab.com/oceanbox/Poseidon/commit/d4766f249b2ec1238a7df61df1a8fd3f3525f797))
* sea distance circle, closing [#73](https://gitlab.com/oceanbox/Poseidon/issues/73) ([fc41c91](https://gitlab.com/oceanbox/Poseidon/commit/fc41c91d41ee35036ca6482e0f5128a51e175d7f))
# [1.29.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.28.0...v1.29.0) (2025-11-06)
### Bug Fixes
* move map type picker to the usual place ([917058b](https://gitlab.com/oceanbox/Poseidon/commit/917058b7be861a674a975f0ba939f5a227c11cf9))
* naming ([5be346e](https://gitlab.com/oceanbox/Poseidon/commit/5be346e0fc7473640f2506a94e81f9e28a95b0c2))
* switch to node path and use lonlat coord in api ([d8f38c4](https://gitlab.com/oceanbox/Poseidon/commit/d8f38c496d8fefb575157082ea1d6fc8f03fe806))
* update fvcomkit ([da6a199](https://gitlab.com/oceanbox/Poseidon/commit/da6a1995c11702fe29965e77abfa7da525ff9e9c))
### Features
* calculation of sea distance, closes [#51](https://gitlab.com/oceanbox/Poseidon/issues/51) ([1b4f4ef](https://gitlab.com/oceanbox/Poseidon/commit/1b4f4ef3606354e09d5c20f8413b939291c6a78d))
# [1.28.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.27.2...v1.28.0) (2025-11-05)
### Bug Fixes
* Don't build Sorcerer Client ([a627b08](https://gitlab.com/oceanbox/Poseidon/commit/a627b08df2fc02e28db105068a9766be5bf6ac10))
### Features
* Centralize Nuget Package Management ([b062f66](https://gitlab.com/oceanbox/Poseidon/commit/b062f66cf90639ec3cccdbcf54826a622f28bb08))
## [1.27.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.27.1...v1.27.2) (2025-11-03)
### Bug Fixes
* cage interaction matrix and labeling, closes [#72](https://gitlab.com/oceanbox/Poseidon/issues/72) ([6d5a724](https://gitlab.com/oceanbox/Poseidon/commit/6d5a72412ba144cb9d948ba0cfdb1d464747e41f))
## [1.27.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.27.0...v1.27.1) (2025-10-31)
### Bug Fixes
* **Atlantis:** add sanity check for sim names, closes [#66](https://gitlab.com/oceanbox/Poseidon/issues/66) ([637d22e](https://gitlab.com/oceanbox/Poseidon/commit/637d22e0f094042504b50922932da5d1786f0cfe))
* **Atlantis:** minor tweaks in ui naming and layout ([187a5e3](https://gitlab.com/oceanbox/Poseidon/commit/187a5e36e3bd28bf73dde4eaa1634df4fecb5ce1))
* **Atlantis:** switch to chart-trend icon ([9b6a3ef](https://gitlab.com/oceanbox/Poseidon/commit/9b6a3ef1334eb0e2f74c1e398f3873a7095e5d43))
* **Atlantis:** use Conc instead of activeLayer ([039df74](https://gitlab.com/oceanbox/Poseidon/commit/039df744d3901e3e476c53db2a39daaae0d93855))
* **Atlantis:** use tab for statistics ([3a9c616](https://gitlab.com/oceanbox/Poseidon/commit/3a9c616f6b29df39fc679126036a79d76a4c2909))
# [1.27.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.26.4...v1.27.0) (2025-10-27)
### Bug Fixes
* **Atlantis:** fix buttons on sim setup ([c6eee07](https://gitlab.com/oceanbox/Poseidon/commit/c6eee0731705254bd32b68de8ceac7b6a37a5cc6))
* **Atlantis:** fix style in point samples section ([e85a114](https://gitlab.com/oceanbox/Poseidon/commit/e85a114a234006c7aa4456ebf383f2dfbb884b65))
* **Atlantis:** read sample radius from string ([0bd66f8](https://gitlab.com/oceanbox/Poseidon/commit/0bd66f89fd8539a323a3fcc34aca7a6620b26370))
* **Mapster:** Do not disable timeline when not in OceanControls ([aaa5627](https://gitlab.com/oceanbox/Poseidon/commit/aaa56271b0e97132550eb687725d39174d7446b5))
### Features
* **Atlantis:** add alternative unit for downwelling, closes [#47](https://gitlab.com/oceanbox/Poseidon/issues/47) ([234cd19](https://gitlab.com/oceanbox/Poseidon/commit/234cd19af7021c45cf74e6792b38784f27ca981e))
* **Atlantis:** introduce average point values within circle ([a7cb34d](https://gitlab.com/oceanbox/Poseidon/commit/a7cb34d7bad0fcb76b67a36c6292e5d65b878448))
* **Atlantis:** time series from point samples, closes [#29](https://gitlab.com/oceanbox/Poseidon/issues/29) ([1d1ddf2](https://gitlab.com/oceanbox/Poseidon/commit/1d1ddf2fef4d8687a1eb9652538e9d2fa2c1a40f))
* **Hipster:** add slurm account and comment ([40414db](https://gitlab.com/oceanbox/Poseidon/commit/40414db1f6976fcfd87e55e60b57feec0189e9cf))
## [1.26.4](https://gitlab.com/oceanbox/Poseidon/compare/v1.26.3...v1.26.4) (2025-10-27)
### Bug Fixes
* Create TMP directory for atlantis and add errorhandler ([0473f7b](https://gitlab.com/oceanbox/Poseidon/commit/0473f7b7656accd8bc493fd12908c2dd4a59a1b9))
## [1.26.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.26.2...v1.26.3) (2025-10-27)
### Bug Fixes
* **Atlas:** Fix top nav logo offset ([c5b85e9](https://gitlab.com/oceanbox/Poseidon/commit/c5b85e9ad44d02edda765ce7a8b6f314adaea669))
* **Atlas:** Show loading when waiting for model area archives ([9181b43](https://gitlab.com/oceanbox/Poseidon/commit/9181b43732151fae37f0a837d946458b6544103d))
* **Mapster:** Add back stats download button ([86425a2](https://gitlab.com/oceanbox/Poseidon/commit/86425a2292ea413bc9a6b24aab2105c9e85fade1))
* **Mapster:** Add timeseries help text on stats ([0dbd11e](https://gitlab.com/oceanbox/Poseidon/commit/0dbd11ed68991a13d94f8a3053794751b5185bc4))
* **Mapster:** Catch potential timeout error fetching time series ([214fb65](https://gitlab.com/oceanbox/Poseidon/commit/214fb650c1c06f5fbb9e42d922fb352833f3efec))
* **Mapster:** Choose better warn condition for missing post drift ([17181f6](https://gitlab.com/oceanbox/Poseidon/commit/17181f6ca2d5f6a23a8d5f7f8853b1a64c670324))
* **Mapster:** Fix plotly chart not auto ranging properly ([c862cb1](https://gitlab.com/oceanbox/Poseidon/commit/c862cb172fd24dbb3cb071e0e223665804965ed3))
* **Mapster:** Handle grid search case for sub grids/models ([a046e7d](https://gitlab.com/oceanbox/Poseidon/commit/a046e7d84957cdc31d047b397ddd9d9470fdb847))
* **Mapster:** Handle probing errors and show toast ([db37040](https://gitlab.com/oceanbox/Poseidon/commit/db370402090fc78206c31a48347338624fdabbed))
* **Mapster:** Make loading clearer when loading ([215409c](https://gitlab.com/oceanbox/Poseidon/commit/215409c7c880e2ca48e8df58e591cbb3f385000c))
* **Mapster:** Make notifier list an array ([3b4ec02](https://gitlab.com/oceanbox/Poseidon/commit/3b4ec021af8f1efff3f994a8ce912d8780eb966c))
* **Mapster:** More sane stat selection in depth plots ([fa63ade](https://gitlab.com/oceanbox/Poseidon/commit/fa63ade2e1a974cc4cbf2c40b67b999b2040a1af))
* **Mapster:** Redirect to /signin if entering map unauthenticated ([7176919](https://gitlab.com/oceanbox/Poseidon/commit/7176919e944fab87f7081887077a25c7ec2a5f33))
* **Mapster:** Remove Esc listener from side nav stats controls ([59ce5b9](https://gitlab.com/oceanbox/Poseidon/commit/59ce5b98b83bd53dd47e0a7666fedcaf5b8bad20))
* **Mapster:** Show warning when non-transport sims are missing post ([9bd0562](https://gitlab.com/oceanbox/Poseidon/commit/9bd0562b47eac0a36338ca0ce479b9207f142a67))
* **Mapster:** Sidebar styling updates ([f7fbc5d](https://gitlab.com/oceanbox/Poseidon/commit/f7fbc5d70dc277f2f9bb9df2fa020b9b49a06416))
* **Mapster:** Update field probing data extraction view ([9eed6af](https://gitlab.com/oceanbox/Poseidon/commit/9eed6af711e570fb4b7079686a7e01a592c3f383))
* Use custom plotly bundle in react-plotly.js ([51fc750](https://gitlab.com/oceanbox/Poseidon/commit/51fc7503b5fae6c05c233888ece06e314f0555a3))
## [1.26.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.26.1...v1.26.2) (2025-10-27)
### Bug Fixes
* **tmp:** Add prints to debug ([b5c21bb](https://gitlab.com/oceanbox/Poseidon/commit/b5c21bb62c31c98e21da7a670eda92e1b5c9d02d))
## [1.26.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.26.0...v1.26.1) (2025-10-27)
### Bug Fixes
* Don't use nodejs in dev ([90bab5e](https://gitlab.com/oceanbox/Poseidon/commit/90bab5ecc0d095f494bb3c65c16c5ad5a36cc8a8)), closes [oceanbox/Poseidon#1](https://gitlab.com/oceanbox/Poseidon/issues/1)
* Typo and build archivist with netcdf runtimeDeps ([8482921](https://gitlab.com/oceanbox/Poseidon/commit/84829214204828cbf1fed6f54f4055dc9d456561))
# [1.26.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.25.5...v1.26.0) (2025-10-23)
### Features
* **fga:** Add start_time to ReadChanges ([9388d37](https://gitlab.com/oceanbox/Poseidon/commit/9388d3758dc769106c2413686e59821352e74967))
## [1.25.5](https://gitlab.com/oceanbox/Poseidon/compare/v1.25.4...v1.25.5) (2025-10-22)
### Bug Fixes
* **nix:** Build Archivist SIF in pipeline ([16f9686](https://gitlab.com/oceanbox/Poseidon/commit/16f968657b61cb6ce6cecb12d8016565a25919fc))
## [1.25.4](https://gitlab.com/oceanbox/Poseidon/compare/v1.25.3...v1.25.4) (2025-10-15)
### Bug Fixes
* reduce number of cpus per drifter job ([b293044](https://gitlab.com/oceanbox/Poseidon/commit/b293044258ecc9699e62205181e8963f8470d1c2))
## [1.25.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.25.2...v1.25.3) (2025-10-15)
### Bug Fixes
* **Atlantis:** set limitations on simulation parameters ([407a278](https://gitlab.com/oceanbox/Poseidon/commit/407a278e283144ef2d163108c63e8b2a586b560c))
## [1.25.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.25.1...v1.25.2) (2025-10-15)
### Bug Fixes
* Send Drifters to Inbox instantly and Track Plumes ([48b5da9](https://gitlab.com/oceanbox/Poseidon/commit/48b5da9da3f3fcffc57592ea66f3a3d3c8828761))
* Submit as queued if there is a queue ([d64ee45](https://gitlab.com/oceanbox/Poseidon/commit/d64ee45d9e82da86c6ca348d36169dbbdb363edd))
## [1.25.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.25.0...v1.25.1) (2025-10-12)
### Bug Fixes
* Cleanup ([f721077](https://gitlab.com/oceanbox/Poseidon/commit/f72107751f0e81c1685a7b07ef5c22d512e5c031))
* Release ([cb6265d](https://gitlab.com/oceanbox/Poseidon/commit/cb6265dcb1c2458eb69098be61df77afdd85ef32))
# [1.25.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.24.5...v1.25.0) (2025-10-10)
### Bug Fixes
* **Atlantis:** avoid positive release depth, closes [#42](https://gitlab.com/oceanbox/Poseidon/issues/42) ([16643c3](https://gitlab.com/oceanbox/Poseidon/commit/16643c346033b6d3e134152a2bf6f79bbfd45ca3))
* **Atlantis:** clear point samples on deselect ([8174354](https://gitlab.com/oceanbox/Poseidon/commit/81743540d053dbc789cda41fdbc9b716decda53e))
### Features
* **Atlantis:** add point samples to any sim type, closes [#54](https://gitlab.com/oceanbox/Poseidon/issues/54) ([9a41f0a](https://gitlab.com/oceanbox/Poseidon/commit/9a41f0a2f3c804d5d6728c1d28020c51e61d060f))
* **Atlantis:** add toggle button for showing releases, closes [#53](https://gitlab.com/oceanbox/Poseidon/issues/53) ([f8045f6](https://gitlab.com/oceanbox/Poseidon/commit/f8045f6dcc46a1ba0de33e9092db87f798fb6255))
* **Atlantis:** copy release sites, closes [#56](https://gitlab.com/oceanbox/Poseidon/issues/56) ([9bb9d85](https://gitlab.com/oceanbox/Poseidon/commit/9bb9d855e4f93f07e634b1f7e499feb7136643f1))
* **Atlantis:** fetch time series data from plot, closes [#55](https://gitlab.com/oceanbox/Poseidon/issues/55) ([a332fe7](https://gitlab.com/oceanbox/Poseidon/commit/a332fe72a2c745ffe8ed45598c6a9ab8f3391cb0))
## [1.24.5](https://gitlab.com/oceanbox/Poseidon/compare/v1.24.4...v1.24.5) (2025-10-10)
### Bug Fixes
* **mapster:** Set static artifact for plume downloads ([5476583](https://gitlab.com/oceanbox/Poseidon/commit/5476583cb2fde0beed37b6235144532c6234432c))
## [1.24.4](https://gitlab.com/oceanbox/Poseidon/compare/v1.24.3...v1.24.4) (2025-10-03)
### Bug Fixes
* broken vite version ([5e84185](https://gitlab.com/oceanbox/Poseidon/commit/5e8418595046d7685eb405c36953f7b888650aea))
* update drifters.api version ([22339c6](https://gitlab.com/oceanbox/Poseidon/commit/22339c6adebe584254b6c1a5c52804ce970435d5))
## [1.24.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.24.2...v1.24.3) (2025-10-02)
### Bug Fixes
* Remove client from archivist cli and use correct path ([60a47b0](https://gitlab.com/oceanbox/Poseidon/commit/60a47b030801c6fc36f3d7599455624058e9c075))
## [1.24.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.24.1...v1.24.2) (2025-10-02)
### Bug Fixes
* remove tz contours on deselect ([a5b348e](https://gitlab.com/oceanbox/Poseidon/commit/a5b348e439c33ff7367ae299f8e5afed87b2be8c))
## [1.24.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.24.0...v1.24.1) (2025-10-02)
### Bug Fixes
* Update lockfiles ([904845f](https://gitlab.com/oceanbox/Poseidon/commit/904845fe0aeb1b1daaa75f785205ad4242eb4f05))
# [1.24.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.23.3...v1.24.0) (2025-10-02)
### Bug Fixes
* **Atlantis:** sync import of sp-picker ([6151eb8](https://gitlab.com/oceanbox/Poseidon/commit/6151eb86f0ce9bcc013fa5f12c3713035e39cc7d))
* Build Archivist with correct archmaester interface ([d182900](https://gitlab.com/oceanbox/Poseidon/commit/d182900b028549fcf443c841ba8aad0adec57a19))
### Features
* **Atlantis:** add multiple transition zone contours, closes [#34](https://gitlab.com/oceanbox/Poseidon/issues/34) ([c75d137](https://gitlab.com/oceanbox/Poseidon/commit/c75d13711ae681b428cf177e7463888ac9a997f3))
* **Atlantis:** first take on cage interaction matrix, closes [#33](https://gitlab.com/oceanbox/Poseidon/issues/33) ([9ea31be](https://gitlab.com/oceanbox/Poseidon/commit/9ea31be3329afca6f29d869c95251c64e61bb6b4))
* **Atlantis:** point values section to sedimentation fields, closes [#43](https://gitlab.com/oceanbox/Poseidon/issues/43) ([7fff70b](https://gitlab.com/oceanbox/Poseidon/commit/7fff70b7d5e9f01303d6036f0e299ce92d44950a))
* **Atlantis:** separate groups in accumulate, closes [#44](https://gitlab.com/oceanbox/Poseidon/issues/44) ([bda39de](https://gitlab.com/oceanbox/Poseidon/commit/bda39dea78edc85f273ab471a12ae24d2b6c9485))
* **Sorcerer:** add api for field2d contours ([598fd5a](https://gitlab.com/oceanbox/Poseidon/commit/598fd5a7b05fc169b14ead197c51e00385c01f06))
## [1.23.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.23.2...v1.23.3) (2025-09-29)
### Bug Fixes
* **Atlantis:** Go back to how secrets were fetched ([ea335bc](https://gitlab.com/oceanbox/Poseidon/commit/ea335bc3fb1ca68ef2e23247ec17baac362fee4f))
## [1.23.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.23.1...v1.23.2) (2025-09-29)
### Bug Fixes
* **Atlantis:** Ensure dapr is healthy before running ([e344647](https://gitlab.com/oceanbox/Poseidon/commit/e34464782ab95613f786c24a139be0ecbf97a043))
* **Atlantis:** Use correct DI settings type in FgaActor ([be1f942](https://gitlab.com/oceanbox/Poseidon/commit/be1f942413b78f288fe26a042fa53ac1d482eda9))
## [1.23.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.23.0...v1.23.1) (2025-09-27)
### Bug Fixes
* **Atlantis:** Overwrite slurm token with value from secrets ([94c8acc](https://gitlab.com/oceanbox/Poseidon/commit/94c8accf45b44bf9840c8165a2668d3ba948c696))
# [1.23.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.22.8...v1.23.0) (2025-09-27)
### Bug Fixes
* add slurm-access-token stub to tilt ([190b227](https://gitlab.com/oceanbox/Poseidon/commit/190b227d97880f310e14d6459b6e9b0e18f3715a))
* change slurm secret token key to SLURM_JWT ([b4c8de2](https://gitlab.com/oceanbox/Poseidon/commit/b4c8de2e09b7a6676f96dbeee520496c13d8059a))
### Features
* move slurmrestd from basic auth to jwt ([0922a8f](https://gitlab.com/oceanbox/Poseidon/commit/0922a8fa08c81e967edf03fdedb9dfba77b2ee8a))
## [1.22.8](https://gitlab.com/oceanbox/Poseidon/compare/v1.22.7...v1.22.8) (2025-09-22)
### Bug Fixes
* **dataagent:** Add privateassets and bump api to match version ([93c1661](https://gitlab.com/oceanbox/Poseidon/commit/93c166144eb0b411aa147dc4e935aa560de2badd))
## [1.22.7](https://gitlab.com/oceanbox/Poseidon/compare/v1.22.6...v1.22.7) (2025-09-20)
### Bug Fixes
* **dataagent:** Actual bump in version ([b9b9720](https://gitlab.com/oceanbox/Poseidon/commit/b9b97205c27c1d9a9d1f50f1c7c7d86b2d9e4eba))
## [1.22.6](https://gitlab.com/oceanbox/Poseidon/compare/v1.22.5...v1.22.6) (2025-09-19)
### Bug Fixes
* **dataagent:** Add back weird Targets ([0586f2f](https://gitlab.com/oceanbox/Poseidon/commit/0586f2f7d3c5dec1c3b8b8d7039f8214690238c2))
## [1.22.5](https://gitlab.com/oceanbox/Poseidon/compare/v1.22.4...v1.22.5) (2025-09-19)
### Bug Fixes
* **mapster:** Add authorization for plume usage ([7b90371](https://gitlab.com/oceanbox/Poseidon/commit/7b90371f849f92260d84bfd03f581799a7e86fb5))
## [1.22.4](https://gitlab.com/oceanbox/Poseidon/compare/v1.22.3...v1.22.4) (2025-09-16)
### Bug Fixes
* Add archivist binary to shell and allow dirs in add ([ebc12b9](https://gitlab.com/oceanbox/Poseidon/commit/ebc12b9ef9cfc483cdb4c0319ce08f761930a9f6))
* **Atlantis:** Remove heuristic caching tags instead of cache control ([f6ce5d2](https://gitlab.com/oceanbox/Poseidon/commit/f6ce5d26c383027b5363ab7804d766a057e60b28))
* Don't validate files in parser ([29eeb17](https://gitlab.com/oceanbox/Poseidon/commit/29eeb17306ab6b44e3aea6136601f259f98ab123))
## [1.22.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.22.2...v1.22.3) (2025-09-15)
### Bug Fixes
* Load agenix secrets in direnv ([b2d8dec](https://gitlab.com/oceanbox/Poseidon/commit/b2d8dec9a4be788f7c38dd26369ddd37f32fcd22))
## [1.22.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.22.1...v1.22.2) (2025-09-15)
### Bug Fixes
* **Umami:** Add some initial event tracking ([ace083a](https://gitlab.com/oceanbox/Poseidon/commit/ace083af9e7318a0eece2f7c88fd728607b3395e))
## [1.22.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.22.0...v1.22.1) (2025-09-12)
### Bug Fixes
* Typo rename URL -> WEB_ID ([2e260d1](https://gitlab.com/oceanbox/Poseidon/commit/2e260d118445f86b733bbe5068fd9d9adc7df207))
# [1.22.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.21.11...v1.22.0) (2025-09-12)
### Features
* **Atlantis:** Add user id to umami payload as unique id ([5a6c725](https://gitlab.com/oceanbox/Poseidon/commit/5a6c7258a49bbd91e8ddc9b0ccce715d34afed58))
## [1.21.11](https://gitlab.com/oceanbox/Poseidon/compare/v1.21.10...v1.21.11) (2025-09-11)
### Bug Fixes
* Change from plausible to umami ([c2a99ac](https://gitlab.com/oceanbox/Poseidon/commit/c2a99ac1eed9dbdafac52f163dc49a384ee7399a))
## [1.21.10](https://gitlab.com/oceanbox/Poseidon/compare/v1.21.9...v1.21.10) (2025-09-11)
### Bug Fixes
* **Atlantis:** Configure static files manually ([5402af7](https://gitlab.com/oceanbox/Poseidon/commit/5402af7f8f3f588131862b2c9a95d7469ed68561))
* **Atlantis:** Use correct barentswatch secret ([9cd9518](https://gitlab.com/oceanbox/Poseidon/commit/9cd9518a38912b2b50fd4e01ead348e92a7e1a40))
## [1.21.9](https://gitlab.com/oceanbox/Poseidon/compare/v1.21.8...v1.21.9) (2025-09-09)
### Bug Fixes
* Add no-cache header to served content ([1247d4d](https://gitlab.com/oceanbox/Poseidon/commit/1247d4d0923b2cce16c278012abdeb16b6e8e913))
* **Slurm:** Catch decode errors in handleSlurmEvents ([ad91a3d](https://gitlab.com/oceanbox/Poseidon/commit/ad91a3dd2339d9e9252eeb0ca82e1a37e205d5b4))
## [1.21.8](https://gitlab.com/oceanbox/Poseidon/compare/v1.21.7...v1.21.8) (2025-09-08)
### Bug Fixes
* Add closing bracket vite atlantis ([2c6a30a](https://gitlab.com/oceanbox/Poseidon/commit/2c6a30a4f6d5adad548443e4558e3fee1fda5545))
## [1.21.7](https://gitlab.com/oceanbox/Poseidon/compare/v1.21.6...v1.21.7) (2025-09-08)
### Bug Fixes
* Add hash to vite output artifacts ([8e72010](https://gitlab.com/oceanbox/Poseidon/commit/8e72010740d20bfb99ac7b635a5f679546d407ba))
## [1.21.6](https://gitlab.com/oceanbox/Poseidon/compare/v1.21.5...v1.21.6) (2025-09-05)
### Bug Fixes
* **Atlantis:** convert release span from sec to hr ([e7ae2ee](https://gitlab.com/oceanbox/Poseidon/commit/e7ae2ee38aa49fd5bfe4a3404aed7c8aeab7139e))
* **Mapster:** Redirect to Atlas if no archive is chosen ([047d747](https://gitlab.com/oceanbox/Poseidon/commit/047d74723ec7ec2710cbf4e219a80d7dcbd7926e))
* **Mapster:** Test session storage on loading map ([3a7df65](https://gitlab.com/oceanbox/Poseidon/commit/3a7df65f0a94440ec2fae401730cc2fa0bcd8937))
## [1.21.5](https://gitlab.com/oceanbox/Poseidon/compare/v1.21.4...v1.21.5) (2025-09-02)
### Bug Fixes
* Clean up package.json ([27df77b](https://gitlab.com/oceanbox/Poseidon/commit/27df77bfebcaa2efdd22f5057d5b811c209d9a53))
* Ignore buildkit builder ([6e31e77](https://gitlab.com/oceanbox/Poseidon/commit/6e31e77f0347c6b3a3953d043b0e0842f057ad8a))
## [1.21.4](https://gitlab.com/oceanbox/Poseidon/compare/v1.21.3...v1.21.4) (2025-09-02)
### Bug Fixes
* **Mapster:** Fix aquaculture locations not showing ([4806dd9](https://gitlab.com/oceanbox/Poseidon/commit/4806dd97de3ffbe2d683e8318d16a53db217e171))
## [1.21.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.21.2...v1.21.3) (2025-08-27)
### Bug Fixes
* Default to Debug and rename to env ([f441a15](https://gitlab.com/oceanbox/Poseidon/commit/f441a15f693c9beb0f01c17634a9bbc1a8addc9d))
## [1.21.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.21.1...v1.21.2) (2025-08-27)
### Bug Fixes
* Add Error propagation and diagnosticlogging ([b2cce31](https://gitlab.com/oceanbox/Poseidon/commit/b2cce311b5a2cea329e95a755d1db71d24876b76))
## [1.21.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.21.0...v1.21.1) (2025-08-27)
### Bug Fixes
* 4GB maxRequestBodySize in prod and update fsi file ([cee62b1](https://gitlab.com/oceanbox/Poseidon/commit/cee62b15e6b2e15fd86fd5f9f5e05aaa6a92d136))
# [1.21.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.20.2...v1.21.0) (2025-08-27)
## [1.20.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.20.1...v1.20.2) (2025-08-22)
### Bug Fixes
* **Mapster:** Fix ArchiveDialog table squeeze ([90b17a5](https://gitlab.com/oceanbox/Poseidon/commit/90b17a59a24aa5be03cf5fc168a63bf131f05bce))
* **Mapster:** Fix InboxDialog table squeeze ([124a900](https://gitlab.com/oceanbox/Poseidon/commit/124a900b4413a3526692d6d736feb0aab423f126))
## [1.20.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.20.0...v1.20.1) (2025-08-22)
### Bug Fixes
* Downgrade vite plugin for correct js bundle ([d35f666](https://gitlab.com/oceanbox/Poseidon/commit/d35f666664c740e7778a889d2a7be1f8f9f776ac))
# [1.20.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.19.4...v1.20.0) (2025-08-22)
### Features
* add custom depth separator in conc analysis ([62b09de](https://gitlab.com/oceanbox/Poseidon/commit/62b09deb8d64feeae3a242c39314c28b16842cc3))
## [1.19.4](https://gitlab.com/oceanbox/Poseidon/compare/v1.19.3...v1.19.4) (2025-08-21)
### Bug Fixes
* Don't run linter ([1780867](https://gitlab.com/oceanbox/Poseidon/commit/1780867a89244bbd7b6c095e477acbabb3fd4cc7))
* **nix:** Format, lint and remove dead code ([7d378a5](https://gitlab.com/oceanbox/Poseidon/commit/7d378a52d1e6f2293d6f3bcae9b0a97b23fb4d74))
## [1.19.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.19.2...v1.19.3) (2025-08-21)
### Bug Fixes
* Add fable temp files to gitignore ([8d9843d](https://gitlab.com/oceanbox/Poseidon/commit/8d9843d4679198c88bee8261e8cf5091f45280a1))
## [1.19.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.19.1...v1.19.2) (2025-08-20)
### Bug Fixes
* Add libnetcdf as runtime dependency for containers ([6e7fa4b](https://gitlab.com/oceanbox/Poseidon/commit/6e7fa4b9c702e1e7bb694443163693397293f27f))
* Add memorysize ([f626796](https://gitlab.com/oceanbox/Poseidon/commit/f6267962b34920288e3b625ea637890cd2e9a289))
* Include nix files for CI ([7983d94](https://gitlab.com/oceanbox/Poseidon/commit/7983d948f10e11461be91dc6a9259a3eb2749953))
## [1.19.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.19.0...v1.19.1) (2025-08-19)
### Bug Fixes
* **Atlas:** Fix squashed archive table in modal ([3593a44](https://gitlab.com/oceanbox/Poseidon/commit/3593a4494d332ae042c13e345fd0a0d59b46984e))
# [1.19.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.18.3...v1.19.0) (2025-08-19)
### Features
* Add OpenLayer FullScreen Controls ([2a3bb68](https://gitlab.com/oceanbox/Poseidon/commit/2a3bb68e3e1ffe57772c961c27b904d9023a5294))
## [1.18.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.18.2...v1.18.3) (2025-08-18)
### Bug Fixes
* Add .env to gitignore and generate server docs ([a37e602](https://gitlab.com/oceanbox/Poseidon/commit/a37e6021312f431fd33c435981ddfcafd47b890c))
## [1.18.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.18.1...v1.18.2) (2025-08-18)
### Bug Fixes
* **nix:** Add `debug` as argstr and match version with fsproj's ([82c43bb](https://gitlab.com/oceanbox/Poseidon/commit/82c43bb5f0db0e8e442cab97f049f1a0568ab2db))
## [1.18.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.18.0...v1.18.1) (2025-08-14)
### Bug Fixes
* Encrypt netrc with agenix and add to git ([4490ddf](https://gitlab.com/oceanbox/Poseidon/commit/4490ddf47c6f53de34317fcf6e0c97603785931c))
* Encrypt netrc with agenix and add to git ([68ef2f7](https://gitlab.com/oceanbox/Poseidon/commit/68ef2f7b7272af094376a8e3438f294812c4ca69))
* Encrypt netrc with agenix and add to git ([270e5c0](https://gitlab.com/oceanbox/Poseidon/commit/270e5c02fc3dfc904195a0c318c0453594d0d93f))
* fix loading of ssh keys ([842df2f](https://gitlab.com/oceanbox/Poseidon/commit/842df2fdb672c000b3f21ca2045a60329e9c2a61))
# [1.18.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.17.1...v1.18.0) (2025-08-12)
### Bug Fixes
* Cleanup ([786165c](https://gitlab.com/oceanbox/Poseidon/commit/786165c8452564944f743084cf0d1b23dda1b9cf))
* Drop netrc and add nupkg build ([d27cf41](https://gitlab.com/oceanbox/Poseidon/commit/d27cf4148758ea6db4d4697018f6c20a33608c6f))
* Revert tilt instances and add additional docs ([0402785](https://gitlab.com/oceanbox/Poseidon/commit/0402785fc7078e11f298635cf9a0e0cba47ecad1))
### Features
* Build with Nix ([ebe6b70](https://gitlab.com/oceanbox/Poseidon/commit/ebe6b7088310340587708811be18f211165b6d96))
* migrate to nix-actions and modernize build infrastructure ([27e54a7](https://gitlab.com/oceanbox/Poseidon/commit/27e54a7e1da571315b02bfa028461bfd82d144bc))
## [1.17.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.17.0...v1.17.1) (2025-08-07)
### Bug Fixes
* **Sorcerer:** remove duplicate release sites in transition zone ([33c28cc](https://gitlab.com/oceanbox/Poseidon/commit/33c28ccb7647cc13f6a2d29fb2508741796b12e9))
# [1.17.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.16.4...v1.17.0) (2025-08-06)
### Bug Fixes
* **Atlantis:** send correct aid for aze contour download ([43ff8c5](https://gitlab.com/oceanbox/Poseidon/commit/43ff8c52e4c93ddaba6ef83dd0dfda3ee12997ee))
### Features
* Rewrite Archivist CLI with Fargo ([fa063a9](https://gitlab.com/oceanbox/Poseidon/commit/fa063a9508f70421f53da0b028f0d107b1b2b841))
## [1.16.4](https://gitlab.com/oceanbox/Poseidon/compare/v1.16.3...v1.16.4) (2025-07-09)
### Bug Fixes
* Don't restore ([cb9d678](https://gitlab.com/oceanbox/Poseidon/commit/cb9d678bce9fe95b9f709682470abf12564e9b4d))
* Remove compiler check ([18c5399](https://gitlab.com/oceanbox/Poseidon/commit/18c5399625d47867fc2f265e514fcc66c74cb676))
## [1.16.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.16.2...v1.16.3) (2025-07-09)
### Bug Fixes
* Check for fable compiler in Dapr.Actor interfaces ([aa6c01c](https://gitlab.com/oceanbox/Poseidon/commit/aa6c01c5f27c7aa9c764c930426cb92c8f31b9f2))
* Plume cleanup ([0e080a7](https://gitlab.com/oceanbox/Poseidon/commit/0e080a70e194e3b54cc2179396e0662067db9cef))
* Use Ordinal StringComparison for fgaToken ([dbef031](https://gitlab.com/oceanbox/Poseidon/commit/dbef03187c7977666d40997828430a386868c1c6))
## [1.16.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.16.1...v1.16.2) (2025-07-08)
### Bug Fixes
* Don't restore before Release build ([c4134f8](https://gitlab.com/oceanbox/Poseidon/commit/c4134f8b2755ccfaebd33499b81e6ffb6af1093f))
## [1.16.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.16.0...v1.16.1) (2025-07-08)
### Bug Fixes
* Make inboxTypeFromString case insensitive ([fe3370f](https://gitlab.com/oceanbox/Poseidon/commit/fe3370f50f58c4b05a4b5016ba3a6a4a50bb6d1e))
* Remove unused code and bump slurmrestd ([a6c2c94](https://gitlab.com/oceanbox/Poseidon/commit/a6c2c949c2bbfec923aec551f180c88ead7fc071))
# [1.16.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.15.3...v1.16.0) (2025-07-07)
### Bug Fixes
* Add working parts of plume ([0e256e6](https://gitlab.com/oceanbox/Poseidon/commit/0e256e621894244f43f9bf4ba8cb78d2905d8815))
## [1.15.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.15.2...v1.15.3) (2025-07-03)
### Bug Fixes
* **atlantis:** disable probing on no stats ([a464a5a](https://gitlab.com/oceanbox/Poseidon/commit/a464a5ac06ea9420c963409f562a40b3da04b500))
* **plume:** adjust plume stop when changing start or duration ([13ac803](https://gitlab.com/oceanbox/Poseidon/commit/13ac8032690809dbd93b36b5a1e93a6eccd9d236))
* **plume:** allow cancel plume when running ([a400bf9](https://gitlab.com/oceanbox/Poseidon/commit/a400bf9e0bb8703f109bbc809dbabf08bec172c4))
* **plume:** notify on fail ([e69897f](https://gitlab.com/oceanbox/Poseidon/commit/e69897fc94d1011ecb4391c69f499f9856a8941a))
## [1.15.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.15.1...v1.15.2) (2025-07-03)
### Bug Fixes
* misc wonk to fix pipeline build ([fd78702](https://gitlab.com/oceanbox/Poseidon/commit/fd78702ab49016726723dfdb2a144f180c6e4c10))
## [1.15.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.15.0...v1.15.1) (2025-07-02)
### Bug Fixes
* Disable spinner after job is completed ([558c482](https://gitlab.com/oceanbox/Poseidon/commit/558c4829d903798e682233b07951f0ca8b78e9a2))
# [1.15.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.14.5...v1.15.0) (2025-07-02)
### Bug Fixes
* add missing fetch fields button for downwelling ([44f12a8](https://gitlab.com/oceanbox/Poseidon/commit/44f12a825d0f6d3bb7a6da4e661b84c2669a6d02))
* **atlantis:** show color bar on stats properties ([3046ec5](https://gitlab.com/oceanbox/Poseidon/commit/3046ec563d5456e46090673a4f91d5b51da83a92))
### Features
* Add Initial Plume UI ([1c14274](https://gitlab.com/oceanbox/Poseidon/commit/1c14274cdbdd9dbe940742d5116be5b1e32e9dff))
* add traits input for plume ([d603453](https://gitlab.com/oceanbox/Poseidon/commit/d60345339b871655836fa6254265d957371d1dc2))
* **plume:** add download button for plume result ([1cbd34c](https://gitlab.com/oceanbox/Poseidon/commit/1cbd34c1022db5f1a9b4233f88266f1b55262f74))
## [1.14.5](https://gitlab.com/oceanbox/Poseidon/compare/v1.14.4...v1.14.5) (2025-07-01)
### Bug Fixes
* add missing fetch fields button for downwelling ([2f39571](https://gitlab.com/oceanbox/Poseidon/commit/2f395712d5fd85d5769c4af3d0cf5855fb556ae6))
## [1.14.4](https://gitlab.com/oceanbox/Poseidon/compare/v1.14.3...v1.14.4) (2025-07-01)
### Bug Fixes
* **atlantis:** show color bar on stats properties ([59ac2aa](https://gitlab.com/oceanbox/Poseidon/commit/59ac2aa3c922d89fedb4133db98461e353b007e4))
## [1.14.3](https://gitlab.com/oceanbox/Poseidon/compare/v1.14.2...v1.14.3) (2025-06-30)
### Bug Fixes
* **sorcerer:** Only run sorcerer on c-x nodes ([41d792d](https://gitlab.com/oceanbox/Poseidon/commit/41d792dc2adafe7faa710bb1d6a271e415b03b84))
## [1.14.2](https://gitlab.com/oceanbox/Poseidon/compare/v1.14.1...v1.14.2) (2025-06-27)
### Bug Fixes
* **atlantis:** Replace plotly with plotly-basic-dist-min ([36e12f3](https://gitlab.com/oceanbox/Poseidon/commit/36e12f39cc9475f6cb8ab55f66522e9dce41bde7))
* **atlantis:** Switch to custom bundle ([ec87726](https://gitlab.com/oceanbox/Poseidon/commit/ec87726347552a009f89fd1d948a82e08280077a))
## [1.14.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.14.0...v1.14.1) (2025-06-25)
### Bug Fixes
* **atlantis:** multiple select archive management ([8bd0c71](https://gitlab.com/oceanbox/Poseidon/commit/8bd0c7146f609f26293692d6b2c9b9d80e8695a9))
# [1.14.0](https://gitlab.com/oceanbox/Poseidon/compare/v1.13.1...v1.14.0) (2025-06-23)
### Bug Fixes
* Allow sentry in tilt and prod ([cab6de5](https://gitlab.com/oceanbox/Poseidon/commit/cab6de560d1850e1344e85fbda6d548f47f8cbff))
* Build Catalog and Archivist Client ([7d34824](https://gitlab.com/oceanbox/Poseidon/commit/7d348248bb456446a63d54d9f4958f527dea4fb1))
* **sentry:** Only run in Prod/Staging ([712b7d2](https://gitlab.com/oceanbox/Poseidon/commit/712b7d2bf2db280a5403d4f790401933c5585b87))
### Features
* **atlantis:** Add Sentry to Client and Server ([39dbfa5](https://gitlab.com/oceanbox/Poseidon/commit/39dbfa5592ebf628b4318c65d0ab7673419c6bec))
* **atlantis:** Instrument server with sentry ([d47359b](https://gitlab.com/oceanbox/Poseidon/commit/d47359b84085eb10a843a288037ef3f97327e3ab))
* **sorcerer:** Instrument sorcerer server ([92d3583](https://gitlab.com/oceanbox/Poseidon/commit/92d3583e0b576a0ec3bfe43dd868920a5d5526bc))
## [1.13.1](https://gitlab.com/oceanbox/Poseidon/compare/v1.13.0...v1.13.1) (2025-06-22)

View File

@@ -1 +1 @@
1.13.1
1.40.5

View File

@@ -5,29 +5,22 @@ Stage: build
%files
. /build
# Install apt packages
%post
# Add keys and sources lists
apt-get update && apt-get install -y ca-certificates gnupg
mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
NODE_MAJOR=20
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
# Install node, 7zip, yarn, git, process tools
apt update \
&& apt install -y nodejs p7zip-full git procps ssh-client
&& apt install -y p7zip-full git procps ssh-client
# Clean up
apt autoremove -y \
&& apt clean -y \
&& rm -rf /var/lib/apt/lists/*
# Install dotnet tools
dotnet tool install fable -g
# Build application
%post
cd /build/src/Archivist
dotnet restore --force-evaluate
dotnet run bundle
Bootstrap: docker
From: mcr.microsoft.com/dotnet/runtime:9.0
Stage: runtime
@@ -39,7 +32,6 @@ Stage: runtime
apt update \
&& apt install -y libnetcdf-dev
# Clean up
apt autoremove -y \
&& apt clean -y \
&& rm -rf /var/lib/apt/lists/*
@@ -52,4 +44,4 @@ Stage: runtime
export ARCHMAESTER_AUTH="admin:en-to-tre-fire"
%runscript
exec /app/Archivist "$@"
exec /app/archivist "$@"

1148
bun.lock

File diff suppressed because it is too large Load Diff

69
default.nix Normal file
View File

@@ -0,0 +1,69 @@
{
sources ? import ./nix,
system ? builtins.currentSystem,
pkgs ? import sources.nixpkgs {
inherit system;
config = { };
overlays = [ ];
},
env ? "Debug",
nix-utils ? import sources.nix-utils { },
}:
let
version =
let
clean = str: str |> (pkgs.lib.removeSuffix "\n");
version = builtins.readFile ./VERSION;
in
clean version;
dotnet-sdk = pkgs.dotnetCorePackages.sdk_10_0;
dotnet-runtime = pkgs.dotnetCorePackages.aspnetcore_10_0;
deps = nix-utils.output.lib.nuget.deps;
# Usage: export NETRC="$(agenix -d netrc.age)" in `./nix/secrets`
netrcConfig = builtins.getEnv "NETRC";
scripts = import ./scripts { inherit pkgs; };
packages = import ./nix/packages {
inherit
env
deps
pkgs
version
dotnet-sdk
dotnet-runtime
;
inherit netrcConfig;
};
containers = pkgs.callPackage ./nix/containers.nix {
inherit (packages)
atlantis
sorcerer
atlantis-client
archivist
;
inherit
version
env
;
codex = packages.codex;
};
in
{
inherit packages;
inherit scripts;
# Expose atlantis as default packages
default = packages.atlantis;
# Docker and Singurlarity images
containers = containers;
checks = {
pre-commit = import ./nix/pre-commit.nix;
};
}

View File

@@ -1,6 +1,6 @@
{
"sdk": {
"version": "9.0.0",
"version": "10.0.100",
"rollForward": "latestMinor"
}
}

42
justfile Normal file
View File

@@ -0,0 +1,42 @@
# Poseidon build commands
# Install just: https://github.com/casey/just
#
# Sub-projects with justfiles:
# - Atlantis (src/Atlantis) - Server + Client application with Fable/Vite
# - ServerPack (src/ServerPack) - Server package library
# - DataAgent (src/DataAgent) - Data agent library
# - Interfaces (src/Interfaces) - API interfaces library
# - Archivist (src/Archivist) - CLI tool with client
# - Sorcerer (src/Sorcerer) - Server application with client
#
# Run 'just <project>' to see available commands for each project (e.g., 'just atlantis')
set dotenv-load
# Default recipe - show available commands
default:
@just --list
# Show available commands for Atlantis (src/Atlantis)
atlantis:
@just src/Atlantis/
# Show available commands for ServerPack (src/ServerPack)
serverpack:
@just src/ServerPack/
# Show available commands for DataAgent (src/DataAgent)
dataagent:
@just src/DataAgent/
# Show available commands for Interfaces (src/Interfaces)
interfaces:
@just src/Interfaces/
# Show available commands for Archivist (src/Archivist)
archivist:
@just src/Archivist/
# Show available commands for Sorcerer (src/Sorcerer)
sorcerer:
@just src/Sorcerer/

109
nix/README.md Normal file
View File

@@ -0,0 +1,109 @@
# Nix
This directory contains Nix expressions defining the packages, and containers used to run/build Poseidon.
## Directory Structure
```
nix/
├── packages/ # Individual Poseidon service packages
│ ├── default.nix # Entry point - builds all packages with dependencies
│ ├── serverpack.nix
│ ├── dataagent.nix
│ ├── atlantis.nix
│ ├── atlantis-client.nix
│ ├── atlantis-client.json # Client dependencies metadata
│ ├── atlantis-deps.json # Atlantis backend dependencies metadata
│ ├── sorcerer.nix
│ └── archivist.nix
├── secrets/ # Age encrypted files
│ ├── secrets.nix
│ └── *.age
├── containers.nix # Docker & Singularity container definitions
│ ├── atlantis
│ ├── sorcerer
│ └── archivist
└── pre-commit.nix # Pre-commit hooks for code quality
```
## Usage
Note: `nix-build` can be switched for `nom-build` for a pretty-printed output (using [Nix Output Monitor](https://github.com/maralorn/nix-output-monitor))
### Secrets
```bash
# To run nix-build
$ set -x NETRC $(agenix -d netrc.age)
```
Secrets used for development and production are stored as `*.age` files and decrypted/encrypted using agenix via ssh-keys.
```bash
# To encrypt
$ agenix -e name.age # And add to secrets.nix
# To decrypt
$ agenix -d name.age
```
In case a new key is added to the `secrets.nix` file we need to re-key the age files with:
```bash
$ agenix -r
```
### Building Individual Packages
```bash
# Build a specific service
$ nix-build -A packages.atlantis
$ nix-build -A packages.sorcerer
# Build all packages
$ nix-build -A packages
```
### Building Container Images
```bash
# Build Docker containers
$ nix-build -A containers.atlantis
$ nix-build -A containers.sorcerer
# Build Singularity container for HPC
$ nix-build -A containers.archivist
```
### Development Shell
```bash
# Enter development environment
$ nix-shell
# Or use a specific package's shell
$ nix-shell -A packages.atlantis
```
### Running Services
```bash
# Run Atlantis server
$ ./result/bin/atlantis
# Run Sorcerer API
$ ./result/bin/sorcerer
```
### Update dependencies
When updating the `npm` dependencies, the `outputHash` in `atlantis-client.nix` needs to be updated. Simply run
```bash
nix-build -A packages.atlantis-client # in the root
```
It will then fail on the wrong hash and provide the correct one.
#### Deterministic builds, vendor hashes, and lock files
Nix aims for deterministic (reproducible) builds. A key part of this is **fixed-output derivations (FODs)** such as `fetchurl`, `buildDotnetModule`s vendor step, `fetchgit`, etc. For any FOD, Nix requires a **content hash** up front. After the build/fetch runs, Nix verifies that the resulting outputs hash exactly matches what was declared; if it doesnt, the build fails with a “hash mismatch in fixed-output derivation” error. This protects you from drifting dependencies and ensures that CI and local builds use the exact same inputs.
#### Why vendor hashes need to be pinned
Language ecosystems resolve and download a lot of upstream content (Go modules, npm/yarn, cargo crates, vendored tarballs…). To make those fetches deterministic, Nix needs the **expected content hash**. For example, with `makeDerivation`, you must set `outputHash` so Nix knows what the fully-resolved module tree should hash to. If you bump a version or change dependencies, the **content changes** and the old hash becomes invalid—Nix will (correctly) refuse the build until you update the pinned hash.

102
nix/containers.nix Normal file
View File

@@ -0,0 +1,102 @@
{
env,
pkgs,
version,
atlantis,
atlantis-client,
sorcerer,
archivist,
codex,
}:
let
# Entrypoints
startArchivist = pkgs.writeScriptBin "entrypoint" ''
#!${pkgs.runtimeShell}
set -euo pipefail
exec archivist "$@"
'';
in
{
atlantis = pkgs.dockerTools.buildLayeredImage {
name = "Atlantis";
tag = version;
created = "now";
contents = [
atlantis
pkgs.dockerTools.caCertificates
]
++ pkgs.lib.optionals (env == "Debug") [
pkgs.busybox
pkgs.dockerTools.binSh
];
extraCommands = ''
mkdir -m 0777 tmp
mkdir -p ./app
cp -r ${atlantis}/lib/Atlantis/* ./app/
cp -r ${atlantis-client}/public ./app/
'';
config = {
cmd = [ "Server" ];
workingDir = "/app";
env = [
"SERVER_CONTENT_ROOT=/app/public"
];
};
};
sorcerer = pkgs.dockerTools.buildLayeredImage {
name = "Sorcerer";
tag = version;
created = "now";
contents = [
sorcerer
pkgs.dockerTools.caCertificates
]
++ pkgs.lib.optionals (env == "Debug") [
pkgs.busybox
pkgs.dockerTools.binSh
];
extraCommands = ''
mkdir -p ./app
cp -r ${sorcerer}/lib/Sorcerer/* ./app/
'';
config = {
cmd = [ "Sorcerer" ];
workingDir = "/app";
};
};
archivist = pkgs.dockerTools.buildLayeredImage {
name = "archivist";
tag = archivist.version;
created = "now";
contents = [
archivist
]
++ pkgs.lib.optionals (env == "Debug") [
pkgs.busybox
pkgs.dockerTools.binSh
];
compressor = "none";
extraCommands = ''
mkdir -p ./app
cp -r ${archivist}/lib/Archivist/* ./app/
'';
config = {
entrypoint = [ "${startArchivist}/bin/entrypoint" ];
workingDir = "/app";
env = [
"ARCHMAESTER_URL=https://maps.oceanbox.io"
"ARCHMAESTER_AUTH=admin:en-to-tre-fire"
];
};
};
codex = pkgs.callPackage ../src/Codex/container.nix { server = codex; };
}

249
nix/default.nix Normal file
View File

@@ -0,0 +1,249 @@
/*
This file is provided under the MIT licence:
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
# Generated by npins. Do not modify; will be overwritten regularly
let
# Backwards-compatibly make something that previously didn't take any arguments take some
# The function must return an attrset, and will unfortunately be eagerly evaluated
# Same thing, but it catches eval errors on the default argument so that one may still call it with other arguments
mkFunctor =
fn:
let
e = builtins.tryEval (fn { });
in
(if e.success then e.value else { error = fn { }; }) // { __functor = _self: fn; };
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295
range =
first: last: if first > last then [ ] else builtins.genList (n: first + n) (last - first + 1);
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257
stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1));
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269
stringAsChars = f: s: concatStrings (map f (stringToCharacters s));
concatStrings = builtins.concatStringsSep "";
# If the environment variable NPINS_OVERRIDE_${name} is set, then use
# the path directly as opposed to the fetched source.
# (Taken from Niv for compatibility)
mayOverride =
name: path:
let
envVarName = "NPINS_OVERRIDE_${saneName}";
saneName = stringAsChars (c: if (builtins.match "[a-zA-Z0-9]" c) == null then "_" else c) name;
ersatz = builtins.getEnv envVarName;
in
if ersatz == "" then
path
else
# this turns the string into an actual Nix path (for both absolute and
# relative paths)
builtins.trace "Overriding path of \"${name}\" with \"${ersatz}\" due to set \"${envVarName}\"" (
if builtins.substring 0 1 ersatz == "/" then
/. + ersatz
else
/. + builtins.getEnv "PWD" + "/${ersatz}"
);
mkSource =
name: spec:
{
pkgs ? null,
}:
assert spec ? type;
let
# Unify across builtin and pkgs fetchers.
# `fetchGit` requires a wrapper because of slight API differences.
fetchers =
if pkgs == null then
{
inherit (builtins) fetchTarball fetchurl;
# For some fucking reason, fetchGit has a different signature than the other builtin fetchers …
fetchGit = args: (builtins.fetchGit args).outPath;
}
else
{
fetchTarball =
{
url,
sha256,
}:
pkgs.fetchzip {
inherit url sha256;
extension = "tar";
};
inherit (pkgs) fetchurl;
fetchGit =
{
url,
submodules,
rev,
name,
narHash,
}:
pkgs.fetchgit {
inherit url rev name;
fetchSubmodules = submodules;
hash = narHash;
};
};
# Dispatch to the correct code path based on the type
path =
if spec.type == "Git" then
mkGitSource fetchers spec
else if spec.type == "GitRelease" then
mkGitSource fetchers spec
else if spec.type == "PyPi" then
mkPyPiSource fetchers spec
else if spec.type == "Channel" then
mkChannelSource fetchers spec
else if spec.type == "Tarball" then
mkTarballSource fetchers spec
else if spec.type == "Container" then
mkContainerSource pkgs spec
else
builtins.throw "Unknown source type ${spec.type}";
in
spec // { outPath = mayOverride name path; };
mkGitSource =
{
fetchTarball,
fetchGit,
...
}:
{
repository,
revision,
url ? null,
submodules,
hash,
...
}:
assert repository ? type;
# At the moment, either it is a plain git repository (which has an url), or it is a GitHub/GitLab repository
# In the latter case, there we will always be an url to the tarball
if url != null && !submodules then
fetchTarball {
inherit url;
sha256 = hash;
}
else
let
url =
if repository.type == "Git" then
repository.url
else if repository.type == "GitHub" then
"https://github.com/${repository.owner}/${repository.repo}.git"
else if repository.type == "GitLab" then
"${repository.server}/${repository.repo_path}.git"
else if repository.type == "Forgejo" then
"${repository.server}/${repository.owner}/${repository.repo}.git"
else
throw "Unrecognized repository type ${repository.type}";
urlToName =
url: rev:
let
matched = builtins.match "^.*/([^/]*)(\\.git)?$" url;
short = builtins.substring 0 7 rev;
appendShort = if (builtins.match "[a-f0-9]*" rev) != null then "-${short}" else "";
in
"${if matched == null then "source" else builtins.head matched}${appendShort}";
name = urlToName url revision;
in
fetchGit {
rev = revision;
narHash = hash;
inherit name submodules url;
};
mkPyPiSource =
{ fetchurl, ... }:
{
url,
hash,
...
}:
fetchurl {
inherit url;
sha256 = hash;
};
mkChannelSource =
{ fetchTarball, ... }:
{
url,
hash,
...
}:
fetchTarball {
inherit url;
sha256 = hash;
};
mkTarballSource =
{ fetchTarball, ... }:
{
url,
locked_url ? url,
hash,
...
}:
fetchTarball {
url = locked_url;
sha256 = hash;
};
mkContainerSource =
pkgs:
{
image_name,
image_tag,
image_digest,
...
}:
if pkgs == null then
builtins.throw "container sources require passing in a Nixpkgs value: https://github.com/andir/npins/blob/master/README.md#using-the-nixpkgs-fetchers"
else
pkgs.dockerTools.pullImage {
imageName = image_name;
imageDigest = image_digest;
finalImageTag = image_tag;
};
in
mkFunctor (
{
input ? ./sources.json,
}:
let
data =
if builtins.isPath input then
# while `readFile` will throw an error anyways if the path doesn't exist,
# we still need to check beforehand because *our* error can be caught but not the one from the builtin
# *piegames sighs*
if builtins.pathExists input then
builtins.fromJSON (builtins.readFile input)
else
throw "Input path ${toString input} does not exist"
else if builtins.isAttrs input then
input
else
throw "Unsupported input type ${builtins.typeOf input}, must be a path or an attrset";
version = data.version;
in
if version == 7 then
builtins.mapAttrs (name: spec: mkFunctor (mkSource name spec)) data.pins
else
throw "Unsupported format version ${toString version} in sources.json. Try running `npins upgrade`"
)

47
nix/packages/api.nix Normal file
View File

@@ -0,0 +1,47 @@
{
env,
deps,
pkgs,
netrcConfig,
nix-gitignore,
packageSources,
dotnet-sdk,
dotnet-runtime,
buildDotnetModule,
}:
let
src = nix-gitignore.gitignoreSource [ ] ../../.;
projectFile = "src/Interfaces/Api/Poseidon.Api.fsproj";
versionMatch = builtins.match ".*<Version>([^<]+)</Version>.*" (
builtins.readFile (../../. + "/${projectFile}")
);
version = builtins.head versionMatch;
in
buildDotnetModule rec {
inherit
dotnet-sdk
dotnet-runtime
version
src
projectFile
;
name = "Poseidon.Api";
pname = "Poseidon.Api";
nugetDeps = deps {
inherit
name
pkgs
netrcConfig
packageSources
;
lockfiles = [
../../src/Interfaces/Api/packages.lock.json
];
};
doCheck = false;
buildType = env;
# packNupkg = true;
# NOTE(mrtz): Can't package nuget without it
# [ref](https://github.com/dotnet/fsharp/issues/12320)
# dotnetFlags = "--property:TargetsForTfmSpecificContentInPackage=";
}

View File

@@ -0,0 +1,46 @@
{
env,
pkgs,
deps,
netrcConfig,
nix-gitignore,
packageSources,
dotnet-sdk,
buildDotnetModule,
}:
let
src = nix-gitignore.gitignoreSource [ ] ../../.;
projectFile = "src/Archivist/src/Cli/Archivist.fsproj";
versionMatch = builtins.match ".*<Version>([^<]+)</Version>.*" (
builtins.readFile (../../. + "/${projectFile}")
);
version = builtins.head versionMatch;
in
buildDotnetModule rec {
inherit
dotnet-sdk
version
src
projectFile
;
name = "Archivist";
pname = name;
dotnet-runtime = pkgs.dotnetCorePackages.runtime_10_0;
dotnetRestoreFlags = "--force-evaluate";
nugetDeps = deps {
inherit
name
pkgs
netrcConfig
packageSources
;
lockfiles = [
../../src/Archivist/src/Cli/packages.lock.json
];
};
runtimeDeps = [
pkgs.netcdf
];
buildType = env;
doCheck = false;
}

View File

@@ -0,0 +1,89 @@
{
lib,
bun,
pkgs,
deps,
fable,
version,
dotnet-sdk,
netrcConfig,
nodeModules,
nix-gitignore,
packageSources,
dotnet-runtime,
buildDotnetModule,
}:
let
root = ../../.;
src = nix-gitignore.gitignoreSource [ ] root;
# src = lib.cleanSource root;
pname = "Atlantis";
in
buildDotnetModule {
inherit dotnet-sdk dotnet-runtime;
inherit src version;
pname = "${pname}-Client";
projectFile = "src/Atlantis/src/Client/Client.fsproj";
dotnetRestoreFlags = "--force-evaluate";
# nugetDeps = ./atlantis-client.json; # nix-build -A packages.atlantis-client.fetch-deps && ./result src/Atlantis/nix/atlantis-client.json
nugetDeps = deps {
inherit
pkgs
netrcConfig
packageSources
;
name = "${pname}-Client";
lockfiles = [
../../src/Atlantis/src/Client/packages.lock.json
];
};
# Skip the default dotnet build since we're using Fable
dontDotnetBuild = true;
buildInputs = [
fable
bun
];
buildPhase = ''
runHook preBuild
export HOME=$TMPDIR
cp -r ${nodeModules}/node_modules ./.
chmod -R u+rw node_modules
chmod -R u+x node_modules/.bin
patchShebangs node_modules/.bin
export PATH="./node_modules/.bin:$PATH"
cd src/Atlantis/src/Client
# NOTE(mrtz): Uses fable from nixpkgs instead of dotnet (Could be out of sync). --MSBuildCracker
${lib.getExe fable} -e .jsx -o build
# Run vite from the Atlantis directory with proper config, always bundle for prod
${lib.getExe bun} ../../../../node_modules/.bin/vite build -c ../../vite.config.js --outDir dist/public --mode Production
runHook postBuild
'';
installPhase = ''
runHook preInstall
# Copy output (*.js, *.css and *.html) to `/public`.
mkdir -p $out/public
cp -r dist/public/* $out/public/
runHook postInstall
'';
dontFixup = true;
dontPatchELF = true;
dontStrip = true;
}

49
nix/packages/atlantis.nix Normal file
View File

@@ -0,0 +1,49 @@
{
env,
pkgs,
deps,
version,
netrcConfig,
nix-gitignore,
packageSources,
dotnet-sdk,
dotnet-runtime,
buildDotnetModule,
}:
buildDotnetModule rec {
inherit
version
dotnet-sdk
dotnet-runtime
;
name = "Atlantis";
pname = "Atlantis";
# NOTE(mrtz): Ensures reproducibility and reduces closure size,
# by filtering out irrelevant files and `.git` which changes between commits.
src = nix-gitignore.gitignoreSource [ ] ../..;
projectFile = "src/Atlantis/src/Server/Server.fsproj";
dotnetRestoreFlags = "--force-evaluate";
nugetDeps = deps {
inherit
name
pkgs
netrcConfig
packageSources
;
lockfiles = [
../../src/Atlantis/src/Server/packages.lock.json
../../src/DataAgent/src/Entity/packages.lock.json
];
};
runtimeDeps = [
pkgs.netcdf
];
# NOTE: Add back when we have tests
doCheck = false;
buildType = env;
# Copy `appsettings` for local build
postBuild = ''
mkdir -p $out/bin
cp src/Atlantis/src/Server/appsettings.json $out/bin/
'';
}

View File

@@ -0,0 +1,49 @@
{
pkgs,
deps,
netrcConfig,
nix-gitignore,
packageSources,
dotnet-sdk,
dotnet-runtime,
buildDotnetModule,
}:
let
src = nix-gitignore.gitignoreSource [ ] ../../.;
projectFile = "src/DataAgent/src/DataAgent/Oceanbox.DataAgent.fsproj";
versionMatch = builtins.match ".*<Version>([^<]+)</Version>.*" (
builtins.readFile (../../. + "/${projectFile}")
);
version = builtins.head versionMatch;
runtimeId = "linux-x64";
in
buildDotnetModule rec {
inherit
dotnet-sdk
dotnet-runtime
version
src
projectFile
runtimeId
;
name = "Oceanbox.DataAgent";
pname = "Oceanbox.DataAgent";
dotnetRestoreFlags = "--force-evaluate";
# NOTE(mrtz): Can't package nuget without it
# [ref](https://github.com/dotnet/fsharp/issues/12320)
nugetDeps = deps {
inherit
name
pkgs
netrcConfig
packageSources
;
lockfiles = [
../../src/DataAgent/src/Entity/packages.lock.json
../../src/DataAgent/src/DataAgent/packages.lock.json
];
};
doCheck = false;
# packNupkg = true;
# dotnetFlags = [ "--property:TargetsForTfmSpecificContentInPackage="];
}

113
nix/packages/default.nix Normal file
View File

@@ -0,0 +1,113 @@
{
env,
deps,
pkgs,
version,
dotnet-sdk,
dotnet-runtime,
netrcConfig ? null,
}:
let
# NOTE(mrtz): Gitlab Nuget Registry does not support groupwide fetches :/
packageSources = import ./sources.nix;
nodeModules = pkgs.callPackage ./node-modules.nix {};
codex-client = pkgs.callPackage ../../src/Codex/src/Client {
inherit
deps
dotnet-sdk
netrcConfig
nodeModules
packageSources
;
};
in
{
serverpack = pkgs.callPackage ./serverpack.nix {
inherit
deps
netrcConfig
dotnet-sdk
dotnet-runtime
packageSources
;
};
# NOTE(mrtz): It's acutally Oceanbox.DataAgent
archmaester = pkgs.callPackage ./dataagent.nix {
inherit
deps
netrcConfig
dotnet-sdk
dotnet-runtime
packageSources
;
};
# NOTE(mrtz): It's acutally Poseidon.Api
interfaces = pkgs.callPackage ./api.nix {
inherit
env
deps
netrcConfig
dotnet-sdk
dotnet-runtime
packageSources
;
};
atlantis = pkgs.callPackage ./atlantis.nix {
inherit
env
deps
netrcConfig
version
dotnet-sdk
dotnet-runtime
packageSources
;
};
sorcerer = pkgs.callPackage ./sorcerer.nix {
inherit
env
deps
netrcConfig
dotnet-sdk
dotnet-runtime
packageSources
;
};
archivist = pkgs.callPackage ./archivist.nix {
inherit
env
deps
netrcConfig
dotnet-sdk
packageSources
;
};
atlantis-client = pkgs.callPackage ./atlantis-client.nix {
inherit
deps
version
dotnet-sdk
netrcConfig
nodeModules
dotnet-runtime
packageSources
;
};
codex = pkgs.callPackage ../../src/Codex/src/Server {
inherit
deps
dotnet-sdk
netrcConfig
dotnet-runtime
packageSources
;
client = codex-client;
};
}

View File

@@ -0,0 +1,52 @@
{
bun,
stdenvNoCC,
nix-gitignore,
writableTmpDirAsHomeHook,
}:
stdenvNoCC.mkDerivation {
name = "node-modules";
nativeBuildInputs = [
bun
writableTmpDirAsHomeHook
];
src = nix-gitignore.gitignoreSource [ ] ../../.;
dontConfigure = true;
# Required else we get errors that our fixed-output derivation references store paths
dontFixup = true;
# Only install dependencies, don't build
buildPhase = ''
runHook preBuild
export BUN_INSTALL_CACHE_DIR=$(mktemp -d)
# Disable post-install scripts to avoid shebang issues
bun install \
--frozen-lockfile \
--ignore-scripts \
--backend copyfile \
--no-progress \
--force
runHook postBuild
'';
installPhase = ''
runHook preInstall
mkdir -p $out
cp -r node_modules $out/
runHook postInstall
'';
outputHashMode = "recursive";
outputHashAlgo = "sha256";
# NOTE: Empty this when a new dependency is added
outputHash = "sha256-bbCaGoZRE7vRuAS3eRyP8yHANYXBJVaHmuL99BAovjY=";
}

View File

@@ -0,0 +1,44 @@
{
pkgs,
deps,
netrcConfig,
nix-gitignore,
packageSources,
dotnet-sdk,
dotnet-runtime,
buildDotnetModule,
}:
let
src = nix-gitignore.gitignoreSource [ ] ../../.;
projectFile = "src/ServerPack/src/Oceanbox.ServerPack.fsproj";
versionMatch = builtins.match ".*<Version>([^<]+)</Version>.*" (
builtins.readFile (../../. + "/${projectFile}")
);
version = builtins.head versionMatch;
in
buildDotnetModule rec {
inherit
dotnet-sdk
dotnet-runtime
version
src
projectFile
;
name = "Oceanbox.ServerPack";
pname = "Oceanbox.ServerPack";
nugetDeps = deps {
inherit
name
pkgs
netrcConfig
packageSources
;
lockfiles = [
../../src/ServerPack/src/packages.lock.json
];
};
packNupkg = true;
# NOTE(mrtz): Can't package nuget without it
# [ref](https://github.com/dotnet/fsharp/issues/12320)
dotnetFlags = "--property:TargetsForTfmSpecificContentInPackage=";
}

50
nix/packages/sorcerer.nix Normal file
View File

@@ -0,0 +1,50 @@
{
env,
deps,
pkgs,
netrcConfig,
nix-gitignore,
packageSources,
dotnet-sdk,
dotnet-runtime,
buildDotnetModule,
}:
let
src = nix-gitignore.gitignoreSource [ ] ../../.;
projectFile = "src/Sorcerer/src/Server/Sorcerer.fsproj";
# NOTE(mrtz): Extra the version attribute from the fsharp project (XML)
versionMatch = builtins.match ".*<Version>([^<]+)</Version>.*" (
builtins.readFile (../../. + "/${projectFile}")
);
version = builtins.head versionMatch;
in
buildDotnetModule rec {
inherit
dotnet-sdk
dotnet-runtime
version
src
projectFile
;
name = "Sorcerer";
pname = name;
dotnetRestoreFlags = "--force-evaluate";
nugetDeps = deps {
inherit
name
pkgs
netrcConfig
packageSources
;
lockfiles = [
../../src/Sorcerer/src/Server/packages.lock.json
../../src/DataAgent/src/Entity/packages.lock.json
];
};
runtimeDeps = [
pkgs.netcdf
];
# TODO: Add back when we have tests
doCheck = false;
buildType = env;
}

19
nix/packages/sources.nix Normal file
View File

@@ -0,0 +1,19 @@
{
"Drifters.Api" = "https://gitlab.com/api/v4/projects/37086336/packages/nuget/download";
"Fable.Lit" = "https://gitlab.com/api/v4/projects/61744837/packages/nuget/download";
"Fable.Lit.Elmish" = "https://gitlab.com/api/v4/projects/61744837/packages/nuget/download";
"Fable.Lit.React" = "https://gitlab.com/api/v4/projects/61744837/packages/nuget/download";
"Fable.OpenLayers" = "https://gitlab.com/api/v4/projects/36202053/packages/nuget/download";
"Fable.SignalR" = "https://gitlab.com/api/v4/projects/40255650/packages/nuget/download";
"Fable.SignalR.AspNetCore" = "https://gitlab.com/api/v4/projects/40255650/packages/nuget/download";
"Fable.SignalR.Elmish" = "https://gitlab.com/api/v4/projects/40255650/packages/nuget/download";
"Fable.SignalR.Saturn" = "https://gitlab.com/api/v4/projects/40255650/packages/nuget/download";
"Fable.SignalR.Shared" = "https://gitlab.com/api/v4/projects/40255650/packages/nuget/download";
"Matplotlib.ColorMaps" = "https://gitlab.com/api/v4/projects/36675671/packages/nuget/download";
"Oceanbox.DataAgent" = "https://gitlab.com/api/v4/projects/37541600/packages/nuget/download";
"Oceanbox.FvcomKit" = "https://gitlab.com/api/v4/projects/35569541/packages/nuget/download";
"Oceanbox.ServerPack" = "https://gitlab.com/api/v4/projects/67427353/packages/nuget/download";
"ProjNet.FSharp" = "https://gitlab.com/api/v4/projects/35009572/packages/nuget/download";
"Oceanbox.SDSLite" = "https://gitlab.com/api/v4/projects/34025102/packages/nuget/download";
}

36
nix/pre-commit.nix Normal file
View File

@@ -0,0 +1,36 @@
let
sources = import ./default.nix;
pkgs = import sources.nixpkgs { };
pre-commit = import sources.pre-commit;
in
pre-commit.run {
src = ./.;
# NOTE: Do not run at pre-commit time
default_stages = [
"pre-push"
];
package = pkgs.prek;
hooks = {
nixfmt-rfc-style = {
enable = true;
package = pkgs.nixfmt-rfc-style;
};
deadnix = {
enable = true;
package = pkgs.deadnix;
};
# NOTE(mrtz): Does not work with |>
# statix = {
# enable = true;
# package = pkgs.statix;
# settings.ignore = [ "../nix/default.nix" ];
# };
# TODO(mrtz): Format manually for now
# fantomas = {
# enable = true;
# name = "fantomas";
# entry = "${pkgs.fantomas}/bin/fantomas src";
# files = "(\\.fs$)|(\\.fsx$)|(\\.fsi$)";
# };
};
}

34
nix/secrets/netrc.age Normal file
View File

@@ -0,0 +1,34 @@
age-encryption.org/v1
-> ssh-ed25519 7bzzBw +aTkCV7hy4aJfDYbuHWFLP/1fSit2IFavOB0g2U/7EA
w5Eu3FiX/4USe2HOXtbebv9tArwrgIvM60+N8vVFJOA
-> ssh-ed25519 NV/uBA ynrvtm8t5Nlgpg/fJKF6svts4CcwzQxubMWzin8LHlo
nDLJpmUsy2FX6lGWgf5p3cTQ0lMIoAOQkOIyzLhNOsA
-> ssh-ed25519 7au5uA uq0WLcauhAx55gBHewUrVHLoZ5l1wqqNLIyc5pijgSw
lQtu0SGM5gg+uSQkT2uVFenisKBZxYAuKkOjxodluNw
-> ssh-rsa mafBQw
lKWdqgtpUd8UUQYG2z+mFejTZKd3tjNnXuQX7A88oZbuwK0OS+e2Q60AEezH9/+z
za6huPKwJWBo1YP3nnREBINmRH0rSYvSSvhW+X01Mupb2xoBf5ba4FAevDEMXlZh
SfdduBF3X71dLEtrsZnwUmpNwcSKFzYHin1JugsWKiYfmiMJ0ZdIUHmneyimUgGx
BpRlTaQyf4n6joDgNX4XhhmPafSJ6FpEmQTEy9XLUNjWPlhKLIgswS97Xm0NHab8
wY6/ZsL1Um8008dcMeq+KjT6P7jGjifhwY97m4f8C/Pc5NZewNPPVo8l+X2F/uSd
5cHKZCLcwwIbFfhWaJD51E2o1mf7ZvS+CgdVkO09lqb4tVABQOvWVHxzj+nh7sP6
/LXCdzkVbopPI3XxNzn0PDIlx/mcCiv30rUqMR/9q3NS2p51YOF8FvniHesNusHE
Kkr5rTkfUqN/MOJ0an9o+wNL7g6HPvNR3BRcyuSc5qJJ+4ZDZcPyfngNP/YVe0BN
-> ssh-ed25519 m/eJnA JzmTs9RX/dF9p49AEKvS2e9xCSHPOWn2r7CHZ61Cd1s
X4/Hbazgs9H9kc25HFFxFVKMU3FKAr6hj30+SoA7gHU
-> ssh-rsa HJx+NA
L7otgs2FGW0/vMg+QoC/xK3wnD/l98cF6TVASE5NAHGwmm6CurtftoMnsQ7I6XUN
OP3gDYWPvSnDvGkhQC39KKtGZWpCeDepicrXabySfbjJb0qEKkwekh2D9SJCCNPQ
N3b0rL0IRuTEBkUCRyOxwIZ4jbBrfMF0TTIL6mdfg/PRIshvwb/fp3+1AFZqOxt5
pqHTku5vVHcNzfv4CdNkeksME10W1HccK/Jwyk41Cny4xoOSnlFEsP1bJAB6h5LX
MLmTkXMd0dwHTvLjSNbHsoOKh/a2m+LIAc70Bp4mgzv9x0gUUQcVObpJ6HZJTUvc
JqOSzcGukUdt7zZPWDA4Ty6w422JGpq5ZTX8kCV0wTBtsT4Ne1GeMOUVFYPaoFof
I4bQZeWnOnvqabN6mhLDx+aXSnHX07nHcvQS2c6vTH9qKIU7Ujopox2EqHClkyD6
d2qHwem9PvWHzFKfUq3u51MTeJvQSHkK+61fIUZdjMEEQhvd/ixTavzLjmi84X0X
XLgbJ46NEMwAWG03fys4Te9IEbXkW/gGJEC1gq4Ui9BTerfhC5F+2B51neVLm+xp
U6K4LtjKJStrCjZL/ZJ8AJHgBgeRzMfB69OanEGJsXDP5OOz21x7ToYx2FrDxLTi
4L0dY1WjiaI7yTeE99C08YaopjQRsGGiFfQ25ibc1kQ
--- 84bJaUdMhO93N0qltdsWhEhKZbCma9hK7sJpUUH1N+c
|Ðß3Å]„ÍKôsdð-<2D>Qwð•%K´D”öµj{Øá*Ç
Š=4VÍb3·^mÙcl˜'›¼ÏøúýŒÈ¶07 èƒ&AÒ·é)ZµÕÿ/ÝõoY ˆ6{c¡&ø¼®ž)*¥

28
nix/secrets/secrets.nix Normal file
View File

@@ -0,0 +1,28 @@
# Agenix secrets configuration
#
# This file defines which SSH public keys can decrypt secrets.
# To encrypt a secret:
# agenix -e netrc.age
#
# To decrypt a secret (for testing):
# agenix -d netrc.age
let
user-keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII77Aa2MFZMTha8PdkNg32UR8y6Hwb4R0aR9Ad9qifNq" # Moritz
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBksFS1H6QZNQsrmWSnWuvVVaOcjvPZ0CLisWCDDDtU/" # Moritz 2
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKiAS30ZO+wgfAqDE9Y7VhRunn2QszPHA5voUwo+fGOf" # Jonas
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDULdlLC8ZLu9qBZUYsjhpr6kv5RH4yPkekXQdD7prkqapyoptUkO1nOTDwy7ZsKDxmp9Zc6OtdhgoJbowhGW3VIZPmooWO8twcaYDpkxEBLUehY/n8SlAwBtiHJ4mTLLcynJMVrjmTQLF3FeWVof0Aqy6UtZceFpLp1eNkiHTCM3anwtb9+gfr91dX1YsAOqxqv7ooRDu5rCRUvOi4OvRowepyuBcCjeWpTkJHkC9WGxuESvDV3CySWkGC2fF2LHkAu6SFsFE39UA5ZHo0b1TK+AFqRFiBAb7ULmtuno1yxhpBxbozf8+Yyc7yLfMNCyBpL1ci7WnjKkghQv7yM1xN2XMJLpF56v0slSKMoAs7ThoIlmkRm/6o3NCChgu0pkpNg/YP6A3HfYiEDgChvA6rAHX6+to50L9xF3ajqk4BUzWd/sCk7Q5Op2lzj31L53Ryg8vMP8hjDjYcgEcCCsGOcjUVgcsmfC9LupwRIEz3aF14AWg66+3zAxVho8ozjes=" # Jonas 2
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKfgY468dPNpdXZCkD9jw1p2qA0+z56Wi/c1VYE+riki" # Stig
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC2tox0uyFGfU1zPNU6yAVSoGOUkeU959aiTMrqu1U9MCCOP2o4IhZIlRpZ08XVnUU/AhycCUF4HgGqdcco8oIVX0P0Cn83KJoD/DOqAiz+1VwIUUV1ylrRdNqCgf4wnmLni3sUPHJdQnuq57+pzDDjHMr9CcBL2KzOHD/QanfR+jZmv9K3OS5oDcWquSCziXkpbkWQURPactmtyzGK2FRRxONZgYrB8gRTDstlWQg/t6GHNVelzuJ7SEf+t8pk/S2e/XAvfZyRJhrVJ35iZKpmxkIn5v0g1Z+z0yX/KRSAPRtNg9uM44cmto77MFx7iFs0CuleL3zHvRvZYW1ZnsKAiP07UkEK87luMpkTzFr9CSHJGpgk1RZYA3qidQti44n6NU9YRNhzO4v+KQE6XDqO80gZCJboSXr3fnYn/QHpPXzK5JcZNWmClyMURYj10qv9So3Fh0o3LV5GThA6JgN874vUywUZanPEdn8ePBcAsjLRzA4YBGEuvJCc6FELSuY2s+/pFba8NXQvrOdJKSRC0g5USQFfaWDln4Q4zZ1G5z76p1u6GtRWxvakkUQ0fze9KAW7msxeKaw+B7uMtyvCL8V2zEE8WKFP1sNyYEe7Sgp3RVfym2VPMNTZVhEImfM/3D+WbzfoJztnJvFKXeeMCcne4G8swyef3o1s3b+CvQ==" # Simen
];
# TODO(mrtz): Add key for ekman & rossby
system-keys = [
];
all-keys = user-keys ++ system-keys;
in
{
"netrc.age".publicKeys = all-keys;
}

49
nix/sources.json Normal file
View File

@@ -0,0 +1,49 @@
{
"pins": {
"agenix": {
"type": "Git",
"repository": {
"type": "GitHub",
"owner": "ryantm",
"repo": "agenix"
},
"branch": "main",
"submodules": false,
"revision": "fcdea223397448d35d9b31f798479227e80183f6",
"url": "https://github.com/ryantm/agenix/archive/fcdea223397448d35d9b31f798479227e80183f6.tar.gz",
"hash": "sha256-wyT7Pl6tMFbFrs8Lk/TlEs81N6L+VSybPfiIgzU8lbQ="
},
"nix-utils": {
"type": "Git",
"repository": {
"type": "Git",
"url": "https://git.sr.ht/~mrtz/nix-utils"
},
"branch": "trunk",
"submodules": false,
"revision": "098f594425d2b9dde0657becad0f6498d074f8b3",
"url": null,
"hash": "sha256-y++BijM+FRkKDhVrL7YXZQiJ0DNVMiRN7yHf6QIXBUI="
},
"nixpkgs": {
"type": "Channel",
"name": "nixpkgs-unstable",
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.05pre930822.ed142ab1b3a0/nixexprs.tar.xz",
"hash": "sha256-XH6awru9NnBc/m+2YhRNT8r1PAKEiPGF3gs//F3ods0="
},
"pre-commit": {
"type": "Git",
"repository": {
"type": "GitHub",
"owner": "cachix",
"repo": "git-hooks.nix"
},
"branch": "master",
"submodules": false,
"revision": "a1ef738813b15cf8ec759bdff5761b027e3e1d23",
"url": "https://github.com/cachix/git-hooks.nix/archive/a1ef738813b15cf8ec759bdff5761b027e3e1d23.tar.gz",
"hash": "sha256-Efs3VUPelRduf3PpfPP2ovEB4CXT7vHf8W+xc49RL/U="
}
},
"version": 7
}

View File

@@ -1,74 +1,79 @@
{
"private": true,
"type": "module",
"scripts": {
"start": "vite src/Client",
"build": "vite build -p",
"test": "vite test/Client"
},
"version": "1.0.0",
"devDependencies": {
"@rollup/plugin-node-resolve": "^16.0.1",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/exec": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"@semantic-release/gitlab": "^13.2.6",
"@vitejs/plugin-react": "^4.5.2",
"@semantic-release/gitlab": "^13.2.8",
"@sentry/vite-plugin": "^4.3.0",
"@vitejs/plugin-react": "^5.0.4",
"rollup-plugin-scss": "^4.0.1",
"sass": "^1.89.2",
"semantic-release": "^24.2.5",
"sass": "^1.93.0",
"semantic-release": "^24.2.9",
"semantic-release-dotnet": "^1.0.0",
"vite": "^6.3.5",
"vite": "^7.1.9",
"vite-plugin-mkcert": "^1.17.8"
},
"dependencies": {
"@fluentui/react-components": "^9.72.9",
"@fluentui/react-datepicker-compat": "^0.6.20",
"@fluentui/react-calendar-compat": "^0.3.15",
"@fluentui/react-timepicker-compat": "^0.4.26",
"@fluentui-contrib/react-data-grid-react-window": "^1.4.2",
"@fluentui/react-icons": "^2.0.316",
"react-window": "^2.2.3",
"@fortawesome/fontawesome-free": "^6.7.2",
"@lit-labs/motion": "^1.0.8",
"@lit/context": "^1.1.5",
"@microsoft/signalr": "^8.0.7",
"@sentry/browser": "^8.55.0",
"@lit/context": "^1.1.6",
"@microsoft/signalr": "^8.0.17",
"@sentry/browser": "^9.46.0",
"@shoelace-style/shoelace": "^2.20.1",
"@spectrum-web-components/accordion": "^1.7.0",
"@spectrum-web-components/action-button": "^1.6.0",
"@spectrum-web-components/action-group": "^1.7.0",
"@spectrum-web-components/action-menu": "^1.7.0",
"@spectrum-web-components/button": "^1.6.0",
"@spectrum-web-components/card": "^1.7.0",
"@spectrum-web-components/checkbox": "^1.6.0",
"@spectrum-web-components/color-slider": "^1.7.0",
"@spectrum-web-components/dialog": "^1.7.0",
"@spectrum-web-components/divider": "^1.6.0",
"@spectrum-web-components/field-group": "^1.6.0",
"@spectrum-web-components/field-label": "^1.6.0",
"@spectrum-web-components/icon": "^1.6.0",
"@spectrum-web-components/menu": "^1.6.0",
"@spectrum-web-components/number-field": "^1.6.0",
"@spectrum-web-components/overlay": "^1.6.0",
"@spectrum-web-components/popover": "^1.6.0",
"@spectrum-web-components/progress-bar": "^1.7.0",
"@spectrum-web-components/progress-circle": "^1.6.0",
"@spectrum-web-components/radio": "^1.7.0",
"@spectrum-web-components/slider": "^1.7.0",
"@spectrum-web-components/split-view": "^1.7.0",
"@spectrum-web-components/status-light": "^1.7.0",
"@spectrum-web-components/styles": "^1.6.0",
"@spectrum-web-components/switch": "^1.7.0",
"@spectrum-web-components/table": "^1.7.0",
"@spectrum-web-components/tabs": "^1.6.0",
"@spectrum-web-components/theme": "^1.6.0",
"@spectrum-web-components/toast": "^1.7.0",
"@spectrum-web-components/tooltip": "^1.6.0",
"@spectrum-web-components/top-nav": "^1.7.0",
"@spectrum-web-components/underlay": "^1.6.0",
"@spectrum-web-components/accordion": "^1.8.0",
"@spectrum-web-components/action-button": "^1.8.0",
"@spectrum-web-components/action-group": "^1.8.0",
"@spectrum-web-components/action-menu": "^1.8.0",
"@spectrum-web-components/alert-banner": "^1.8.0",
"@spectrum-web-components/button": "^1.8.0",
"@spectrum-web-components/card": "^1.8.0",
"@spectrum-web-components/checkbox": "^1.8.0",
"@spectrum-web-components/color-slider": "^1.8.0",
"@spectrum-web-components/combobox": "^1.8.0",
"@spectrum-web-components/contextual-help": "^1.8.0",
"@spectrum-web-components/dialog": "^1.8.0",
"@spectrum-web-components/divider": "^1.8.0",
"@spectrum-web-components/field-group": "^1.8.0",
"@spectrum-web-components/field-label": "^1.8.0",
"@spectrum-web-components/icon": "^1.8.0",
"@spectrum-web-components/menu": "^1.8.0",
"@spectrum-web-components/number-field": "^1.8.0",
"@spectrum-web-components/overlay": "^1.8.0",
"@spectrum-web-components/popover": "^1.8.0",
"@spectrum-web-components/progress-bar": "^1.8.0",
"@spectrum-web-components/progress-circle": "^1.8.0",
"@spectrum-web-components/radio": "^1.8.0",
"@spectrum-web-components/slider": "^1.8.0",
"@spectrum-web-components/split-view": "^1.8.0",
"@spectrum-web-components/status-light": "^1.8.0",
"@spectrum-web-components/styles": "^1.8.0",
"@spectrum-web-components/switch": "^1.8.0",
"@spectrum-web-components/table": "^1.8.0",
"@spectrum-web-components/tabs": "^1.8.0",
"@spectrum-web-components/theme": "^1.8.0",
"@spectrum-web-components/toast": "^1.8.0",
"@spectrum-web-components/tooltip": "^1.8.0",
"@spectrum-web-components/top-nav": "^1.8.0",
"@spectrum-web-components/underlay": "^1.8.0",
"@turf/bezier-spline": "^7.2.0",
"@vaadin/login": "^24.8.0",
"lit": "^3.3.0",
"lit-html": "^3.3.0",
"ol": "^10.6.0",
"lit": "^3.3.1",
"lit-html": "^3.3.1",
"ol": "^10.6.1",
"plotly.js": "^2.35.3",
"proj4": "^2.17.0",
"proj4": "^2.19.10",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-plotly.js": "^2.6.0",
"vis-timeline": "^7.7.4"
"vis-timeline": "^8.4.0"
}
}
}

20
scripts/README.md Normal file
View File

@@ -0,0 +1,20 @@
# Scripts
Development helper scripts.
```shell
├── update-deps.sh
│ └─ Updates dependencies for the Poseidon project, including both .NET and npm.
├── configure-manifests.sh
│ └─ Configures Kubernetes manifests by replacing placeholders and applying them to a namespace.
├── get-barentswatch-token.sh
│ └─ Retrieves an authentication token from the Barentswatch API using client credentials.
├── init-namespace.sh
│ └─ Creates a Kubernetes namespace if it doesn't already exist.
├── run_lgtm.sh
│ └─ Runs the LGTM (Loki, Grafana, Tempo, Mimir) observability stack in Docker.
├── start-postgres.sh
│ └─ Starts a PostgreSQL development server using Docker with persistent volume.
└── trackFga.sh
└─ Toggles Fine-Grained Authorization tracking in the Archmaester service.
```

View File

@@ -1,8 +1,8 @@
#!/usr/bin/env bash
top="$(cd "$(dirname "$BASH_SOURCE[0]")" >/dev/null 2>&1 && pwd)"
top="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
if [ $# = 1 -o $# = 2 ]; then
if [ $# = 1 ] || [ $# = 2 ]; then
env=$1
ns=${2:-atlantis}
else
@@ -10,10 +10,10 @@ else
exit 1
fi
$top/init-namespace.sh $ns
"$top/init-namespace.sh" "$ns"
if [ -d $top/../manifests ]; then
for i in $top/../manifests/*.yaml; do
sed "s/<x>-/$env-/g" $i | kubectl apply -n $ns -f -
if [ -d "$top/../manifests" ]; then
for i in "$top"/../manifests/*.yaml; do
sed "s/<x>-/$env-/g" "$i" | kubectl apply -n "$ns" -f -
done
fi

56
scripts/default.nix Normal file
View File

@@ -0,0 +1,56 @@
{ pkgs }:
let
inherit (pkgs.lib) mapAttrs;
inherit (pkgs)
writeShellApplication
jaq
fd
ripgrep
coreutils
kubectl
curl
gnused
;
scripts = {
update-deps = [
jaq
coreutils
fd
ripgrep
];
configure-manifests = [
coreutils
kubectl
gnused
];
get-barentswatch-token = [
curl
];
init-namespace = [
kubectl
coreutils
];
run_lgtm = [
ripgrep
coreutils
];
start-postgres = [
coreutils
];
trackFga = [
curl
];
};
self = mapAttrs (
name: runtimeInputs:
writeShellApplication {
inherit name runtimeInputs;
text = builtins.readFile ./${name}.sh;
}
) scripts;
in
self

View File

@@ -3,7 +3,7 @@
ns=${1:-oceanbox}
init_namespace() {
kubectl create ns $ns
kubectl create ns "$ns"
}
kubectl get ns $ns > /dev/null 2>&1 || init_namespace
kubectl get ns "$ns" > /dev/null 2>&1 || init_namespace

View File

@@ -1,8 +1,6 @@
#!/bin/sh
docker ps 2>&1 | grep -q zipkin
if [ $? = 0 ]; then
if docker ps 2>&1 | grep -q zipkin; then
echo "Please stop the running Zipkin Docker instance and try again"
exit 1
fi

View File

@@ -2,8 +2,7 @@
echo "Starting postgresql server with docker"
docker volume inspect archmaester_dev > /dev/null 2>&1
if [ $? -ne 0 ]; then
if ! docker volume inspect archmaester_dev > /dev/null 2>&1; then
echo -n "Creating separate dev volume"
docker volume create archmaester_dev
fi

View File

@@ -8,4 +8,4 @@ esac
curl \
-H "Authorization: bearer $ARCHMAESTER_AUTH" \
-d "$toggle" \
https://$USER-atlantis.dev.oceanbox.io/internal/trackFga
"https://${USER}-atlantis.dev.oceanbox.io/internal/trackFga"

258
scripts/update-deps.sh Executable file
View File

@@ -0,0 +1,258 @@
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
shopt -s lastpipe
# Determine project root - handle both direct execution and nix result execution
if [[ "${BASH_SOURCE[0]}" == */result/bin/* ]]; then
# Running from nix result, find project root by looking for default.nix
PROJECT_ROOT="$(pwd)"
while [[ "$PROJECT_ROOT" != "/" && ! -f "$PROJECT_ROOT/default.nix" ]]; do
PROJECT_ROOT="$(dirname "$PROJECT_ROOT")"
done
if [[ ! -f "$PROJECT_ROOT/default.nix" ]]; then
echo "Error: Could not find project root with default.nix" >&2
exit 1
fi
else
# Running directly from scripts directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
fi
PACKAGES_DIR="$PROJECT_ROOT/nix/packages"
info() {
echo -e "\033[36m[INFO]\033[0m $*"
}
success() {
echo -e "\033[32m[SUCCESS]\033[0m $*"
}
error() {
echo -e "\033[31m[ERROR]\033[0m $*"
}
warn() {
echo -e "\033[33m[WARN]\033[0m $*"
}
declare -A PRIVATE_DEPS=(
["Drifters.Api"]="https://gitlab.com/api/v4/projects/37086336/packages/nuget/download/drifters.api"
["Fable.SignalR.AspNetCore"]="https://gitlab.com/api/v4/projects/40255650/packages/nuget/download/fable.signalr.aspnetcore"
["Fable.SignalR.Saturn"]="https://gitlab.com/api/v4/projects/40255650/packages/nuget/download/fable.signalr.saturn"
["Fable.SignalR.Shared"]="https://gitlab.com/api/v4/projects/40255650/packages/nuget/download/fable.signalr.shared"
["Fable.SignalR"]="https://gitlab.com/api/v4/projects/40255650/packages/nuget/download/fable.signalr"
["Fable.SignalR.Elmish"]="https://gitlab.com/api/v4/projects/40255650/packages/nuget/download/fable.signalr.elmish"
["Fable.Lit"]="https://gitlab.com/api/v4/projects/61744837/packages/nuget/download/fable.lit"
["Fable.Lit.Elmish"]="https://gitlab.com/api/v4/projects/61744837/packages/nuget/download/fable.lit.elmish"
["Fable.Lit.React"]="https://gitlab.com/api/v4/projects/61744837/packages/nuget/download/fable.lit.react"
["Fable.OpenLayers"]="https://gitlab.com/api/v4/projects/36202053/packages/nuget/download/fable.openlayers"
["Oceanbox.FvcomKit"]="https://gitlab.com/api/v4/projects/35569541/packages/nuget/download/oceanbox.fvcomkit"
["ProjNet.FSharp"]="https://gitlab.com/api/v4/projects/35009572/packages/nuget/download/projnet.fsharp"
["SDSLite.Oceanbox"]="https://gitlab.com/api/v4/projects/34025102/packages/nuget/download/sdslite.oceanbox"
["Matplotlib.ColorMaps"]="https://gitlab.com/api/v4/projects/36675671/packages/nuget/download/matplotlib.colormaps"
)
# Function to add private URLs to a .NET deps JSON file
add_private_urls() {
local json_file="$1"
local temp_file="${json_file}.tmp"
info "Processing private URLs for $json_file"
# NOTE: Thanks in large to Claude :) for this monstrosity
# shellcheck disable=SC2016
local jaq_filter='map(if $private_deps[.pname] then . + {"url": ($url_map[.pname] + "/" + (.version | ascii_downcase) + "/" + (.pname | ascii_downcase) + "." + (.version | ascii_downcase) + ".nupkg")} else . end)'
jaq --argjson private_deps "$(printf '%s\n' "${!PRIVATE_DEPS[@]}" | jaq -R . | jaq -s 'map({key: ., value: 1}) | from_entries')" \
--argjson url_map "$(for pkg in "${!PRIVATE_DEPS[@]}"; do
echo "{\"$pkg\": \"${PRIVATE_DEPS[$pkg]}\"}"
done | jaq -s 'add')" \
"$jaq_filter" "$json_file" > "$temp_file"
if [[ -s "$temp_file" ]]; then
mv "$temp_file" "$json_file"
success "Updated private URLs in $json_file"
else
error "Failed to update $json_file"
rm -f "$temp_file"
return 1
fi
}
# Function to update .NET dependencies
update_dotnet_deps() {
local package_name="$1"
local json_file="$2"
info "Updating .NET dependencies for $package_name"
cd "$PROJECT_ROOT"
# Run nix-build to generate new dependencies
if nix-build -A "packages.$package_name.fetch-deps" --no-out-link > /dev/null 2>&1; then
local result_path
result_path=$(nix-build -A "packages.$package_name.fetch-deps" --no-out-link)
# Run the result to update the JSON file
if "$result_path" "$json_file"; then
success "Generated new dependencies for $package_name"
# Add private URLs if this JSON file has any
if [[ -f "$json_file" ]]; then
add_private_urls "$json_file"
fi
else
error "Failed to generate dependencies for $package_name"
return 1
fi
else
error "Failed to build fetch-deps for $package_name"
return 1
fi
}
# Function to backup files
backup_files() {
local timestamp
timestamp=$(date +%Y%m%d_%H%M%S)
local backup_dir="$PROJECT_ROOT/.deps_backup_$timestamp"
info "Creating backup in $backup_dir"
mkdir -p "$backup_dir"
cp "$PACKAGES_DIR/atlantis-deps.json" "$backup_dir/" 2>/dev/null || true
cp "$PACKAGES_DIR/atlantis-client.json" "$backup_dir/" 2>/dev/null || true
success "Backup created in $backup_dir"
}
# Function to show usage
usage() {
cat << EOF
Usage: $0 [OPTIONS]
Update Poseidon project dependencies including .NET packages
OPTIONS:
-h, --help Show this help message
-b, --backup Create backup before updating (default: true)
--no-backup Skip creating backup
--atlantis-only Update only atlantis dependencies
--client-only Update only atlantis-client dependencies
--dry-run Show what would be updated without making changes
EXAMPLES:
$0 Update all dependencies
$0 --atlantis-only Update only atlantis server dependencies
$0 --dry-run Preview changes without applying them
EOF
}
main() {
local create_backup=true
local update_atlantis=true
local update_client=true
local dry_run=false
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
usage
exit 0
;;
-b|--backup)
create_backup=true
shift
;;
--no-backup)
create_backup=false
shift
;;
--atlantis-only)
update_client=false
shift
;;
--client-only)
update_atlantis=false
shift
;;
--dry-run)
dry_run=true
shift
;;
*)
error "Unknown option: $1"
usage
exit 1
;;
esac
done
info "Starting dependency update process"
if [[ "$dry_run" == "true" ]]; then
warn "DRY RUN MODE - No files will be modified"
if [[ "$update_atlantis" == "true" ]]; then
info "Would update: atlantis-deps.json"
fi
if [[ "$update_client" == "true" ]]; then
info "Would update: atlantis-client.json"
fi
exit 0
fi
# Verify we're in the right directory
if [[ ! -f "$PROJECT_ROOT/default.nix" ]] || [[ ! -d "$PACKAGES_DIR" ]]; then
error "This script must be run from the Poseidon project root or scripts directory"
exit 1
fi
# Check dependencies
if ! command -v jaq &> /dev/null; then
error "jaq not found in PATH. Please install jaq."
exit 1
fi
if ! command -v nix-build &> /dev/null; then
error "nix-build not found in PATH. Please install Nix."
exit 1
fi
# Create backup if requested
if [[ "$create_backup" == "true" ]]; then
backup_files
fi
# Update dependencies
local exit_code=0
if [[ "$update_atlantis" == "true" ]]; then
if ! update_dotnet_deps "atlantis" "$PACKAGES_DIR/atlantis-deps.json"; then
exit_code=1
fi
fi
if [[ "$update_client" == "true" ]]; then
if ! update_dotnet_deps "atlantis-client" "$PACKAGES_DIR/atlantis-client.json"; then
exit_code=1
fi
fi
if [[ $exit_code -eq 0 ]]; then
success "All dependency updates completed successfully!"
info "You may want to commit these changes:"
info " git add nix/packages/"
info " git commit -m 'chore: update dependencies'"
else
error "Some dependency updates failed. Check the output above."
fi
exit $exit_code
}
main "$@"

101
scripts/update-rider.sh Executable file
View File

@@ -0,0 +1,101 @@
#!/usr/bin/env nix-shell
#! nix-shell -i bash --pure
#! nix-shell -p bash which xmlstarlet
if [[ ! $# -eq 1 ]]; then
echo "Usage: $0 <dotnet-path>"
exit 1
fi
dotnet_path=$1
function stderr() {
echo "$@" 1>&2;
}
function create_settings_file() {
cat << EOF
<?xml version="1.0"?>
<wpf:ResourceDictionary
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:ss="urn:schemas-jetbrains-com:settings-storage-xaml"
xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xml:space="preserve"
>
<s:String x:Key="/Default/Environment/Hierarchy/Build/BuildTool/DotNetCliExePath/@EntryValue">${1}</s:String>
<s:String x:Key="/Default/Environment/Hierarchy/Build/BuildTool/CustomBuildToolPath/@EntryValue">${2}</s:String>
</wpf:ResourceDictionary>
EOF
}
# HACK: Configure Rider to use the correct .NET paths from an ambient .NET
function use_rider_dotnet() {
local solution_file=$(find . -maxdepth 1 -type f -name '*.slnx' | cut -d'.' -f2 | cut -d'/' -f2)
local settings_file=$(find . -maxdepth 1 -type f -name '*.sln.DotSettings.user')
# Get paths
local cli_path=$(realpath "$dotnet_path")
local dir=$(dirname $cli_path)
local msbuild_path=$(find "$dir" -maxdepth 3 -type f -name MSBuild.dll)
# stderr "dotnet path is $dir"
# stderr "Found msbuild: $msbuild_path"
if [ -f "$settings_file" ] ; then
# stderr "Updating rider settings file: $settings_file"
# stderr "Setting DotNetCliExePath to $cli_path"
# NOTE: check if dotnet binary in share folder settings exists
xml sel -t -v "wpf:ResourceDictionary/s:String[@x:Key='/Default/Environment/Hierarchy/Build/BuildTool/DotNetCliExePath/@EntryValue']" "$settings_file"
if [[ $? -eq 0 ]]; then
xml ed --inplace \
-N wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" \
-N x="http://schemas.microsoft.com/winfx/2006/xaml" \
-N s="clr-namespace:System;assembly=mscorlib" \
-N ss="urn:schemas-jetbrains-com:settings-storage-xaml" \
--update "//s:String[@x:Key='/Default/Environment/Hierarchy/Build/BuildTool/DotNetCliExePath/@EntryValue']" \
--value "$cli_path" \
"$settings_file"
else
xml ed --inplace \
-N wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" \
-N x="http://schemas.microsoft.com/winfx/2006/xaml" \
-N s="clr-namespace:System;assembly=mscorlib" \
-N ss="urn:schemas-jetbrains-com:settings-storage-xaml" \
-s /wpf:ResourceDictionary -t elem -n s:String -v "$cli_path" \
--var new_node '$prev' \
-i '$new_node' -t attr -n "x:Key" -v "/Default/Environment/Hierarchy/Build/BuildTool/DotNetCliExePath/@EntryValue" \
"$settings_file"
fi
xml sel -t -v "wpf:ResourceDictionary/s:String[@x:Key='/Default/Environment/Hierarchy/Build/BuildTool/CustomBuildToolPath/@EntryValue']" "$settings_file"
if [[ $? -eq 0 ]]; then
xmlstarlet ed --inplace \
-N wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" \
-N x="http://schemas.microsoft.com/winfx/2006/xaml" \
-N s="clr-namespace:System;assembly=mscorlib" \
-N ss="urn:schemas-jetbrains-com:settings-storage-xaml" \
--update "//s:String[@x:Key='/Default/Environment/Hierarchy/Build/BuildTool/CustomBuildToolPath/@EntryValue']" \
--value "$msbuild_path" \
"$settings_file"
else
xml ed --inplace \
-N wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" \
-N x="http://schemas.microsoft.com/winfx/2006/xaml" \
-N s="clr-namespace:System;assembly=mscorlib" \
-N ss="urn:schemas-jetbrains-com:settings-storage-xaml" \
-s /wpf:ResourceDictionary -t elem -n s:String -v "$cli_path" \
--var new_node '$prev' \
-i '$new_node' -t attr -n "x:Key" -v "/Default/Environment/Hierarchy/Build/BuildTool/CustomBuildToolPath/@EntryValue" \
"$settings_file"
fi
else
create_settings_file $cli_path $msbuild_path > "$solution_file.sln.DotSettings.user"
fi
}
function main() {
use_rider_dotnet
}
main

View File

@@ -1,10 +1,81 @@
with import <nixpkgs> { };
mkShell {
{
sources ? import ./nix,
pkgs ? import sources.nixpkgs { },
pre-commit ? import ./nix/pre-commit.nix,
}:
let
dotnet-sdk = pkgs.dotnetCorePackages.sdk_10_0;
agenix = pkgs.callPackage "${sources.agenix}/pkgs/agenix.nix" { };
fable = pkgs.buildDotnetGlobalTool {
pname = "fable";
version = "4.24.0";
nugetHash = "sha256-ERewWqfEyyZKpHFFALpMGJT0fDWywBYY5buU/wTZZTg=";
};
in
pkgs.mkShellNoCC {
buildInputs = [ dotnet-sdk ];
packages = [
bun
# F#
fable
pkgs.dotnet-outdated
pkgs.fantomas
pkgs.fsautocomplete
# JavaScript
pkgs.bun
pkgs.nodejs_25
# Devlopment tools
pkgs.npins
pkgs.mkcert
pkgs.dive
pkgs.nix-output-monitor
pkgs.just
pkgs.skopeo
# Secret management with agenix
agenix
# Kubernetes tools
pkgs.tilt
pkgs.dapr-cli
pkgs.kustomize
pkgs.kubernetes-helm
];
# Environment variables
DOTNET_ROOT = "${dotnet-sdk}/share/dotnet";
LOG_LEVEL = "verbose";
shellHook = '' '';
}
shellHook = ''
scripts/update-rider.sh ${dotnet-sdk}/bin/dotnet
'';
# Alternative shells
passthru = pkgs.lib.mapAttrs (name: value: pkgs.mkShellNoCC (value // { inherit name; })) {
pre-commit.shellHook = pre-commit.shellHook;
ci-shell = {
packages = [
pkgs.npins
];
shellHook = ''
export NPINS_DIRECTORY="nix"
'';
};
agenix-gen = {
packages = [ agenix ];
shellHook = ''
if [ -z "$NETRC" ] && [ -e ~/.ssh/id_ed25519 ]; then
pushd ./nix/secrets
export NETRC=$(agenix -d netrc.age -i ~/.ssh/id_ed25519)
popd
elif [ -z "$NETRC" ] && [ -e ~/.ssh/rsa ]; then
pushd ./nix/secrets
export NETRC=$(agenix -d netrc.age -i ~/.ssh/id_rsa)
popd
fi
'';
};
};
}

View File

@@ -1,67 +0,0 @@
open Fake.Core
open Fake.IO
open Farmer
open Farmer.Builders
open Helpers
initializeContext()
let clientPath = Path.getFullName "src/Client"
let cliPath = Path.getFullName "src/Cli"
let testPath = Path.getFullName "tests"
let distPath = Path.getFullName "dist"
let vite = $"bunx --bun vite -c ../../vite.config.js"
let viteBundle = $"{vite} build --outDir {distPath}/public"
Target.create "Clean" (fun _ -> Shell.cleanDir distPath)
// Target.create "Bundle" (fun _ ->
// let vite = $"{viteBundle} -m production"
// run dotnet $"publish -c Release -o \"{distPath}\"" serverPath
// run dotnet $"fable -o build/client --run {vite}" clientPath
// )
// Target.create "BundleDebug" (fun _ ->
// let vite = $"{viteBundle} -m development --minify false"
// run dotnet $"publish -c Debug -o \"{distPath}\"" serverPath
// run dotnet $"fable -o build/client --run {vite}" clientPath
// )
Target.create "Bundle" (fun _ ->
run dotnet $"publish -c Release -o \"{distPath}\"" cliPath
)
Target.create "BundleDebug" (fun _ ->
run dotnet $"publish -c Debug -o \"{distPath}\"" cliPath
)
Target.create "Format" (fun _ ->
run dotnet "fantomas . -r" "src"
)
Target.create "Test" (fun _ ->
if System.IO.Directory.Exists testPath then
run dotnet "run" testPath
else ()
)
Target.create "Run" (fun _ -> Target.runOrDefault "Bundle")
open Fake.Core.TargetOperators
let dependencies = [
"Clean"
==> "Bundle"
"Clean"
==> "BundleDebug"
"Clean"
==> "Test"
]
[<EntryPoint>]
let main args = runOrDefault args

View File

@@ -1,127 +0,0 @@
module Helpers
open Fake.Core
let initializeContext () =
let execContext = Context.FakeExecutionContext.Create false "build.fsx" [ ]
Context.setExecutionContext (Context.RuntimeContext.Fake execContext)
module Proc =
module Parallel =
open System
let locker = obj()
let colors =
[| ConsoleColor.Blue
ConsoleColor.Yellow
ConsoleColor.Magenta
ConsoleColor.Cyan
ConsoleColor.DarkBlue
ConsoleColor.DarkYellow
ConsoleColor.DarkMagenta
ConsoleColor.DarkCyan |]
let print color (colored: string) (line: string) =
lock locker
(fun () ->
let currentColor = Console.ForegroundColor
Console.ForegroundColor <- color
Console.Write colored
Console.ForegroundColor <- currentColor
Console.WriteLine line)
let onStdout index name (line: string) =
let color = colors.[index % colors.Length]
if isNull line then
print color $"{name}: --- END ---" ""
else if String.isNotNullOrEmpty line then
print color $"{name}: " line
let onStderr name (line: string) =
let color = ConsoleColor.Red
if isNull line |> not then
print color $"{name}: " line
let redirect (index, (name, createProcess)) =
createProcess
|> CreateProcess.redirectOutputIfNotRedirected
|> CreateProcess.withOutputEvents (onStdout index name) (onStderr name)
let printStarting indexed =
for (index, (name, c: CreateProcess<_>)) in indexed do
let color = colors.[index % colors.Length]
let wd =
c.WorkingDirectory
|> Option.defaultValue ""
let exe = c.Command.Executable
let args = c.Command.Arguments.ToStartInfo
print color $"{name}: {wd}> {exe} {args}" ""
let run cs =
cs
|> Seq.toArray
|> Array.indexed
|> fun x -> printStarting x; x
|> Array.map redirect
|> Array.Parallel.map Proc.run
let createProcess exe arg dir =
CreateProcess.fromRawCommandLine exe arg
|> CreateProcess.withWorkingDirectory dir
|> CreateProcess.ensureExitCode
let dotnet = createProcess "dotnet"
let fable = createProcess "fable"
let bun =
let bunPath =
match ProcessUtils.tryFindFileOnPath "bun" with
| Some path -> path
| None ->
"bun was not found in path. Please install it and make sure it's available from your path. " +
"See https://safe-stack.github.io/docs/quickstart/#install-pre-requisites for more info"
|> failwith
createProcess bunPath
let bunx = createProcess "bunx"
type BundleMode =
| Prod
| Devel
| Watch
with
override this.ToString() =
match this with
| Prod -> "production"
| Devel -> "development"
| Watch -> "watch"
let viteCmd (m: BundleMode) outDir =
match m with
| Prod -> $"vite build -c ../../vite.config.js -m {m} --emptyOutDir --outDir {outDir}/public"
| Devel -> $"vite build -c ../../vite.config.js -m {m} --minify false --sourcemap true --emptyOutDir --outDir {outDir}/public"
| Watch -> "vite -c ../../vite.config.js"
let run proc arg dir =
proc arg dir
|> Proc.run
|> ignore
let runParallel processes =
processes
|> Proc.Parallel.run
|> ignore
let runOrDefault args =
try
match args with
| [| target |] -> Target.runOrDefault target
| _ ->
Target.runOrDefault "Run"
0
with e ->
printfn "%A" e
1

View File

@@ -1 +1,6 @@
use nix
#!/usr/bin/env bash
# the shebang is ignored, but nice for editors
use nix
# HACK: Workaround for direnv bug
unset TMP TMPDIR TEMP TEMPDIR

View File

@@ -0,0 +1,15 @@
# yaml-language-server: $schema=https://gitlab.com/gitlab-org/gitlab/-/raw/master/app/assets/javascripts/editor/schema/ci.json
variables:
SKIP_TESTS: "true"
include:
- project: oceanbox/gitlab-ci
ref: v4.5
file: DotnetDeployment.gitlab-ci.yml
inputs:
project-name: archivist
project-dir: src/Cli
dockerize-archivist:
tags:
- nix

View File

@@ -1,17 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include=".build/Helpers.fs" />
<Compile Include=".build/Build.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Fake.Core.Target" Version="6.1.3" />
<PackageReference Include="Fake.DotNet.Cli" Version="6.1.3" />
<PackageReference Include="Fake.IO.FileSystem" Version="6.1.3" />
<PackageReference Include="Farmer" Version="1.9.10" />
<PackageReference Update="FSharp.Core" Version="9.0.201" />
</ItemGroup>
</Project>

40
src/Archivist/justfile Normal file
View File

@@ -0,0 +1,40 @@
# Archivist build commands
# Install just: https://github.com/casey/just
set dotenv-load
src_path := "src"
client_path := "src/Client"
cli_path := "src/Cli"
test_path := "tests"
dist_path := "dist"
# Default recipe - show available commands
default:
@just --list
# Clean build artifacts
clean:
rm -rf {{dist_path}}
# Build production bundle
bundle: clean
dotnet publish -c Release -o {{dist_path}} {{cli_path}}
# Build debug bundle
bundle-debug: clean
dotnet publish -c Debug -o {{dist_path}} {{cli_path}}
# Format code with Fantomas
format:
fantomas {{src_path}} -r
# Run tests
test: clean
#!/usr/bin/env bash
if [ -d "{{test_path}}" ]; then
dotnet run {{test_path}}
fi
# Run (builds bundle)
run: bundle

View File

@@ -1,22 +1,20 @@
with import <nixpkgs> { };
{
sources ? import ./../../nix,
pkgs ? import sources.nixpkgs { },
}:
let
port = 9000;
baseShell = import ./../../shell.nix { inherit pkgs; };
in
mkShell {
packages = [
tilt
dapr-cli
kustomize
bun
mkcert
];
pkgs.mkShellNoCC {
inputsFrom = [ baseShell ];
buildInputs = [
netcdf
pkgs.netcdf
];
LOG_LEVEL = "verbose";
LD_LIBRARY_PATH = lib.makeLibraryPath [ netcdf ];
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [ pkgs.netcdf ];
DB_HOST = "localhost";
DB_PORT = 5432;
@@ -31,7 +29,10 @@ mkShell {
SERVER_PORT = port + 85;
TILT_PORT = port + 50;
DOTNET_ROOT = "${pkgs.dotnetCorePackages.sdk_10_0}/share/dotnet";
shellHook = ''
export PATH="$PWD/src/Cli/bin/Release/net9.0/linux-x64/:$PATH"
export ARCHMAESTER_URL="https://$USER-atlantis.dev.oceanbox.io"
'';
}

View File

@@ -1,9 +1,9 @@
module AclCli
open Argu
open Args
open FSharpPlus
open Serilog
open ArchiveIndex
open Oceanbox.DataAgent
@@ -16,14 +16,14 @@ let inline private execAsync job =
type private Handler = string[] -> Async<Result<unit, string>>
let addOwners (args: ParseResults<Prinicipal>) =
let owners = args.GetResult Prinicipal.Ids |> Array.ofList
let addOwners (args: PrincipalArgs) =
let owners = args.Ids |> Array.ofList
if args.Contains Prinicipal.Archive then
let aid = args.GetResult Prinicipal.Archive
if args.Archive.IsSome then
let aid = args.Archive |> Option.get
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let aclApi = api.aclApi ()
async {
@@ -31,7 +31,7 @@ let addOwners (args: ParseResults<Prinicipal>) =
match! aclApi.addOwners (aid, owners) with
| Ok _ ->
Log.Information $"Added owners %A{owners} to archive {aid}"
return Ok()
return Ok ()
| Error e ->
Log.Error $"Error: {e}"
return Error e
@@ -39,20 +39,20 @@ let addOwners (args: ParseResults<Prinicipal>) =
return Error exn.Message
})
else
async { return Ok() }
async { return Ok () }
|> Async.RunSynchronously
|> function
| Ok _ -> ()
| Error e -> Log.Error e
let addUsers (args: ParseResults<Prinicipal>) =
let users = args.GetResult Prinicipal.Ids |> Array.ofList
let addUsers (args: PrincipalArgs) =
let users = args.Ids |> Array.ofList
if args.Contains Prinicipal.Archive then
let aid = args.GetResult Prinicipal.Archive
if args.Archive.IsSome then
let aid = args.Archive |> Option.get
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let aclApi = api.aclApi ()
async {
@@ -60,7 +60,7 @@ let addUsers (args: ParseResults<Prinicipal>) =
match! aclApi.addUsers (aid, users) with
| Ok _ ->
Log.Information $"Added users %A{users} to archive {aid}"
return Ok()
return Ok ()
| Error e ->
Log.Error $"Error: {e}"
return Error e
@@ -69,15 +69,19 @@ let addUsers (args: ParseResults<Prinicipal>) =
})
else
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let aclApi = api.adminApi ()
async {
try
match! aclApi.addUsers (users) with
let req : Archmaester.AddUsersRequest = {
group = ""
users = users
}
match! aclApi.addUsers req with
| Ok _ ->
Log.Information $"Added users %A{users}"
return Ok()
return Ok ()
| Error e ->
Log.Error $"Error: {e}"
return Error e
@@ -89,14 +93,14 @@ let addUsers (args: ParseResults<Prinicipal>) =
| Ok _ -> ()
| Error e -> Log.Error e
let addGroups (args: ParseResults<Prinicipal>) =
let groups = args.GetResult Prinicipal.Ids |> Array.ofList
let addGroups (args: PrincipalArgs) =
let groups = args.Ids |> Array.ofList
if args.Contains Prinicipal.Archive then
let aid = args.GetResult Prinicipal.Archive
if args.Archive.IsSome then
let aid = args.Archive |> Option.get
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let aclApi = api.aclApi ()
async {
@@ -104,7 +108,7 @@ let addGroups (args: ParseResults<Prinicipal>) =
match! aclApi.addGroups (aid, groups) with
| Ok _ ->
Log.Information $"Added groups %A{groups} to archive {aid}"
return Ok()
return Ok ()
| Error e ->
Log.Error $"Error: {e}"
return Error e
@@ -113,15 +117,15 @@ let addGroups (args: ParseResults<Prinicipal>) =
})
else
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let aclApi = api.adminApi ()
async {
try
match! aclApi.addGroups (groups) with
match! aclApi.addGroups groups with
| Ok _ ->
Log.Information $"Added groups %A{groups}"
return Ok()
return Ok ()
| Error e ->
Log.Error $"Error: {e}"
return Error e
@@ -133,14 +137,14 @@ let addGroups (args: ParseResults<Prinicipal>) =
| Ok _ -> ()
| Error e -> Log.Error e
let deleteOwners (args: ParseResults<Prinicipal>) =
let owners = args.GetResult Prinicipal.Ids |> Array.ofList
let deleteOwners (args: PrincipalArgs) =
let owners = args.Ids |> Array.ofList
if args.Contains Prinicipal.Archive then
let aid = args.GetResult Prinicipal.Archive
if args.Archive.IsSome then
let aid = args.Archive |> Option.get
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let aclApi = api.aclApi ()
async {
@@ -148,7 +152,7 @@ let deleteOwners (args: ParseResults<Prinicipal>) =
match! aclApi.removeOwners (aid, owners) with
| Ok _ ->
Log.Information $"Removed owners %A{owners} from archive {aid}"
return Ok()
return Ok ()
| Error e ->
Log.Error $"Error: {e}"
return Error e
@@ -156,20 +160,20 @@ let deleteOwners (args: ParseResults<Prinicipal>) =
return Error exn.Message
})
else
async { return Ok() }
async { return Ok () }
|> Async.RunSynchronously
|> function
| Ok _ -> ()
| Error e -> Log.Error e
let deleteUsers (args: ParseResults<Prinicipal>) =
let users = args.GetResult Prinicipal.Ids |> Array.ofList
let deleteUsers (args: PrincipalArgs) =
let users = args.Ids |> Array.ofList
if args.Contains Prinicipal.Archive then
let aid = args.GetResult Prinicipal.Archive
if args.Archive.IsSome then
let aid = args.Archive.Value
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let aclApi = api.aclApi ()
async {
@@ -177,7 +181,7 @@ let deleteUsers (args: ParseResults<Prinicipal>) =
match! aclApi.removeUsers (aid, users) with
| Ok _ ->
Log.Information $"Removed users %A{users} from archive {aid}"
return Ok()
return Ok ()
| Error e ->
Log.Error $"Error: {e}"
return Error e
@@ -186,15 +190,15 @@ let deleteUsers (args: ParseResults<Prinicipal>) =
})
else
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let aclApi = api.adminApi ()
async {
try
match! aclApi.removeUsers (users) with
match! aclApi.removeUsers users with
| Ok _ ->
Log.Information $"Removed users %A{users}"
return Ok()
return Ok ()
| Error e ->
Log.Error $"Error: {e}"
return Error e
@@ -206,14 +210,14 @@ let deleteUsers (args: ParseResults<Prinicipal>) =
| Ok _ -> ()
| Error e -> Log.Error e
let deleteGroups (args: ParseResults<Prinicipal>) =
let groups = args.GetResult Prinicipal.Ids |> Array.ofList
let deleteGroups (args: PrincipalArgs) =
let groups = args.Ids |> Array.ofList
if args.Contains Prinicipal.Archive then
let aid = args.GetResult Prinicipal.Archive
if args.Archive.IsSome then
let aid = args.Archive |> Option.get
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let aclApi = api.aclApi ()
async {
@@ -221,7 +225,7 @@ let deleteGroups (args: ParseResults<Prinicipal>) =
match! aclApi.removeGroups (aid, groups) with
| Ok _ ->
Log.Information $"Removed groups %A{groups} from archive {aid}"
return Ok()
return Ok ()
| Error e ->
Log.Error $"Error: {e}"
return Error e
@@ -230,15 +234,15 @@ let deleteGroups (args: ParseResults<Prinicipal>) =
})
else
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let aclApi = api.adminApi ()
async {
try
match! aclApi.removeGroups (groups) with
match! aclApi.removeGroups groups with
| Ok _ ->
Log.Information $"Removed groups %A{groups}"
return Ok()
return Ok ()
| Error e ->
Log.Error $"Error: {e}"
return Error e

View File

@@ -1,10 +1,10 @@
module ArchiveCli
open FSharpPlus
open Serilog
open System
open System.IO
open Argu
open Serilog
open FSharpPlus
open Args
open ArchiveIndex
open Archmaester.Dto
@@ -17,20 +17,20 @@ let inline private execAsync job =
| Ok _ -> ()
| Error e -> Log.Error e
let getArchiveId (args: ParseResults<AddArchive>) (idx: ArchiveIndex.ArchiveIndex) =
match args.TryGetResult AddArchive.Id with
let getArchiveId (args: AddArchiveArgs) (idx: ArchiveIndex.ArchiveIndex) =
match args.Id with
| Some id -> id
| None -> idx.archiveId
let getArchiveBasePath (args: ParseResults<AddArchive>) =
let getArchiveBasePath (args: AddArchiveArgs) =
let e = "neither index.json or base path specified"
let fs = args.GetResult AddArchive.Files
let fs = args.Files
match args.TryGetResult AddArchive.Index with
match args.Index with
| Some f -> getBasePath f
| None ->
if fs.Length = 1 then
// TODO: Handle exn
// TODO: Handle exn :/
if isDir fs[0] then
Path.GetFullPath fs[0]
else
@@ -42,25 +42,25 @@ let getArchiveBasePath (args: ParseResults<AddArchive>) =
let getModelAreaArchives modelId =
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let cli = api.inventoryApi ()
cli.getModelAreaArchives (modelId, ArchiveType.FromString "*:*:*"))
let getArchive archiveId =
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let cli = api.adminApi ()
cli.getArchiveDto archiveId)
let getFiles archiveId =
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let cli = api.adminApi ()
cli.getFiles archiveId)
let getAllFiles archiveId =
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let cli = api.adminApi ()
cli.getAllFiles archiveId)
@@ -78,7 +78,7 @@ let postArchive (idx, modelArea, basePath, files, reverse, json, published) =
let args = idx, modelArea, basePath, files, reverse, json, published
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let adminApi = api.adminApi ()
instantiateArchiveDto args
@@ -103,7 +103,7 @@ let postArchive (idx, modelArea, basePath, files, reverse, json, published) =
/// <param name="force">TODO: Whether to overwrite the archive when we find a conflicting ID</param>
let postSubArchive (force: bool) (dto: SubArchiveDef) =
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let cli = api.archiveApi ()
cli.addSubArchive dto)
@@ -113,11 +113,14 @@ let postSubArchive (force: bool) (dto: SubArchiveDef) =
/// <param name="idx">Content of the archive's index file</param>
let postArchiveUpdate (idx: ArchiveIndex) =
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let archiveApi = api.archiveApi ()
async {
match! archiveApi.getArchive idx.archiveId with
| Error err ->
failwith err
return Error err
| Ok _ ->
let form: Archmaester.Forms.ArchiveForm = {
name = idx.name
@@ -129,28 +132,28 @@ let postArchiveUpdate (idx: ArchiveIndex) =
}
return! archiveApi.updateArchive (idx.archiveId, form)
| Error err ->
failwith err
return Error err
})
let modifyArchive (args: ParseResults<ModifyArchive>) =
let aid = args.GetResult ModifyArchive.ArchiveId
let modifyArchive (args: ModifyArchiveArgs) =
let aid = args.ArchiveId
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let archiveApi = api.archiveApi ()
let expiry = args.TryGetResult ModifyArchive.Expires |> Option.map DateTime.Parse
let points = args.TryGetResult ModifyArchive.Fence |> Option.map List.toArray
let expiry = args.Expires |> Option.map DateTime.Parse
let points = args.Fence |> Option.map List.toArray
async {
try
match! archiveApi.getArchive aid with
| Error e ->
Log.Error $"{e}"
return Error e
| Ok a ->
let form: Archmaester.Forms.ArchiveForm = {
name = args.GetResult(ModifyArchive.Name, a.name)
isPublished = args.GetResult(ModifyArchive.Published, a.isPublished)
isPublic = args.GetResult(ModifyArchive.Public, a.isPublic)
name = args.Name |> Option.defaultValue a.name
isPublished = args.Published |> Option.defaultValue a.isPublished
isPublic = args.Public |> Option.defaultValue a.isPublic
expires = expiry
json = if a.json = "" then None else Some a.json
geometry =
@@ -167,60 +170,57 @@ let modifyArchive (args: ParseResults<ModifyArchive>) =
|> fun y ->
Log.Debug $"%A{y}"
y
else a.polygon
else
a.polygon
}
return! archiveApi.updateArchive (aid, form)
| Error e ->
Log.Error $"{e}"
return Error e
with exn ->
return Error exn.Message
})
|> execAsync
let modifyArchiveAttribs (args: ParseResults<ModifyArchiveAttribs>) =
let aid = args.GetResult ModifyArchiveAttribs.ArchiveId
let modifyArchiveAttribs (args: ModifyArchiveAttribsArgs) =
let aid = args.ArchiveId
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let adminApi = api.adminApi ()
async {
try
if args.Contains ModifyArchiveAttribs.Add_Related then
let related = args.GetResult ModifyArchiveAttribs.Add_Related
if args.AddRelated.IsSome then
let related = args.AddRelated |> Option.get
let! _ = adminApi.addAssociation (aid, related)
()
if args.Contains ModifyArchiveAttribs.Remove_Related then
let related = args.GetResult ModifyArchiveAttribs.Remove_Related
if args.RemoveRelated.IsSome then
let related = args.RemoveRelated |> Option.get
let! _ = adminApi.removeAssociation (aid, related)
()
match! adminApi.getArchiveDto aid with
| Error e ->
Log.Error $"{e}"
return Error e
| Ok a ->
let form: Archmaester.Forms.ArchiveAttribsForm = {
description = args.GetResult(ModifyArchiveAttribs.Description, a.props.description)
basePath = args.GetResult(ModifyArchiveAttribs.Base_Path, a.files.basePath)
modelArea = args.GetResult(ModifyArchiveAttribs.Model_Area, a.props.modelArea)
focalpoint = args.GetResult(ModifyArchiveAttribs.Focal, a.props.focalPoint)
defaultZoom = args.GetResult(ModifyArchiveAttribs.Zoom, a.props.defaultZoom)
description = args.Description |> Option.defaultValue a.props.description
basePath = args.BasePath |> Option.defaultValue a.files.basePath
modelArea = args.ModelArea |> Option.defaultValue a.props.modelArea
focalpoint = args.Focal |> Option.defaultValue a.props.focalPoint
defaultZoom = args.Zoom |> Option.defaultValue a.props.defaultZoom
json = if a.json = "" then None else Some a.json
geometry = if a.polygon = [||] then None else Some a.polygon
}
return! adminApi.updateArchiveAttribs (aid, form)
| Error e ->
Log.Error $"{e}"
return Error e
with exn ->
return Error exn.Message
})
|> execAsync
let deleteArchives (args: ParseResults<Delete>) =
args.GetResult Delete.Archive |> retireArchive |> ignore
let deleteArchives (archive: string) = archive |> retireArchive |> ignore
let readDriftersInputJson file =
if File.Exists file then
@@ -229,23 +229,23 @@ let readDriftersInputJson file =
Log.Error $"Drifters input not found: {file}"
""
let rec addArchive (args: ParseResults<AddArchive>) =
let rec addArchive (args: AddArchiveArgs) =
let basePath = getArchiveBasePath args
Log.Information $"BasePath: %s{basePath}"
let idx =
match args.TryGetResult AddArchive.Index with
match args.Index with
| Some ix -> ix
| None -> $"{basePath}/index.json"
|> readArchiveIdx
let files =
getArchiveFiles idx.archiveType basePath (args.GetResult AddArchive.Files |> Array.ofList)
let files = getArchiveFiles idx.archiveType basePath (args.Files |> Array.ofList)
let modelArea = initiateModelArea idx basePath
let json, reverse =
match idx.archiveType with
| Drifters(_, DriftersFormat.Particle) ->
| Drifters (_, DriftersFormat.Particle) ->
let s = readDriftersInputJson (Path.Join [| basePath; "input.json" |])
let r =
@@ -258,8 +258,8 @@ let rec addArchive (args: ParseResults<AddArchive>) =
match r with
| Ok x -> s, x
| Error e -> failwith e
| Drifters(_, DriftersFormat.Field2D)
| Drifters(_, DriftersFormat.Field3D) ->
| Drifters (_, DriftersFormat.Field2D)
| Drifters (_, DriftersFormat.Field3D) ->
let s = readDriftersInputJson (Path.Join [| basePath; "input.json" |])
let r =
@@ -274,29 +274,29 @@ let rec addArchive (args: ParseResults<AddArchive>) =
| Error e -> failwith e
| _ -> "", false
let published = args.GetResult(AddArchive.Published, defaultValue = true)
let published = args.Published |> Option.defaultValue true
let saveRes =
try
postArchive (idx, modelArea, basePath, files, reverse, json, published)
|> Async.RunSynchronously
with e ->
Log.Error(e, "Archivist.addArchive {ArchmaesterUrl}", Settings.archmaesterUrl)
Log.Error (e, "Archivist.addArchive {ArchmaesterUrl}", Settings.archmaesterUrl)
Error "Could not add archive"
match saveRes with
| Ok() -> Log.Information $"Successfully added archive %s{idx.name}"
| Ok () -> Log.Information $"Successfully added archive %s{idx.name}"
| Error err -> Log.Error $"Error: {err}"
let createSubArchive (args: ParseResults<SubArchive>) =
let createSubArchive (args: SubArchiveArgs) =
let fetchRefArchive = getArchive >> Async.RunSynchronously
let createSubArchive refArchive = {
uuid = Guid.NewGuid()
uuid = Guid.NewGuid ()
reference = refArchive.props.archiveId
name = args.GetResult SubArchive.Name
startFile = args.GetResult SubArchive.From
endFile = args.GetResult(SubArchive.To, defaultValue = Int32.MaxValue)
name = args.Name
startFile = args.From
endFile = args.To |> Option.defaultValue Int32.MaxValue
acl = {
owners = [||]
groups = [||]
@@ -306,12 +306,12 @@ let createSubArchive (args: ParseResults<SubArchive>) =
polygon = [||]
json = ""
isPublic = false
isPublished = args.GetResult(SubArchive.Published, defaultValue = true)
isPublished = args.Published |> Option.defaultValue true
}
let postSubArchive = postSubArchive false >> Async.RunSynchronously
fetchRefArchive (args.GetResult SubArchive.Ref)
fetchRefArchive args.Ref
|> Result.map createSubArchive
|> Result.bind postSubArchive
|> function
@@ -320,7 +320,7 @@ let createSubArchive (args: ParseResults<SubArchive>) =
let getAcl archiveId =
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let cli = api.aclApi ()
cli.getAcl archiveId)
@@ -336,17 +336,17 @@ let printAcl acl =
acl.users |> formatAcl "users" |> (fun s -> printf $"{s}")
acl.groups |> formatAcl "groups" |> (fun s -> printf $"{s}")
let showArchive (args: ParseResults<ShowArchive>) =
let archiveId = args.GetResult ShowArchive.Archive
let showArchive (args: ShowArchiveArgs) =
let archiveId = args.ArchiveId
let fmt = "yyyy-MM-dd HH:mm"
async {
match! getArchive archiveId with
| Ok a ->
let! files =
if args.Contains ShowArchive.All then getAllFiles archiveId
elif args.Contains ShowArchive.Files then getFiles archiveId
else async.Return({ basePath = ""; series = [||] } |> Ok)
if args.All then getAllFiles archiveId
elif args.Files then getFiles archiveId
else async.Return ({ basePath = ""; series = [||] } |> Ok)
let! acl = getAcl archiveId
@@ -360,20 +360,20 @@ let showArchive (args: ParseResults<ShowArchive>) =
f.series
|> Array.iteri (fun n x ->
let name = String.truncate 32 x.name |> fun y -> y.PadRight(32, ' ')
printfn $" %-3d{n}: {name} | {x.startTime.ToString(fmt)} - {x.endTime.ToString(fmt)}")
let name = String.truncate 32 x.name |> fun y -> y.PadRight (32, ' ')
printfn $" %-3d{n}: {name} | {x.startTime.ToString (fmt)} - {x.endTime.ToString (fmt)}")
| Error e -> Log.Error e
| Error e -> Log.Error e
}
|> Async.RunSynchronously
let showRelatedArchives (args: ParseResults<ShowArchive>) =
let archiveId = args.GetResult ShowArchive.Archive
let showRelatedArchives (args: ShowArchiveArgs) =
let archiveId = args.ArchiveId
async {
let! r =
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let cli = api.inventoryApi ()
cli.getAssociated (archiveId, ArchiveType.Any))
// cli.getRelated(archiveId, Atmo(AtmoVariant.Any, AtmoFormat.Any)))
@@ -383,12 +383,12 @@ let showRelatedArchives (args: ParseResults<ShowArchive>) =
}
|> Async.RunSynchronously
let augmentArchive (args: ParseResults<Augment>) =
let aid = args.GetResult Augment.Archive
let fnames = args.GetResult Augment.Files |> List.map Path.GetFullPath
let augmentArchive (args: AugmentArgs) =
let aid = args.Archive
let fnames = args.Files |> List.map Path.GetFullPath
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let archmaester = api.adminApi ()
async {
@@ -402,7 +402,7 @@ let augmentArchive (args: ParseResults<Augment>) =
|> traverse (readDataSet a)
|> Result.bind (fun files ->
let _, _, t = List.head files
let startUtc = t.ToUniversalTime()
let startUtc = t.ToUniversalTime ()
let f = Array.ofList files
ensureContiguous startUtc archive.props.freq f |> Result.map fst)
@@ -414,8 +414,8 @@ let augmentArchive (args: ParseResults<Augment>) =
name = stripBasePath archive.files.basePath name
frames = frames
ordering = 0
startTime = t.ToUniversalTime()
endTime = t.AddSeconds(archive.props.freq * frames |> float)
startTime = t.ToUniversalTime ()
endTime = t.AddSeconds (archive.props.freq * frames |> float)
reverse = reverse
})
@@ -428,27 +428,27 @@ let augmentArchive (args: ParseResults<Augment>) =
})
|> execAsync
let resizeArchive (args: ParseResults<Resize>) =
let aid = args.GetResult Resize.Archive
let first = args.GetResult(Resize.From, Int32.MinValue)
let last = args.GetResult(Resize.To, Int32.MaxValue)
let resizeArchive (args: ResizeArgs) =
let aid = args.Archive
let first = args.From
let last = args.To |> Option.defaultValue Int32.MaxValue
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let archmaester = api.archiveApi ()
async { return! archmaester.resizeArchive (aid, first, last) })
|> execAsync
let addType (t: string) =
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let adminApi = api.adminApi ()
async { return! adminApi.addType t })
|> execAsync
let deleteType (t: string) =
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let adminApi = api.adminApi ()
async { return! adminApi.removeType t })
|> execAsync

View File

@@ -1,15 +1,16 @@
module ArchiveIndex
open FSharpPlus
open Microsoft.Research.Science.Data
open ProjNet.FSharp
open Serilog
open System
open System.IO
open Thoth.Json.Net
open Archmaester.Dto
open Oceanbox.DataAgent
open Oceanbox.DataAgent.DatasetAgent
open FSharpPlus
open ProjNet.FSharp
type ModelAreaIndex = {
modelAreaId: ModelAreaId
@@ -116,7 +117,9 @@ let inline withCliAuth f =
| None ->
Log.Fatal "You must provide ARCHMAESTER_AUTH"
Error "You are not logged in" |> async.Return
| Some auth -> f auth
| Some auth ->
Log.Debug "You are logged in"
f auth
let private (|NumSeq|_|) x =
let rex = Text.RegularExpressions.Regex @".+[-_]([0-9]+)\..+$"
@@ -298,7 +301,7 @@ let FvStatsArchive =
member x.getNumFrames ds = 12 // year
}
let getArchiveFiles format basePath (files: string[]) =
let getArchiveFiles format basePath (files: string array) =
let fs =
if files.Length = 1 then
Directory.GetFiles(basePath, "*.nc", SearchOption.AllDirectories)
@@ -462,32 +465,34 @@ let initiateModelArea (idx: ArchiveIndex) (basePath: string) =
let getParentModelAreaId rid =
async {
let! parent = adminApi.getArchiveDto rid
match parent with
| Ok p -> return Ok p.props.modelArea
| Error e -> return failwith $"Could not get model area id: {e}"
try
let! parent = adminApi.getArchiveDto rid
match parent with
| Ok p ->
return Ok p.props.modelArea
| Error e -> return failwith $"Could not get model area id: {e}"
with e -> return failwith $"Failed to get parent archive: %A{rid} with %A{e.Message}"
}
let checkModelArea () =
let failBadly () =
Log.Error "Either modelArea or reference must be specified!"
Environment.Exit 1
failwith "Missing modelArea or reference."
let failBadly () =
Log.Error "Either modelArea or reference must be specified!"
Environment.Exit 1
failwith "Missing modelArea or reference."
match idx.modelArea with
| None ->
if idx.reference.IsSome then
async {
match! inventoryApi.getArchive idx.reference.Value with
| Ok ref -> return! modelAreaApi.getModelArea ref.modelArea
| Error e ->
Log.Error $"{e}"
return None
}
else
failBadly ()
| Some mid -> modelAreaApi.getModelArea mid
match idx.modelArea with
| None ->
if idx.reference.IsSome then
async {
match! inventoryApi.getArchive idx.reference.Value with
| Ok ref -> return! modelAreaApi.getModelArea ref.modelArea
| Error e ->
Log.Error $"{e}"
return None
}
else
failBadly ()
| Some mid -> modelAreaApi.getModelArea mid
// match model with
// | ModelAreaId mid -> modelAreaApi.getModelArea mid
// | ModelArea m -> tryAddModelArea m basePath
@@ -497,7 +502,6 @@ let initiateModelArea (idx: ArchiveIndex) (basePath: string) =
// | Ok m -> return! tryAddModelArea m basePath
// | Error e -> return Error e
// }
match idx.archiveType with
| FvStats _
| Atmo _ ->
@@ -515,14 +519,11 @@ let initiateModelArea (idx: ArchiveIndex) (basePath: string) =
| Fvcom _ ->
async {
let! model = modelAreaApi.getModelAreaId idx.name
match model with
| Ok m ->
Log.Debug $"Existing model area: %A{model}"
return Ok m
| Error _ ->
Log.Debug "Make new model area"
match! checkModelArea () with
| Some ma ->
Log.Debug $"New model area: %A{ma}"
@@ -630,7 +631,6 @@ let instantiateArchiveDto (idx, modelArea, basePath, files, reverse, json, publi
}
let retireArchive (archive: string) =
// TODO: retire all dependent archies
let aid =
try
Guid.Parse archive

View File

@@ -2,7 +2,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
<AssemblyName>archivist</AssemblyName>
<Version>7.1.0</Version>
</PropertyGroup>
<ItemGroup>
@@ -16,27 +19,26 @@
<Compile Include="Main.fs"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Argu" Version="6.2.5"/>
<PackageReference Include="FSharp.Data" Version="6.4.1"/>
<PackageReference Include="FSharpPlus" Version="1.7.0"/>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.1"/>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.1">
<PackageReference Include="Fargo.CmdLine" />
<PackageReference Include="FSharp.Data" />
<PackageReference Include="FSharpPlus" />
<PackageReference Include="Microsoft.EntityFrameworkCore" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" >
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.1">
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" >
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Oceanbox.FvcomKit" Version="5.6.0"/>
<PackageReference Include="Serilog" Version="4.2.0"/>
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0"/>
<PackageReference Include="Serilog.Sinks.Seq" Version="9.0.0"/>
<PackageReference Include="Thoth.Json.Net" Version="12.0.0"/>
<PackageReference Update="FSharp.Core" Version="9.0.201"/>
<PackageReference Include="Serilog" />
<PackageReference Include="Serilog.Sinks.Console" />
<PackageReference Include="Thoth.Json.Net" />
<PackageReference Include="FSharp.Core" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\DataAgent\src\DataAgent\Oceanbox.DataAgent.fsproj"/>
<ProjectReference Include="..\..\..\DataAgent\src\Entity\Entity.csproj"/>
<ProjectReference Include="..\..\..\Interfaces\Archmaester\Archmaester.Api.fsproj" PrivateAssets="All" />
</ItemGroup>
</Project>

View File

@@ -2,319 +2,582 @@ module Args
open System
open System.IO
open Argu
open Fargo
open Fargo.Operators
open Serilog
open Archmaester.Dto
let colorizer =
function
| ErrorCode.HelpText -> None
| _ -> Some ConsoleColor.Red
let errorHandler = ProcessExiter(colorizer = colorizer)
let showVersion () =
let assembly = System.Reflection.Assembly.GetExecutingAssembly()
let assembly = System.Reflection.Assembly.GetExecutingAssembly ()
let version = System.Diagnostics.FileVersionInfo.GetVersionInfo assembly.Location
printfn $"{version.FileVersion}"
type ListArchive =
| All
| Retired
| Refs
| Type of string
| Owner of string
| User of string
| Group of string
| Archive_Name of string: string
| Model_Name of string: string
| [<AltCommandLine("-V")>] Verbose
| Json
interface IArgParserTemplate with
member this.Usage =
match this with
| All -> "List all archives"
| Refs -> "List referencing archives"
| Retired -> "List retired archives"
| Type _ -> "List archives of the given type"
| Owner _ -> "List archives owned by user"
| User _ -> "List archives for user"
| Group _ -> "Remove archives for group"
| Archive_Name _ -> "List archives matching pattern"
| Model_Name _ -> "List archives matching model area"
| Verbose -> "Detailed listing"
| Json -> "Json output"
type CmdType =
| ListCmd
| ShowCmd
| AddCmd
| DeleteCmd
| ModifyCmd
| AugmentCmd
| ResizeCmd
type ListModel =
| Model_Name of regex: string
| Verbose
| Json
interface IArgParserTemplate with
member this.Usage =
match this with
| Model_Name _ -> "List models matching regex"
| Verbose -> "Detailed listing"
| Json -> "Json output"
type ListType =
| Archives
| Models
| Types
type List =
| [<CliPrefix(CliPrefix.None); AltCommandLine("a")>] Archives of ParseResults<ListArchive>
| [<CliPrefix(CliPrefix.None); AltCommandLine("m")>] Models of ParseResults<ListModel>
| [<CliPrefix(CliPrefix.None); AltCommandLine("t")>] Types
interface IArgParserTemplate with
member this.Usage =
match this with
| Archives _ -> "List archives"
| Models _ -> "List models"
| Types -> "List types"
type ShowType =
| Archive
| Model
type ShowArchive =
| All
| Refs
| Related
| Files
| Json
| [<MainCommand; Last>] Archive of id: ArchiveId
interface IArgParserTemplate with
member this.Usage =
match this with
| Json -> "Json output"
| Refs -> "List archives referencing archive"
| Related -> "List related archives"
| Files -> "List file names"
| All -> "List all available files (for resize)"
| Archive _ -> "Archive id"
type AddType =
| Archive
| Sub
| Model
| Owner
| User
| Group
| Type
type ShowModel =
| Verbose
| Json
| [<MainCommand; Last>] Model of id: ModelAreaId
interface IArgParserTemplate with
member this.Usage =
match this with
| Model _ -> "Model id"
| Verbose -> "Detailed listing"
| Json -> "Json output"
type DeleteType =
| Archive
| Model
| Owner
| User
| Group
| Type
type Show =
| [<CliPrefix(CliPrefix.None); AltCommandLine("a")>] Archive of ParseResults<ShowArchive>
| [<CliPrefix(CliPrefix.None); AltCommandLine("m")>] Model of ParseResults<ShowModel>
interface IArgParserTemplate with
member this.Usage =
match this with
| Archive _ -> "List archive"
| Model _ -> "List model"
type ModifyType =
| Archive
| ArchiveAttribs
| Model
type AddArchive =
| Index of index: string
| No_sort
| Id of guid: ArchiveId
| Force
| Published of bool
| [<MainCommand; Last>] Files of files: string list
interface IArgParserTemplate with
member this.Usage =
match this with
| No_sort -> "No sorting of files"
| Index _ -> "index.json"
| Id _ -> "Archive id to update"
| Files _ -> "Files to add"
| Force -> "Forcefully add or update"
| Published _ -> "Publish archive (default: true)"
type ListArchiveArgs = {
All: bool
Retired: bool
Refs: bool
Type: string option
Owner: string option
User: string option
Group: string option
ArchiveName: string option
ModelName: string option
Verbose: bool
Json: bool
}
type SubArchive =
| [<Mandatory>] Ref of guid: ArchiveId
| [<Mandatory>] From of first: int
| To of last: int
| Published of bool
| [<MainCommand; Last>] Name of name: string
interface IArgParserTemplate with
member this.Usage =
match this with
| Ref _ -> "Reference archive"
| From _ -> "Index of first file to include"
| To _ -> "Index of last file to include"
| Published _ -> "Publish archive (default: true)"
| Name _ -> "Name of the new archive"
type ListModelArgs = { ModelName: string option; Verbose: bool; Json: bool }
type AddModel =
| [<MainCommand; Last>] File of json: string
interface IArgParserTemplate with
member this.Usage =
match this with
| File _ -> "model.json"
type ShowArchiveArgs = {
All: bool
Refs: bool
Related: bool
Files: bool
Json: bool
ArchiveId: ArchiveId
}
type Prinicipal =
| Archive of id: ArchiveId
| [<MainCommand; Last>] Ids of name: string list
interface IArgParserTemplate with
member this.Usage =
match this with
| Archive _ -> "Apply to archive"
| Ids _ -> "Identies"
type ShowModelArgs = { Verbose: bool; Json: bool; ModelId: ModelAreaId }
type Add =
| [<CliPrefix(CliPrefix.None); AltCommandLine("a")>] Archive of ParseResults<AddArchive>
| [<CliPrefix(CliPrefix.None); AltCommandLine("s")>] Sub of ParseResults<SubArchive>
| [<CliPrefix(CliPrefix.None); AltCommandLine("m")>] Model of ParseResults<AddModel>
| [<CliPrefix(CliPrefix.None); AltCommandLine("o")>] Owner of ParseResults<Prinicipal>
| [<CliPrefix(CliPrefix.None); AltCommandLine("u")>] User of ParseResults<Prinicipal>
| [<CliPrefix(CliPrefix.None); AltCommandLine("g")>] Group of ParseResults<Prinicipal>
| [<CliPrefix(CliPrefix.None); AltCommandLine("t")>] Type of string
interface IArgParserTemplate with
member this.Usage =
match this with
| Archive _ -> "Add archive"
| Sub _ -> "Add sub-archive"
| Model _ -> "Add model"
| Owner _ -> "Add owner"
| User _ -> "Add user"
| Group _ -> "Add group"
| Type _ -> "Add type"
type AddArchiveArgs = {
Index: string option
NoSort: bool
Id: ArchiveId option
Force: bool
Published: bool option
Files: string list
}
type DeleteModel =
| Force
| [<MainCommand; Last>] Model of id: ModelAreaId
interface IArgParserTemplate with
member this.Usage =
match this with
| Force -> "Force delete"
| Model _ -> "Model id"
type SubArchiveArgs = {
Ref: ArchiveId
From: int
To: int option
Published: bool option
Name: string
}
type Delete =
| [<CliPrefix(CliPrefix.None); AltCommandLine("a")>] Archive of id: string
| [<CliPrefix(CliPrefix.None); AltCommandLine("m")>] Model of ParseResults<DeleteModel>
| [<CliPrefix(CliPrefix.None); AltCommandLine("o")>] Owner of ParseResults<Prinicipal>
| [<CliPrefix(CliPrefix.None); AltCommandLine("u")>] User of ParseResults<Prinicipal>
| [<CliPrefix(CliPrefix.None); AltCommandLine("g")>] Group of ParseResults<Prinicipal>
| [<CliPrefix(CliPrefix.None); AltCommandLine("t")>] Type of string
interface IArgParserTemplate with
member this.Usage =
match this with
| Archive _ -> "Delete archive"
| Model _ -> "Delete model"
| Owner _ -> "Delete owner"
| User _ -> "Delete user"
| Group _ -> "Delete group"
| Type _ -> "Delete type"
type AddModelArgs = { File: string }
type ModifyArchiveAttribs =
| Base_Path of string
| Projection of string
| Description of string
| Focal of single * single
| Zoom of single
| Retired of bool
| Polygon of file: string
| Model_Area of id: ModelAreaId
| Json of json: string
| Add_Related of ArchiveId
| Remove_Related of ArchiveId
| [<MainCommand; Last>] ArchiveId of id: ModelAreaId
interface IArgParserTemplate with
member this.Usage =
match this with
| Base_Path _ -> ""
| Projection _ -> ""
| Description _ -> ""
| Focal _ -> ""
| Zoom _ -> ""
| Retired _ -> ""
| Polygon _ -> ""
| Model_Area _ -> "'"
| Json _ -> "'"
| Add_Related _ -> ""
| Remove_Related _ -> ""
| ArchiveId _ -> ""
type PrincipalArgs = { Archive: ArchiveId option; Ids: string list }
type ModifyArchive =
| Name of string
| Published of bool
| Public of bool
| Expires of datetime: string
| Json of file: string
| Fence of string list
| End
| [<MainCommand; Last>] ArchiveId of id: ArchiveId
interface IArgParserTemplate with
member this.Usage =
match this with
| Name _ -> "Name of archive"
| Published _ -> "Publish archive"
| Public _ -> "Make archive public (careful!)"
| Expires _ -> "Set expiry"
| Json _ -> "Any json you fancy"
| Fence _ -> "Geofence points given as 'lng,lat',..."
| End -> "End fence list (sic)"
| ArchiveId _ -> "Archive to modify"
type DeleteModelArgs = { Force: bool; ModelId: ModelAreaId }
type ModifyModel =
| Name of string
| Description of string
| Focal of single * single
| Zoom of single
| Polygon of file: string
| Projection of string
| [<MainCommand; Last>] ModelId of id: ModelAreaId
interface IArgParserTemplate with
member this.Usage =
match this with
| Name _ -> ""
| Description _ -> ""
| Focal _ -> ""
| Zoom _ -> ""
| Polygon _ -> "Bounding polygon file"
| Projection _ -> "Projection of bounding polygon"
| ModelId _ -> ""
type ModifyArchiveArgs = {
Name: string option
Published: bool option
Public: bool option
Expires: string option
Json: string option
Fence: string list option
End: bool
ArchiveId: ArchiveId
}
type Modify =
| [<CliPrefix(CliPrefix.None); AltCommandLine("a")>] Archive of ParseResults<ModifyArchive>
| [<CliPrefix(CliPrefix.None); AltCommandLine("t")>] ArchiveAttribs of ParseResults<ModifyArchiveAttribs>
| [<CliPrefix(CliPrefix.None); AltCommandLine("m")>] Model of ParseResults<ModifyModel>
interface IArgParserTemplate with
member this.Usage =
match this with
| Archive _ -> "Modify archive"
| ArchiveAttribs _ -> "Modify archive attribs"
| Model _ -> "Modify model"
type ModifyArchiveAttribsArgs = {
BasePath: string option
Projection: string option
Description: string option
Focal: (single * single) option
Zoom: single option
Retired: bool option
Polygon: string option
ModelArea: ModelAreaId option
Json: string option
AddRelated: ArchiveId option
RemoveRelated: ArchiveId option
ArchiveId: ModelAreaId
}
type Augment =
| [<Mandatory>] Archive of guid: ArchiveId
| [<MainCommand; Last>] Files of name: string list
interface IArgParserTemplate with
member this.Usage =
match this with
| Archive _ -> "Archive id to augment"
| Files _ -> "Files to add"
type ModifyModelArgs = {
Name: string option
Description: string option
Focal: (single * single) option
Zoom: single option
Polygon: string option
Projection: string option
ModelId: ModelAreaId
}
type Resize =
| [<Mandatory>] From of first: int
| To of last: int
| [<MainCommand; Last>] Archive of id: ArchiveId
interface IArgParserTemplate with
member this.Usage =
match this with
| From _ -> "Index of first file to include"
| To _ -> "Index of last file to include"
| Archive _ -> "Archive to resize"
type AugmentArgs = { Archive: ArchiveId; Files: string list }
type Verbs =
| [<CliPrefix(CliPrefix.None)>] Add of ParseResults<Add>
| [<CliPrefix(CliPrefix.None); AltCommandLine([| "ls" |])>] List of ParseResults<List>
| [<CliPrefix(CliPrefix.None)>] Show of ParseResults<Show>
| [<CliPrefix(CliPrefix.None); AltCommandLine([| "rm" |])>] Delete of ParseResults<Delete>
| [<CliPrefix(CliPrefix.None)>] Modify of ParseResults<Modify>
| [<CliPrefix(CliPrefix.None)>] Augment of ParseResults<Augment>
| [<CliPrefix(CliPrefix.None)>] Resize of ParseResults<Resize>
| Log_Level of level: int
| [<AltCommandLine("-v")>] Version
interface IArgParserTemplate with
member this.Usage =
match this with
| List _ -> "List entities"
| Show _ -> "Show entities"
| Add _ -> "Add entities"
| Modify _ -> "Modify entities"
| Delete _ -> "Delete entities"
| Augment _ -> "Augment archive"
| Resize _ -> "Resize archive"
| Log_Level _ -> "0=Error, 1=Warning, 2=Info, 3=Debug, 4=Verbose"
| Version -> "Show the current version"
type ResizeArgs = { From: int; To: int option; Archive: ArchiveId }
type Command =
| ListArchives of ListArchiveArgs
| ListModels of ListModelArgs
| ListTypes of int
| ShowArchive of ShowArchiveArgs
| ShowModel of ShowModelArgs
| AddArchive of AddArchiveArgs
| AddSub of SubArchiveArgs
| AddModel of AddModelArgs
| AddOwner of PrincipalArgs
| AddUser of PrincipalArgs
| AddGroup of PrincipalArgs
| AddType of string
| DeleteArchive of string
| DeleteModel of DeleteModelArgs
| DeleteOwner of PrincipalArgs
| DeleteUser of PrincipalArgs
| DeleteGroup of PrincipalArgs
| DeleteType of string
| ModifyArchive of ModifyArchiveArgs
| ModifyArchiveAttribs of ModifyArchiveAttribsArgs
| ModifyModel of ModifyModelArgs
| Augment of AugmentArgs
| Resize of ResizeArgs
| Version of int
let parseArchiveId (s: string) =
match ArchiveId.TryParse s with
| true, g -> Ok g
| false, _ -> Error "Invalid archive ID format"
let parseModelAreaId (s: string) =
match ModelAreaId.TryParse s with
| true, g -> Ok g
| false, _ -> Error "Invalid model area ID format"
let parseFocal (s: string) =
let parts = s.Split ','
if parts.Length = 2 then
match Single.TryParse parts.[0], Single.TryParse parts.[1] with
| (true, x), (true, y) -> Ok (x, y)
| _ -> Error "Invalid focal point format (should be 'x,y')"
else
Error "Invalid focal point format (should be 'x,y')"
let argParser: Arg<Command * int> =
fargo {
let! logLevel =
opt "log-level" null "level" "Log level (0=Error, 1=Warning, 2=Info, 3=Debug, 4=Verbose)"
|> optParse (fun s ->
match Int32.TryParse s with
| true, v when v >= 0 && v <= 4 -> Ok v
| true, _ -> Error "Log level must be between 0 and 4"
| false, _ -> Error "Invalid log level value"
)
|> defaultValue 2
let! version = flag "version" "v" "Show version"
if version then
return Version logLevel, logLevel
else
match!
cmd "list" "ls" "List entities" |>> CmdType.ListCmd
<|> (cmd "show" "s" "Show entity details" |>> CmdType.ShowCmd)
<|> (cmd "add" "a" "Add entities" |>> CmdType.AddCmd)
<|> (cmd "delete" "rm" "Delete entities" |>> CmdType.DeleteCmd)
<|> (cmd "modify" "m" "Modify entities" |>> CmdType.ModifyCmd)
<|> (cmd "augment" "au" "Augment archive" |>> CmdType.AugmentCmd)
<|> (cmd "resize" "r" "Resize archive" |>> CmdType.ResizeCmd)
with
| CmdType.ListCmd ->
match!
cmd "archives" "a" "List archives" |>> ListType.Archives
<|> (cmd "models" "m" "List models" |>> ListType.Models)
<|> (cmd "types" "t" "List types" |>> ListType.Types)
with
| ListType.Archives ->
let! all = flag "all" null "List all archives"
and! retired = flag "retired" null "List retired archives"
and! refs = flag "refs" null "List referencing archives"
and! archType = opt "type" null "type" "List archives of the given type"
and! owner = opt "owner" null "user" "List archives owned by user"
and! user = opt "user" null "user" "List archives for user"
and! group = opt "group" null "group" "List archives for group"
and! archiveName = opt "archive-name" null "pattern" "List archives matching pattern"
and! modelName = opt "model-name" null "pattern" "List archives matching model area"
and! verbose = flag "verbose" "V" "Detailed listing"
and! json = flag "json" null "Json output"
return
ListArchives {
All = all
Retired = retired
Refs = refs
Type = archType
Owner = owner
User = user
Group = group
ArchiveName = archiveName
ModelName = modelName
Verbose = verbose
Json = json
},
logLevel
| ListType.Models ->
let! modelName = opt "model-name" null "regex" "List models matching regex"
and! verbose = flag "verbose" null "Detailed listing"
and! json = flag "json" null "Json output"
return ListModels { ModelName = modelName; Verbose = verbose; Json = json }, logLevel
| ListType.Types -> return ListTypes logLevel, logLevel
| CmdType.ShowCmd ->
match!
cmd "archive" "a" "Show archive details" |>> ShowType.Archive
<|> (cmd "model" "m" "Show model details" |>> ShowType.Model)
with
| ShowType.Archive ->
let! all = flag "all" null "List all available files (for resize)"
and! refs = flag "refs" null "List archives referencing archive"
and! related = flag "related" null "List related archives"
and! files = flag "files" null "List file names"
and! json = flag "json" null "Json output"
and! archiveId = arg "archive-id" "Archive ID" |> reqArg |> parse parseArchiveId
return
ShowArchive {
All = all
Refs = refs
Related = related
Files = files
Json = json
ArchiveId = archiveId
},
logLevel
| ShowType.Model ->
let! verbose = flag "verbose" null "Detailed listing"
and! json = flag "json" null "Json output"
and! modelId = arg "model-id" "Model ID" |> reqArg |> parse parseModelAreaId
return ShowModel { Verbose = verbose; Json = json; ModelId = modelId }, logLevel
| CmdType.AddCmd ->
match!
cmd "archive" "a" "Add archive" |>> AddType.Archive
<|> (cmd "sub" "s" "Add sub-archive" |>> AddType.Sub)
<|> (cmd "model" "m" "Add model" |>> AddType.Model)
<|> (cmd "owner" "o" "Add owner" |>> AddType.Owner)
<|> (cmd "user" "u" "Add user" |>> AddType.User)
<|> (cmd "group" "g" "Add group" |>> AddType.Group)
<|> (cmd "type" "t" "Add type" |>> AddType.Type)
with
| AddType.Archive ->
let! index = opt "index" null "file" "index.json"
and! noSort = flag "no-sort" null "No sorting of files"
and! id = opt "id" null "guid" "Archive id to update" |> optParse parseArchiveId
and! force = flag "force" null "Forcefully add or update"
and! published =
opt "published" null "bool" "Publish archive (default: true)"
|> optParse (fun s ->
match Boolean.TryParse s with
| true, v -> Ok v
| false, _ -> Error "Invalid boolean value"
)
and! files =
arg "files" "Files to add"
|> Pipe.orStdIn
// |> nonEmpty "No files to add"
|> listParse Ok
return
AddArchive {
Index = index
NoSort = noSort
Id = id
Force = force
Published = published
Files = files
},
logLevel
| AddType.Sub ->
let! refId = opt "ref" null "guid" "Reference archive" |> optParse parseArchiveId |> reqOpt
and! from =
opt "from" null "int" "Index of first file to include"
|> optParse (fun s ->
match Int32.TryParse s with
| true, v -> Ok v
| false, _ -> Error "Invalid integer value"
)
|> reqOpt
and! to' =
opt "to" null "int" "Index of last file to include"
|> optParse (fun s ->
match Int32.TryParse s with
| true, v -> Ok v
| false, _ -> Error "Invalid integer value"
)
and! published =
opt "published" null "bool" "Publish archive (default: true)"
|> optParse (fun s ->
match Boolean.TryParse s with
| true, v -> Ok v
| false, _ -> Error "Invalid boolean value"
)
and! name = arg "name" "Name of the new archive" |> reqArg
return
AddSub {
Ref = refId
From = from
To = to'
Published = published
Name = name
},
logLevel
| AddType.Model ->
let! file = arg "file" "model.json" |> reqArg
return AddModel { File = file }, logLevel
| AddType.Owner ->
let! archive = opt "archive" null "id" "Apply to archive" |> optParse parseArchiveId
and! ids =
arg "ids" "Identities"
|> Pipe.orStdIn
|> nonEmpty "No ids to add"
|> listParse Ok
return AddOwner { Archive = archive; Ids = ids }, logLevel
| AddType.User ->
let! archive = opt "archive" null "id" "Apply to archive" |> optParse parseArchiveId
and! ids =
arg "ids" "Identities"
|> Pipe.orStdIn
|> nonEmpty "No ids to add"
|> listParse Ok
return AddUser { Archive = archive; Ids = ids }, logLevel
| AddType.Group ->
let! archive = opt "archive" null "id" "Apply to archive" |> optParse parseArchiveId
and! ids =
arg "ids" "Identities"
|> Pipe.orStdIn
|> nonEmpty "No ids to add"
|> listParse Ok
return AddGroup { Archive = archive; Ids = ids }, logLevel
| AddType.Type ->
let! typeName = arg "type" "Type name" |> reqArg
return AddType typeName, logLevel
| CmdType.DeleteCmd ->
match!
cmd "archive" "a" "Delete archive" |>> DeleteType.Archive
<|> (cmd "model" "m" "Delete model" |>> DeleteType.Model)
<|> (cmd "owner" "o" "Delete owner" |>> DeleteType.Owner)
<|> (cmd "user" "u" "Delete user" |>> DeleteType.User)
<|> (cmd "group" "g" "Delete group" |>> DeleteType.Group)
<|> (cmd "type" "t" "Delete type" |>> DeleteType.Type)
with
| DeleteType.Archive ->
let! id = arg "id" "Archive ID" |> reqArg
return DeleteArchive id, logLevel
| DeleteType.Model ->
let! force = flag "force" null "Force delete"
and! modelId = arg "model-id" "Model ID" |> reqArg |> parse parseModelAreaId
return DeleteModel { Force = force; ModelId = modelId }, logLevel
| DeleteType.Owner ->
let! archive = opt "archive" null "id" "Apply to archive" |> optParse parseArchiveId
and! ids =
arg "ids" "Identities"
|> Pipe.orStdIn
|> nonEmpty "No ids to add"
|> listParse Ok
return DeleteOwner { Archive = archive; Ids = ids }, logLevel
| DeleteType.User ->
let! archive = opt "archive" null "id" "Apply to archive" |> optParse parseArchiveId
and! ids =
arg "ids" "Identities"
|> Pipe.orStdIn
|> nonEmpty "No ids to add"
|> listParse Ok
return DeleteUser { Archive = archive; Ids = ids }, logLevel
| DeleteType.Group ->
let! archive = opt "archive" null "id" "Apply to archive" |> optParse parseArchiveId
and! ids =
arg "ids" "Identities"
|> Pipe.orStdIn
|> nonEmpty "No ids to add"
|> listParse Ok
return DeleteGroup { Archive = archive; Ids = ids }, logLevel
| DeleteType.Type ->
let! typeName = arg "type" "Type name" |> reqArg
return DeleteType typeName, logLevel
| CmdType.ModifyCmd ->
match!
cmd "archive" "a" "Modify archive" |>> ModifyType.Archive
<|> (cmd "archive-attribs" "t" "Modify archive attributes"
|>> ModifyType.ArchiveAttribs)
<|> (cmd "model" "m" "Modify model" |>> ModifyType.Model)
with
| ModifyType.Archive ->
let! name = opt "name" null "name" "Name of archive"
and! published =
opt "published" null "bool" "Publish archive"
|> optParse (fun s ->
match Boolean.TryParse s with
| true, v -> Ok v
| false, _ -> Error "Invalid boolean value"
)
and! public' =
opt "public" null "bool" "Make archive public (careful!)"
|> optParse (fun s ->
match Boolean.TryParse s with
| true, v -> Ok v
| false, _ -> Error "Invalid boolean value"
)
and! expires = opt "expires" null "datetime" "Set expiry"
and! json = opt "json" null "file" "Any json you fancy"
and! fence =
opt "fence" null "points" "Geofence points given as 'lng,lat',...'"
|> optParse (fun s -> Ok (s.Split (',') |> Array.toList))
and! endFlag = flag "end" null "End fence list (sic)"
and! archiveId = arg "archive-id" "Archive to modify" |> reqArg |> parse parseArchiveId
return
ModifyArchive {
Name = name
Published = published
Public = public'
Expires = expires
Json = json
Fence = fence
End = endFlag
ArchiveId = archiveId
},
logLevel
| ModifyType.ArchiveAttribs ->
let! basePath = opt "base-path" null "path" "Base path"
and! projection = opt "projection" null "proj" "Projection"
and! description = opt "description" null "desc" "Description"
and! focal = opt "focal" null "x,y" "Focal point" |> optParse parseFocal
and! zoom =
opt "zoom" null "level" "Zoom level"
|> optParse (fun s ->
match Single.TryParse s with
| true, v -> Ok v
| false, _ -> Error "Invalid float value"
)
and! retired =
opt "retired" null "bool" "Retired status"
|> optParse (fun s ->
match Boolean.TryParse s with
| true, v -> Ok v
| false, _ -> Error "Invalid boolean value"
)
and! polygon = opt "polygon" null "file" "Polygon file"
and! modelArea = opt "model-area" null "id" "Model area ID" |> optParse parseModelAreaId
and! json = opt "json" null "json" "JSON data"
and! addRelated = opt "add-related" null "id" "Add related archive" |> optParse parseArchiveId
and! removeRelated =
opt "remove-related" null "id" "Remove related archive"
|> optParse parseArchiveId
and! archiveId = arg "archive-id" "Archive ID" |> reqArg |> parse parseModelAreaId
return
ModifyArchiveAttribs {
BasePath = basePath
Projection = projection
Description = description
Focal = focal
Zoom = zoom
Retired = retired
Polygon = polygon
ModelArea = modelArea
Json = json
AddRelated = addRelated
RemoveRelated = removeRelated
ArchiveId = archiveId
},
logLevel
| ModifyType.Model ->
let! name = opt "name" null "name" "Model name"
and! description = opt "description" null "desc" "Description"
and! focal = opt "focal" null "x,y" "Focal point" |> optParse parseFocal
and! zoom =
opt "zoom" null "level" "Zoom level"
|> optParse (fun s ->
match Single.TryParse s with
| true, v -> Ok v
| false, _ -> Error "Invalid float value"
)
and! polygon = opt "polygon" null "file" "Bounding polygon file"
and! projection = opt "projection" null "proj" "Projection of bounding polygon"
and! modelId = arg "model-id" "Model ID" |> reqArg |> parse parseModelAreaId
return
ModifyModel {
Name = name
Description = description
Focal = focal
Zoom = zoom
Polygon = polygon
Projection = projection
ModelId = modelId
},
logLevel
| CmdType.AugmentCmd ->
let! archive =
opt "archive" null "guid" "Archive id to augment"
|> optParse parseArchiveId
|> reqOpt
and! files =
arg "files" "Files to add"
|> Pipe.orStdIn
|> nonEmpty "No files to add"
|> listParse Ok
return Augment { Archive = archive; Files = files }, logLevel
| CmdType.ResizeCmd ->
let! from =
opt "from" null "int" "Index of first file to include"
|> optParse (fun s ->
match Int32.TryParse s with
| true, v -> Ok v
| false, _ -> Error "Invalid integer value"
)
|> reqOpt
and! to' =
opt "to" null "int" "Index of last file to include"
|> optParse (fun s ->
match Int32.TryParse s with
| true, v -> Ok v
| false, _ -> Error "Invalid integer value"
)
and! archive = arg "archive" "Archive to resize" |> reqArg |> parse parseArchiveId
return Resize { From = from; To = to'; Archive = archive }, logLevel
}

View File

@@ -1,13 +1,12 @@
module Listing
open System.Text.RegularExpressions
open Archmaester.Dto
open Argu
open FSharpPlus
open Serilog
open Args
open Oceanbox.DataAgent
open System.Text.RegularExpressions
open Args
open Archmaester.Dto
open Oceanbox.DataAgent
let getArchives archiveIds =
archiveIds
@@ -21,8 +20,10 @@ let listModels verbose (filterM: ModelArea -> bool) (filterA: ArchiveProps -> bo
|> Result.map (
Array.filter filterM
>> Array.iter (fun m ->
let archives = ArchiveCli.getModelAreaArchives m.modelAreaId |> Async.RunSynchronously
let archives =
ArchiveCli.getModelAreaArchives m.modelAreaId |> Async.RunSynchronously
match archives with
| Error e -> Log.Error e
| Ok arch ->
let ax = arch |> Array.filter filterA
@@ -32,67 +33,64 @@ let listModels verbose (filterM: ModelArea -> bool) (filterA: ArchiveProps -> bo
else
Utils.printModelTerse m
Utils.printArchivesTerse ax
printfn ""
| Error e -> Log.Error e)
printfn "")
)
|> Result.defaultWith (fun e -> Log.Error("Listing.listModels error fetching model areas {Error}", e))
|> Result.defaultWith (fun e -> Log.Error ("Listing.listModels error fetching model areas {Error}", e))
let listArchives (args: ParseResults<ListArchive>) =
let verbose = args.Contains ListArchive.Verbose
let retired = args.Contains ListArchive.Retired
let listArchives (args: ListArchiveArgs) =
let verbose = args.Verbose
let retired = args.Retired
let matchO (a: ArchiveDto) =
if args.Contains ListArchive.Owner then
let pat = args.GetResult ListArchive.Owner
a.acl.owners |> Array.exists (fun y -> Regex.IsMatch(y, pat))
else
true
match args.Owner with
| None -> true
| Some owner ->
let pat = owner
a.acl.owners |> Array.exists (fun y -> Regex.IsMatch (y, pat))
let matchU (a: ArchiveDto) =
if args.Contains ListArchive.User then
let pat = args.GetResult ListArchive.User
a.acl.users |> Array.exists (fun y -> Regex.IsMatch(y, pat))
|| a.acl.owners |> Array.exists (fun y -> Regex.IsMatch(y, pat))
else
true
match args.User with
| None -> true
| Some user ->
let pat = user
a.acl.users |> Array.exists (fun y -> Regex.IsMatch (y, pat))
|| a.acl.owners |> Array.exists (fun y -> Regex.IsMatch (y, pat))
let matchG (a: ArchiveDto) =
if args.Contains ListArchive.Group then
let pat = args.GetResult ListArchive.Group
a.acl.groups |> Array.exists (fun y -> Regex.IsMatch(y, pat))
else
true
match args.Group with
| None -> true
| Some group ->
let pat = group
a.acl.groups |> Array.exists (fun y -> Regex.IsMatch (y, pat))
let matchM (m: ModelArea) =
if args.Contains ListArchive.Model_Name then
let pat = args.GetResult ListArchive.Model_Name
Regex.IsMatch(m.name, pat)
else
true
match args.ModelName with
| None -> true
| Some modelName ->
let pat = modelName
Regex.IsMatch (m.name, pat)
let matchA (a: ArchiveProps) =
if args.Contains Archive_Name then
let pat = args.GetResult Archive_Name
printfn "List Archives pattern %s" pat
Regex.IsMatch(a.name, pat)
else
true
match args.ArchiveName with
| None -> true
| Some archiveName ->
let pat = archiveName
printfn $"List Archives pattern %s{pat}"
Regex.IsMatch (a.name, pat)
let matchBox (a: ArchiveProps) =
matchA a //&& matchO a && matchU a && matchG a
// NOTE: Only checks for archive props
let matchBox (a: ArchiveProps) = matchA a //&& matchO a && matchU a && matchG a
if args.Contains ListArchive.All then
if args.All then
listModels verbose matchM matchA
elif args.Contains ListArchive.Type then
elif args.Type.IsSome then
let t =
let spec = args.GetResult ListArchive.Type
let spec = args.Type |> Option.get
try
ArchiveType.FromString spec
with _ ->
Log.Warning $"Unknown archive type: {spec}"
Log.Warning $"Unknown archive type: %s{spec}"
ArchiveType.Any
let archiveF (a: ArchiveProps) = a.archiveType = t && matchBox a

View File

@@ -1,8 +1,9 @@
module Archivist
open Fargo
open Serilog
open Serilog.Events
open Argu
open Args
let configureSerilog level =
@@ -17,103 +18,96 @@ let configureSerilog level =
LoggerConfiguration()
.MinimumLevel.Is(n)
.WriteTo.Console(theme = Serilog.Sinks.SystemConsole.Themes.ConsoleTheme.None)
.CreateLogger()
.CreateLogger ()
let addHandler (args: ParseResults<Add>) =
if args.Contains Add.Archive then
ArchiveCli.addArchive (args.GetResult Add.Archive)
elif args.Contains Add.Sub then
ArchiveCli.createSubArchive (args.GetResult Add.Sub)
elif args.Contains Add.Model then
ModelAreaCli.addModel (args.GetResult Add.Model)
elif args.Contains Add.Owner then
AclCli.addOwners (args.GetResult Add.Owner)
elif args.Contains Add.User then
AclCli.addUsers (args.GetResult Add.User)
elif args.Contains Add.Group then
AclCli.addGroups (args.GetResult Add.Group)
elif args.Contains Add.Type then
ArchiveCli.addType (args.GetResult Add.Type)
else
printfn $"{args.Parser.PrintUsage()}"
let executeCommand (ct: System.Threading.CancellationToken) (cmd: Command * int) =
task {
let logLevel = snd cmd
let listHandler (args: ParseResults<List>) =
if args.Contains List.Archives then
Listing.listArchives (args.GetResult List.Archives)
elif args.Contains List.Models then
ModelAreaCli.listModels (args.GetResult List.Models)
else
eprintfn "No list subcommand listed"
eprintfn "%s" (args.Parser.PrintUsage())
()
Log.Logger <- configureSerilog logLevel
let showHandler (args: ParseResults<Show>) =
if args.Contains Show.Archive then
let show = args.GetResult Show.Archive
printfn $"Archivist connection string: %s{Settings.archmaesterUrl}"
if show.Contains Related then
ArchiveCli.showRelatedArchives show
else
ArchiveCli.showArchive show
elif args.Contains Show.Model then
ModelAreaCli.showModel (args.GetResult Show.Model)
else
args.Parser.PrintUsage() |> printfn "%s"
let deleteHandler (args: ParseResults<Delete>) =
if args.Contains Delete.Archive then
ArchiveCli.deleteArchives args
elif args.Contains Delete.Model then
ModelAreaCli.deleteModel (args.GetResult Delete.Model)
elif args.Contains Delete.Owner then
AclCli.deleteOwners (args.GetResult Delete.Owner)
elif args.Contains Delete.User then
AclCli.deleteUsers (args.GetResult Delete.User)
elif args.Contains Delete.Group then
AclCli.deleteGroups (args.GetResult Delete.Group)
elif args.Contains Delete.Type then
ArchiveCli.deleteType (args.GetResult Delete.Type)
else
()
let modifyHandler (args: ParseResults<Modify>) =
if args.Contains Modify.Archive then
ArchiveCli.modifyArchive (args.GetResult Modify.Archive)
elif args.Contains Modify.ArchiveAttribs then
ArchiveCli.modifyArchiveAttribs (args.GetResult Modify.ArchiveAttribs)
elif args.Contains Modify.Model then
ModelAreaCli.modifyModel (args.GetResult Modify.Model)
else
()
match fst cmd with
| ModifyArchive args ->
ArchiveCli.modifyArchive args
return 0
| ModifyArchiveAttribs args ->
ArchiveCli.modifyArchiveAttribs args
return 0
| ModifyModel args ->
ModelAreaCli.modifyModel args
return 0
| ShowArchive args ->
if args.Related then
ArchiveCli.showRelatedArchives args
return 0
else
ArchiveCli.showArchive args
return 0
| ShowModel args ->
ModelAreaCli.showModel args
return 0
| ListArchives args ->
Listing.listArchives args
return 0
| ListModels args ->
ModelAreaCli.listModels args
return 0
| ListTypes args ->
// NOTE: Not yet defined
// ArchiveCli.c args
return 0
| AddArchive args ->
ArchiveCli.addArchive args
return 0
| AddSub args ->
ArchiveCli.createSubArchive args
return 0
| AddModel args ->
ModelAreaCli.addModel args
return 0
| AddOwner args ->
AclCli.addOwners args
return 0
| AddGroup args ->
AclCli.addGroups args
return 0
| AddUser args ->
AclCli.addUsers args
return 0
| AddType args ->
ArchiveCli.addType args
return 0
| DeleteArchive args ->
ArchiveCli.deleteArchives args
return 0
| DeleteModel args ->
ModelAreaCli.deleteModel args
return 0
| DeleteOwner args ->
AclCli.deleteOwners args
return 0
| DeleteGroup args ->
AclCli.deleteGroups args
return 0
| DeleteUser args ->
AclCli.deleteUsers args
return 0
| DeleteType args ->
ArchiveCli.deleteType args
return 0
| Augment args ->
ArchiveCli.augmentArchive args
return 0
| Resize args ->
ArchiveCli.resizeArchive args
return 0
| Version _ ->
showVersion ()
return 0
}
[<EntryPoint>]
let main argv =
let parser =
ArgumentParser.Create<Verbs>(programName = "Archivist", errorHandler = errorHandler)
let args = parser.Parse argv
Log.Logger <- configureSerilog (args.GetResult(Log_Level, defaultValue = 3))
printfn $"Archivist connection string: {Settings.archmaesterUrl}"
if args.Contains Add then
addHandler (args.GetResult Add)
elif args.Contains List then
listHandler (args.GetResult List)
elif args.Contains Show then
showHandler (args.GetResult Show)
elif args.Contains Delete then
deleteHandler (args.GetResult Delete)
elif args.Contains Modify then
modifyHandler (args.GetResult Modify)
elif args.Contains Augment then
ArchiveCli.augmentArchive (args.GetResult Augment)
elif args.Contains Resize then
ArchiveCli.resizeArchive (args.GetResult Resize)
elif args.Contains Version then
showVersion ()
else
parser.PrintUsage() |> printfn "%s"
0
run "archivist" argParser argv executeCommand

View File

@@ -1,15 +1,15 @@
module ModelAreaCli
open System
open System.IO
open Archmaester
open Argu
open Args
open FSharpPlus
open Serilog
open System
open System.IO
open Args
open ArchiveIndex
open Oceanbox.DataAgent
open Archmaester
open Archmaester.Dto
open Oceanbox.DataAgent
let inline private execAsync job =
job
@@ -24,25 +24,25 @@ let inline private execAsync job =
/// <param name="modelArea">The model area</param>
let postModelArea modelArea =
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let manage = api.adminApi ()
async { return! manage.addModelArea modelArea })
let findModelAreaId (name: string) : Async<Result<ModelAreaId, string>> =
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let archmaester = api.adminApi ()
archmaester.queryModelAreaId name)
let listModelArchives modelAreaId =
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let inventory = api.modelAreaApi ()
async {
try
let! m = inventory.getModelArea modelAreaId
return m |> Option.toResultWith "error"
let! m = inventory.getModelArea modelAreaId
return m |> Option.toResultWith "error"
with exn ->
Log.Error exn.Message
return Error exn.Message
@@ -50,7 +50,7 @@ let listModelArchives modelAreaId =
let listModelArchivesAdmin (mid, filter: ArchiveFilter) =
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let archmaester = api.archiveApi ()
let tp =
@@ -67,16 +67,16 @@ let listModelArchivesAdmin (mid, filter: ArchiveFilter) =
let getModelArea modelId =
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let archmaester = api.modelAreaApi ()
async {
let! m = archmaester.getModelArea modelId
return m |> Option.toResultWith "error"
let! m = archmaester.getModelArea modelId
return m |> Option.toResultWith "error"
})
let getBaseModelArea () =
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let archmaester = api.modelAreaApi ()
async {
@@ -90,14 +90,13 @@ let getBaseModelArea () =
let getBaseModelAreas (helloWorld: Guid) : Result<ModelArea[], string> =
Log.Information $"Fetching model area {helloWorld} model areas:"
withCliAuth (fun auth ->
let intra = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = intra.modelAreaApi()
api.getSubModelAreas helloWorld
|> Async.map Ok)
let intra = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let api = intra.modelAreaApi ()
api.getSubModelAreas helloWorld |> Async.map Ok)
|> Async.RunSynchronously
let addModel (args: ParseResults<AddModel>) =
let file = args.GetResult AddModel.File |> Path.GetFullPath
let addModel (args: AddModelArgs) =
let file = args.File |> Path.GetFullPath
let basePath = Path.GetDirectoryName file
match readModelAreaFile file with
@@ -122,7 +121,7 @@ let addModel (args: ParseResults<AddModel>) =
Log.Debug $"{mdl}"
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let adminApi = api.adminApi ()
async {
@@ -130,7 +129,7 @@ let addModel (args: ParseResults<AddModel>) =
match! adminApi.addModelArea mdl with
| Ok mid ->
Log.Information $"Added model {mid}"
return Ok()
return Ok ()
| Error e ->
Log.Error $"Error: {e}"
return Error e
@@ -140,29 +139,29 @@ let addModel (args: ParseResults<AddModel>) =
|> execAsync
| Error e -> Log.Error $"Error: {e}"
let listModels (args: ParseResults<ListModel>) =
let listModels (args: ListModelArgs) =
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let inventory = api.modelAreaApi ()
async {
try
let! models =inventory.getSubModelAreas Guid.Empty
let! models = inventory.getSubModelAreas Guid.Empty
models |> Array.iter (fun m -> printfn $"{m.name}: {m.modelAreaId}")
return Ok()
return Ok ()
with exn ->
return Error exn.Message
})
|> execAsync
let showModel (args: ParseResults<ShowModel>) =
let showModel (args: ShowModelArgs) =
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let inventory = api.modelAreaApi ()
async {
try
match! inventory.getModelArea (args.GetResult ShowModel.Model) with
match! inventory.getModelArea (args.ModelId) with
| Some m ->
printfn $"{m.name}: {{{m.modelAreaId}}}"
printfn $" Desc.: {m.description}"
@@ -174,7 +173,7 @@ let showModel (args: ParseResults<ShowModel>) =
if m.parent.IsSome then
printfn $" Parent: {m.parent.Value}"
return Ok()
return Ok ()
| None ->
Log.Error $"error"
return Error "error"
@@ -183,36 +182,34 @@ let showModel (args: ParseResults<ShowModel>) =
})
|> execAsync
let modifyModel (args: ParseResults<ModifyModel>) =
let modifyModel (args: ModifyModelArgs) =
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let modelApi = api.modelAreaApi ()
let adminApi = api.adminApi ()
let poly (m: ModelArea) =
let proj = args.TryGetResult ModifyModel.Projection
let proj = args.Projection
if args.Contains ModifyModel.Polygon then
readBoundingPolygon proj (args.GetResult ModifyModel.Polygon) |> Option.ofResult
elif m.polygon = [||] then
None
else
Some m.polygon
match args.Polygon with
| None -> Some m.polygon
| Some poly when m.polygon = [||] -> None
| Some poly -> readBoundingPolygon proj poly |> Option.ofResult
async {
try
match! modelApi.getModelArea (args.GetResult ModifyModel.ModelId) with
match! modelApi.getModelArea args.ModelId with
| Some m ->
let form: Forms.ModelAreaForm = {
name = args.GetResult(ModifyModel.Name, m.name)
description = args.GetResult(ModifyModel.Description, m.description)
focalPoint = args.GetResult(ModifyModel.Focal, m.focalPoint)
defaultZoom = args.GetResult(ModifyModel.Zoom, m.defaultZoom)
name = args.Name |> Option.defaultValue m.name
description = args.Description |> Option.defaultValue m.description
focalPoint = args.Focal |> Option.defaultValue m.focalPoint
defaultZoom = args.Zoom |> Option.defaultValue m.defaultZoom
json = if m.json = "" then None else Some m.json
geometry = poly m
}
return! adminApi.updateModelArea (args.GetResult ModifyModel.ModelId, form)
return! adminApi.updateModelArea (args.ModelId, form)
| None ->
Log.Error $"error"
return Error "error"
@@ -221,13 +218,13 @@ let modifyModel (args: ParseResults<ModifyModel>) =
})
|> execAsync
let deleteModel (args: ParseResults<DeleteModel>) =
let mid = args.GetResult DeleteModel.Model
let force = args.Contains DeleteModel.Force
let deleteModel (args: DeleteModelArgs) =
let mid = args.ModelId
let force = args.Force
let action () =
withCliAuth (fun auth ->
let api = Remoting.v1.InternalApi(Settings.archmaesterUrl, auth)
let api = Remoting.v1.InternalApi (Settings.archmaesterUrl, auth)
let adminApi = api.adminApi ()
async {
@@ -235,7 +232,7 @@ let deleteModel (args: ParseResults<DeleteModel>) =
match! adminApi.deleteModelArea mid with
| Ok _ ->
Log.Information $"Deleted model {mid} and all dependent archives"
return Ok()
return Ok ()
| Error e ->
Log.Error $"Error: {e}"
return Error e
@@ -250,7 +247,7 @@ let deleteModel (args: ParseResults<DeleteModel>) =
printfn "WARNING: This will remove the modeal area and ALL dependent archives!"
printfn "Do you want to continue YES/[no]?"
if Console.ReadLine() = "YES" then
if Console.ReadLine () = "YES" then
action ()
else
Environment.Exit 0

View File

@@ -3,7 +3,7 @@ module Settings
open System
let base64enc (s: string) =
Text.ASCIIEncoding.UTF8.GetBytes(s) |> Convert.ToBase64String
Text.ASCIIEncoding.UTF8.GetBytes s |> Convert.ToBase64String
let tryGetEnv =
Environment.GetEnvironmentVariable
@@ -17,5 +17,4 @@ let archmaesterUrl =
let cliAuth = tryGetEnv "ARCHMAESTER_AUTH" |> Option.map base64enc
let julianStart = DateTime.Parse("1858-11-17 00:00:00Z").ToUniversalTime()
let julianStart = DateTime.Parse("1858-11-17 00:00:00Z").ToUniversalTime ()

View File

@@ -0,0 +1,859 @@
{
"version": 2,
"dependencies": {
"net10.0": {
"Fargo.CmdLine": {
"type": "Direct",
"requested": "[1.7.5, )",
"resolved": "1.7.5",
"contentHash": "u2fPQAmwJSAfCJRUDoN1NEg1ZmkZw5NsbvgLHhkCjLCkefEiRS/2EVu0W5BcxgkehkgI0Wx6E2qn7rnpPR2YKQ==",
"dependencies": {
"FSharp.Core": "8.0.100"
}
},
"FSharp.Core": {
"type": "Direct",
"requested": "[9.0.303, )",
"resolved": "9.0.303",
"contentHash": "6JlV8aD8qQvcmfoe/PMOxCHXc0uX4lR23u0fAyQtnVQxYULLoTZgwgZHSnRcuUHOvS3wULFWcwdnP1iwslH60g=="
},
"FSharp.Data": {
"type": "Direct",
"requested": "[6.4.1, )",
"resolved": "6.4.1",
"contentHash": "P/ShAsNsuKrV9cpK7Mb/fSJ/kpinjOnVGRDXDzi/dYECS/lmlDrAvNVlodPbqCo5hIXvMMkKMc5C4f8ULLW7JQ==",
"dependencies": {
"FSharp.Core": "6.0.1",
"FSharp.Data.Csv.Core": "6.4.1",
"FSharp.Data.Html.Core": "6.4.1",
"FSharp.Data.Http": "6.4.1",
"FSharp.Data.Json.Core": "6.4.1",
"FSharp.Data.Runtime.Utilities": "6.4.1",
"FSharp.Data.WorldBank.Core": "6.4.1",
"FSharp.Data.Xml.Core": "6.4.1"
}
},
"FSharpPlus": {
"type": "Direct",
"requested": "[1.7.0, )",
"resolved": "1.7.0",
"contentHash": "9+PXT3nG7K5bzgYOzxgwZu5ij25BH7OtMkMJUrWkf+HcfbvsEGCvIf3InF8MCvJ5lO02NfGb9fC8slLEytqw0Q==",
"dependencies": {
"FSharp.Core": "6.0.6"
}
},
"Microsoft.EntityFrameworkCore": {
"type": "Direct",
"requested": "[9.0.1, )",
"resolved": "9.0.1",
"contentHash": "E25w4XugXNykTr5Y/sLDGaQ4lf67n9aXVPvsdGsIZjtuLmbvb9AoYP8D50CDejY8Ro4D9GK2kNHz5lWHqSK+wg==",
"dependencies": {
"Microsoft.EntityFrameworkCore.Abstractions": "9.0.1",
"Microsoft.EntityFrameworkCore.Analyzers": "9.0.1",
"Microsoft.Extensions.Caching.Memory": "9.0.1",
"Microsoft.Extensions.Logging": "9.0.1"
}
},
"Microsoft.EntityFrameworkCore.Design": {
"type": "Direct",
"requested": "[9.0.1, )",
"resolved": "9.0.1",
"contentHash": "/pchcadGU57ChRYH0/bvLTeU/n1mpWO+0pVK7pUzzuwRu5SIQb8dVMZVPhzvEI2VO5rP1yricSQBBnOmDqQhvg==",
"dependencies": {
"Humanizer.Core": "2.14.1",
"Microsoft.Build.Framework": "17.8.3",
"Microsoft.Build.Locator": "1.7.8",
"Microsoft.CodeAnalysis.CSharp": "4.8.0",
"Microsoft.CodeAnalysis.CSharp.Workspaces": "4.8.0",
"Microsoft.CodeAnalysis.Workspaces.MSBuild": "4.8.0",
"Microsoft.EntityFrameworkCore.Relational": "9.0.1",
"Microsoft.Extensions.Caching.Memory": "9.0.1",
"Microsoft.Extensions.Configuration.Abstractions": "9.0.1",
"Microsoft.Extensions.DependencyModel": "9.0.1",
"Microsoft.Extensions.Logging": "9.0.1",
"Mono.TextTemplating": "3.0.0"
}
},
"Microsoft.EntityFrameworkCore.Tools": {
"type": "Direct",
"requested": "[9.0.1, )",
"resolved": "9.0.1",
"contentHash": "Yu0+5OInULF5PJX/ILZFWNT4ODBv4CSbS1TCXhkZYI6FxC7WguanhmCY9DL6U1R1YQ1tC38RspbxklmBBuk1SA==",
"dependencies": {
"Microsoft.EntityFrameworkCore.Design": "9.0.1"
}
},
"Serilog": {
"type": "Direct",
"requested": "[4.2.0, )",
"resolved": "4.2.0",
"contentHash": "gmoWVOvKgbME8TYR+gwMf7osROiWAURterc6Rt2dQyX7wtjZYpqFiA/pY6ztjGQKKV62GGCyOcmtP1UKMHgSmA=="
},
"Serilog.Sinks.Console": {
"type": "Direct",
"requested": "[6.0.0, )",
"resolved": "6.0.0",
"contentHash": "fQGWqVMClCP2yEyTXPIinSr5c+CBGUvBybPxjAGcf7ctDhadFhrQw03Mv8rJ07/wR5PDfFjewf2LimvXCDzpbA==",
"dependencies": {
"Serilog": "4.0.0"
}
},
"Thoth.Json.Net": {
"type": "Direct",
"requested": "[12.0.0, )",
"resolved": "12.0.0",
"contentHash": "n2YyONYdWCFS4Pu7wrqgV/l5tPuN+t3gxEfs/2RwqCiQkRnbgKs9dK61zHeZS5YganAoFbxLSwbaNL7SvSrS9g==",
"dependencies": {
"FSharp.Core": "4.7.2",
"Fable.Core": "3.1.6",
"Newtonsoft.Json": "13.0.1"
}
},
"Dapper": {
"type": "Transitive",
"resolved": "2.1.35",
"contentHash": "YKRwjVfrG7GYOovlGyQoMvr1/IJdn+7QzNXJxyMh0YfFF5yvDmTYaJOVYWsckreNjGsGSEtrMTpnzxTUq/tZQw=="
},
"Dapr.Common": {
"type": "Transitive",
"resolved": "1.16.0",
"contentHash": "6JyPI8LxNXSjSpO9vTdbfJh78zOiiC0sgeaXuY8O6SJQh2epaRdEPw0UpamNnld3CkDjp69/VCphox7pU/lh1Q==",
"dependencies": {
"Dapr.Protos": "1.16.0",
"Google.Api.CommonProtos": "2.17.0",
"Google.Protobuf": "3.32.0",
"Grpc.Net.Client": "2.71.0",
"Microsoft.Extensions.Configuration": "9.0.8",
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
"Microsoft.Extensions.DependencyInjection": "9.0.8",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Http": "9.0.8",
"Microsoft.Extensions.Logging": "9.0.8",
"Microsoft.Extensions.Logging.Abstractions": "9.0.8",
"Microsoft.Extensions.Options": "9.0.8"
}
},
"Dapr.Protos": {
"type": "Transitive",
"resolved": "1.16.0",
"contentHash": "4k4iKjyRCsFwX7KY5tDcBWDe6JPkhnvN1nqd8zRhDw3YcajF/Br3SU072YdEQKUQ/MJNvqafvzCNPbqSbK3nqg==",
"dependencies": {
"Google.Api.CommonProtos": "2.17.0",
"Google.Protobuf": "3.32.0",
"Grpc.Net.Client": "2.71.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Logging.Abstractions": "9.0.8"
}
},
"DynamicInterop": {
"type": "Transitive",
"resolved": "0.9.1",
"contentHash": "n21+Hd+tceX8lgaOosPV+Pne+YqnZUd5RLW3OhnsVxWRzYXiAIAKmKweHIePYeY+fmcn3N5tjkJyQUccFuL3bg=="
},
"Fable.Remoting.Json": {
"type": "Transitive",
"resolved": "2.25.0",
"contentHash": "ycmxT5L7jUKLkSJ4uti+WiX1OU50UYhLLiFQwJpQzwPWpNbSXowPjUtGycl8G5429edzeIHGW/77hUlIAufiAg==",
"dependencies": {
"FSharp.Core": "6.0.0",
"Newtonsoft.Json": "13.0.3"
}
},
"FSharp.Data.Csv.Core": {
"type": "Transitive",
"resolved": "6.4.1",
"contentHash": "/RmEq3HSafm4RPAPATDsDTY0aAkJ8ioDDJ0Qf/NuJW7c7/CC3xeU0XC3sHmDkp9v98aeQOSJdTa+NJrMTHzT7g==",
"dependencies": {
"FSharp.Core": "6.0.1",
"FSharp.Data.Runtime.Utilities": "6.4.1"
}
},
"FSharp.Data.Html.Core": {
"type": "Transitive",
"resolved": "6.4.1",
"contentHash": "/T7k5FkR8nRJ3fZ8Bfaf/c9eda2ru0xCIbM+i2Qt/PgtHp2d1ZmDvQIWbYfDLWVcKjRVu/YpRYOw/2fX0RT8ew==",
"dependencies": {
"FSharp.Core": "6.0.1",
"FSharp.Data.Csv.Core": "6.4.1",
"FSharp.Data.Runtime.Utilities": "6.4.1"
}
},
"FSharp.Data.Http": {
"type": "Transitive",
"resolved": "6.4.1",
"contentHash": "7KxlBNwnSIiR1nsPal2ofmgU4Rag8dyDJ+cziW1L9Z+iA55aXeXO/RapQDnyVIwl/Fbm1scGAuSTWP36JNpQFg==",
"dependencies": {
"FSharp.Core": "6.0.1"
}
},
"FSharp.Data.Json.Core": {
"type": "Transitive",
"resolved": "6.4.1",
"contentHash": "mUyqLZiI0XPEiE9FIJLJ3Ndof4hEc2paW049Cw224knmp/b0brMwznLaOqtlmCr49QCELj0tcT0ZCKfb2cFS0g==",
"dependencies": {
"FSharp.Core": "6.0.1",
"FSharp.Data.Http": "6.4.1",
"FSharp.Data.Runtime.Utilities": "6.4.1"
}
},
"FSharp.Data.Runtime.Utilities": {
"type": "Transitive",
"resolved": "6.4.1",
"contentHash": "pG4X3QWilYMF3qjZWpod6QgO38uiYUM3/bkEsEyT69E3zAlFQFO9uUy0tqEhDznHvNx4QtZaScUM+06r94HHnQ==",
"dependencies": {
"FSharp.Core": "6.0.1",
"FSharp.Data.Http": "6.4.1"
}
},
"FSharp.Data.WorldBank.Core": {
"type": "Transitive",
"resolved": "6.4.1",
"contentHash": "opXr3YMArDQCiA1nkEnhSf1s6E0QsotO0VZ5nvQcMXmDuDU4IA1i1DlYp4QVmCXRKj5EHPKMwZkTVNeQDuZ5Bg==",
"dependencies": {
"FSharp.Core": "6.0.1",
"FSharp.Data.Http": "6.4.1",
"FSharp.Data.Json.Core": "6.4.1",
"FSharp.Data.Runtime.Utilities": "6.4.1"
}
},
"FSharp.Data.Xml.Core": {
"type": "Transitive",
"resolved": "6.4.1",
"contentHash": "TprbqQu+DdrR6Kl5biNCAsM8yeQs+pgqRpQYDorbbFIroGw1LBMoX+1iiigJcK89TwJAtiEzVrZCQzHvCDrCbA==",
"dependencies": {
"FSharp.Core": "6.0.1",
"FSharp.Data.Http": "6.4.1",
"FSharp.Data.Json.Core": "6.4.1",
"FSharp.Data.Runtime.Utilities": "6.4.1"
}
},
"FsPickler": {
"type": "Transitive",
"resolved": "5.3.2",
"contentHash": "LFtxXpQNor8az1ez3rN9oz2cqf/06i9yTrPyJ9R83qLEpFAU7Of0WL2hoSXzLHer4lh+6mO1NV4VQFiBzNRtjw==",
"dependencies": {
"FSharp.Core": "4.3.2"
}
},
"Google.Api.CommonProtos": {
"type": "Transitive",
"resolved": "2.17.0",
"contentHash": "elfQPknFr495hm7vdy6ZlgyQh6yzZq9TU7sS35L/Fj/fqjM/mUGau9gVJLhvQEtUlPjtR80hpn/m9HvBMyCXIw==",
"dependencies": {
"Google.Protobuf": "[3.31.1, 4.0.0]"
}
},
"Google.Protobuf": {
"type": "Transitive",
"resolved": "3.32.0",
"contentHash": "fsKxV5bhcvXmZi+cUo5+IxzRMBHwHeFO8G5utNa9f+Mu37kmfy8JcUVvWPt4cX7EuQWAjjHUjZqVl7nGSTRHRg=="
},
"Grpc.Core.Api": {
"type": "Transitive",
"resolved": "2.71.0",
"contentHash": "QquqUC37yxsDzd1QaDRsH2+uuznWPTS8CVE2Yzwl3CvU4geTNkolQXoVN812M2IwT6zpv3jsZRc9ExJFNFslTg=="
},
"Grpc.Net.Client": {
"type": "Transitive",
"resolved": "2.71.0",
"contentHash": "U1vr20r5ngoT9nlb7wejF28EKN+taMhJsV9XtK9MkiepTZwnKxxiarriiMfCHuDAfPUm9XUjFMn/RIuJ4YY61w==",
"dependencies": {
"Grpc.Net.Common": "2.71.0",
"Microsoft.Extensions.Logging.Abstractions": "6.0.0"
}
},
"Grpc.Net.Common": {
"type": "Transitive",
"resolved": "2.71.0",
"contentHash": "v0c8R97TwRYwNXlC8GyRXwYTCNufpDfUtj9la+wUrZFzVWkFJuNAltU+c0yI3zu0jl54k7en6u2WKgZgd57r2Q==",
"dependencies": {
"Grpc.Core.Api": "2.71.0"
}
},
"Humanizer.Core": {
"type": "Transitive",
"resolved": "2.14.1",
"contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw=="
},
"KdTree": {
"type": "Transitive",
"resolved": "1.4.1",
"contentHash": "yWbb35v/V9y88SLLMUPTlAN3pQEoPhDfZf9PApFnlU4kLtwVQ75U9vW5mW4/alQnLBuLKWBKcy4W5xK95mYsuA=="
},
"MathNet.Numerics": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "pg1W2VwaEQMAiTpGK840hZgzavnqjlCMTVSbtVCXVyT+7AX4mc1o89SPv4TBlAjhgCOo9c1Y+jZ5m3ti2YgGgA=="
},
"MathNet.Numerics.FSharp": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "lKYhd68fReW5odX/q+Uzxw3357Duq3zmvkYvnZVqqcc2r/EmrYGDoOdUGuHnhfr8yj9V34js5gQH/7IWcxZJxg==",
"dependencies": {
"FSharp.Core": "6.0.2",
"MathNet.Numerics": "5.0.0"
}
},
"MessagePack.Annotations": {
"type": "Transitive",
"resolved": "3.1.3",
"contentHash": "XTy4njgTAf6UVBKFj7c7ad5R0WVKbvAgkbYZy4f00kplzX2T3VOQ34AUke/Vn/QgQZ7ETdd34/IDWS3KBInSGA=="
},
"MessagePackAnalyzer": {
"type": "Transitive",
"resolved": "3.1.3",
"contentHash": "19u1oVNv2brCs5F/jma8O8CnsKMMpYwNqD0CAEDEzvqwDTAhqC9r7xHZP4stPb3APs/ryO/zVn7LvjoEHfvs7Q=="
},
"Microsoft.Bcl.AsyncInterfaces": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "3aeMZ1N0lJoSyzqiP03hqemtb1BijhsJADdobn/4nsMJ8V1H+CrpuduUe4hlRdx+ikBQju1VGjMD1GJ3Sk05Eg=="
},
"Microsoft.Build.Framework": {
"type": "Transitive",
"resolved": "17.8.3",
"contentHash": "NrQZJW8TlKVPx72yltGb8SVz3P5mNRk9fNiD/ao8jRSk48WqIIdCn99q4IjlVmPcruuQ+yLdjNQLL8Rb4c916g=="
},
"Microsoft.Build.Locator": {
"type": "Transitive",
"resolved": "1.7.8",
"contentHash": "sPy10x527Ph16S2u0yGME4S6ohBKJ69WfjeGG/bvELYeZVmJdKjxgnlL8cJJJLGV/cZIRqSfB12UDB8ICakOog=="
},
"Microsoft.CodeAnalysis.Analyzers": {
"type": "Transitive",
"resolved": "3.3.4",
"contentHash": "AxkxcPR+rheX0SmvpLVIGLhOUXAKG56a64kV9VQZ4y9gR9ZmPXnqZvHJnmwLSwzrEP6junUF11vuc+aqo5r68g=="
},
"Microsoft.CodeAnalysis.Common": {
"type": "Transitive",
"resolved": "4.8.0",
"contentHash": "/jR+e/9aT+BApoQJABlVCKnnggGQbvGh7BKq2/wI1LamxC+LbzhcLj4Vj7gXCofl1n4E521YfF9w0WcASGg/KA==",
"dependencies": {
"Microsoft.CodeAnalysis.Analyzers": "3.3.4"
}
},
"Microsoft.CodeAnalysis.CSharp": {
"type": "Transitive",
"resolved": "4.8.0",
"contentHash": "+3+qfdb/aaGD8PZRCrsdobbzGs1m9u119SkkJt8e/mk3xLJz/udLtS2T6nY27OTXxBBw10HzAbC8Z9w08VyP/g==",
"dependencies": {
"Microsoft.CodeAnalysis.Common": "[4.8.0]"
}
},
"Microsoft.CodeAnalysis.CSharp.Workspaces": {
"type": "Transitive",
"resolved": "4.8.0",
"contentHash": "3amm4tq4Lo8/BGvg9p3BJh3S9nKq2wqCXfS7138i69TUpo/bD+XvD0hNurpEBtcNZhi1FyutiomKJqVF39ugYA==",
"dependencies": {
"Humanizer.Core": "2.14.1",
"Microsoft.CodeAnalysis.CSharp": "[4.8.0]",
"Microsoft.CodeAnalysis.Common": "[4.8.0]",
"Microsoft.CodeAnalysis.Workspaces.Common": "[4.8.0]"
}
},
"Microsoft.CodeAnalysis.Workspaces.Common": {
"type": "Transitive",
"resolved": "4.8.0",
"contentHash": "LXyV+MJKsKRu3FGJA3OmSk40OUIa/dQCFLOnm5X8MNcujx7hzGu8o+zjXlb/cy5xUdZK2UKYb9YaQ2E8m9QehQ==",
"dependencies": {
"Humanizer.Core": "2.14.1",
"Microsoft.Bcl.AsyncInterfaces": "7.0.0",
"Microsoft.CodeAnalysis.Common": "[4.8.0]",
"System.Composition": "7.0.0"
}
},
"Microsoft.CodeAnalysis.Workspaces.MSBuild": {
"type": "Transitive",
"resolved": "4.8.0",
"contentHash": "IEYreI82QZKklp54yPHxZNG9EKSK6nHEkeuf+0Asie9llgS1gp0V1hw7ODG+QyoB7MuAnNQHmeV1Per/ECpv6A==",
"dependencies": {
"Microsoft.Build.Framework": "16.10.0",
"Microsoft.CodeAnalysis.Common": "[4.8.0]",
"Microsoft.CodeAnalysis.Workspaces.Common": "[4.8.0]"
}
},
"Microsoft.EntityFrameworkCore.Abstractions": {
"type": "Transitive",
"resolved": "9.0.1",
"contentHash": "qy+taGVLUs82zeWfc32hgGL8Z02ZqAneYvqZiiXbxF4g4PBUcPRuxHM9K20USmpeJbn4/fz40GkCbyyCy5ojOA=="
},
"Microsoft.EntityFrameworkCore.Analyzers": {
"type": "Transitive",
"resolved": "9.0.1",
"contentHash": "c6ZZJZhPKrXFkE2z/81PmuT69HBL6Y68Cl0xJ5SRrDjJyq5Aabkq15yCqPg9RQ3R0aFLVaJok2DA8R3TKpejDQ=="
},
"Microsoft.Extensions.Caching.Abstractions": {
"type": "Transitive",
"resolved": "9.0.1",
"contentHash": "Eghsg9SyIvq0c8x6cUpe71BbQoOmsytXxqw2+ZNiTnP8a8SBLKgEor1zZeWhC0588IbS2M0PP4gXGAd9qF862Q==",
"dependencies": {
"Microsoft.Extensions.Primitives": "9.0.1"
}
},
"Microsoft.Extensions.Caching.Memory": {
"type": "Transitive",
"resolved": "9.0.1",
"contentHash": "JeC+PP0BCKMwwLezPGDaciJSTfcFG4KjsG8rX4XZ6RSvzdxofrFmcnmW2L4+cWUcZSBTQ+Dd7H5Gs9XZz/OlCA==",
"dependencies": {
"Microsoft.Extensions.Caching.Abstractions": "9.0.1",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging.Abstractions": "9.0.1",
"Microsoft.Extensions.Options": "9.0.1",
"Microsoft.Extensions.Primitives": "9.0.1"
}
},
"Microsoft.Extensions.Configuration": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "6m+8Xgmf8UWL0p/oGqBM+0KbHE5/ePXbV1hKXgC59zEv0aa0DW5oiiyxDbK5kH5j4gIvyD5uWL0+HadKBJngvQ==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
"Microsoft.Extensions.Primitives": "9.0.8"
}
},
"Microsoft.Extensions.Configuration.Abstractions": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "yNou2KM35RvzOh4vUFtl2l33rWPvOCoba+nzEDJ+BgD8aOL/jew4WPCibQvntRfOJ2pJU8ARygSMD+pdjvDHuA==",
"dependencies": {
"Microsoft.Extensions.Primitives": "9.0.8"
}
},
"Microsoft.Extensions.Configuration.Binder": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "0vK9DnYrYChdiH3yRZWkkp4x4LbrfkWEdBc5HOsQ8t/0CLOWKXKkkhOE8A1shlex0hGydbGrhObeypxz/QTm+w==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8"
}
},
"Microsoft.Extensions.DependencyInjection": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "JJjI2Fa+QtZcUyuNjbKn04OjIUX5IgFGFu/Xc+qvzh1rXdZHLcnqqVXhR4093bGirTwacRlHiVg1XYI9xum6QQ==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "xY3lTjj4+ZYmiKIkyWitddrp1uL5uYiweQjqo4BKBw01ZC4HhcfgLghDpPZcUlppgWAFqFy9SgkiYWOMx365pw=="
},
"Microsoft.Extensions.DependencyModel": {
"type": "Transitive",
"resolved": "9.0.1",
"contentHash": "FHPy9cbb0y09riEpsrU5XYpOgf4nTfHj7a0m1wLC5DosGtjJn9g03gGg1GTJmEdRFBQrJwbwTnHqLCdNLsoYgA=="
},
"Microsoft.Extensions.Diagnostics": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "BKkLCFXzJvNmdngeYBf72VXoZqTJSb1orvjdzDLaGobicoGFBPW8ug2ru1nnEewMEwJzMgnsjHQY8EaKWmVhKg==",
"dependencies": {
"Microsoft.Extensions.Configuration": "9.0.8",
"Microsoft.Extensions.Diagnostics.Abstractions": "9.0.8",
"Microsoft.Extensions.Options.ConfigurationExtensions": "9.0.8"
}
},
"Microsoft.Extensions.Diagnostics.Abstractions": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "UDY7blv4DCyIJ/8CkNrQKLaAZFypXQavRZ2DWf/2zi1mxYYKKw2t8AOCBWxNntyPZHPGhtEmL3snFM98ADZqTw==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Options": "9.0.8"
}
},
"Microsoft.Extensions.Http": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "jDj+4aDByk47oESlDDTtk6LWzlXlmoCsjCn6ihd+i9OntN885aPLszUII5+w0B/7wYSZcS3KdjqLAIhKLSiBXQ==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Diagnostics": "9.0.8",
"Microsoft.Extensions.Logging": "9.0.8",
"Microsoft.Extensions.Logging.Abstractions": "9.0.8",
"Microsoft.Extensions.Options": "9.0.8"
}
},
"Microsoft.Extensions.Logging": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "Z/7ze+0iheT7FJeZPqJKARYvyC2bmwu3whbm/48BJjdlGVvgDguoCqJIkI/67NkroTYobd5geai1WheNQvWrgA==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.8",
"Microsoft.Extensions.Logging.Abstractions": "9.0.8",
"Microsoft.Extensions.Options": "9.0.8"
}
},
"Microsoft.Extensions.Logging.Abstractions": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "pYnAffJL7ARD/HCnnPvnFKSIHnTSmWz84WIlT9tPeQ4lHNiu0Az7N/8itihWvcF8sT+VVD5lq8V+ckMzu4SbOw==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8"
}
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "OmTaQ0v4gxGQkehpwWIqPoEiwsPuG/u4HUsbOFoWGx4DKET2AXzopnFe/fE608FIhzc/kcg2p8JdyMRCCUzitQ==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Primitives": "9.0.8"
}
},
"Microsoft.Extensions.Options.ConfigurationExtensions": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "eW2s6n06x0w6w4nsX+SvpgsFYkl+Y0CttYAt6DKUXeqprX+hzNqjSfOh637fwNJBg7wRBrOIRHe49gKiTgJxzQ==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
"Microsoft.Extensions.Configuration.Binder": "9.0.8",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Options": "9.0.8",
"Microsoft.Extensions.Primitives": "9.0.8"
}
},
"Microsoft.Extensions.Primitives": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "tizSIOEsIgSNSSh+hKeUVPK7xmTIjR8s+mJWOu1KXV3htvNQiPMFRMO17OdI1y/4ZApdBVk49u/08QGC9yvLug=="
},
"Microsoft.NET.StringTools": {
"type": "Transitive",
"resolved": "17.11.4",
"contentHash": "mudqUHhNpeqIdJoUx2YDWZO/I9uEDYVowan89R6wsomfnUJQk6HteoQTlNjZDixhT2B4IXMkMtgZtoceIjLRmA=="
},
"Mono.TextTemplating": {
"type": "Transitive",
"resolved": "3.0.0",
"contentHash": "YqueG52R/Xej4VVbKuRIodjiAhV0HR/XVbLbNrJhCZnzjnSjgMJ/dCdV0akQQxavX6hp/LC6rqLGLcXeQYU7XA==",
"dependencies": {
"System.CodeDom": "6.0.0"
}
},
"NetTopologySuite.IO.PostGis": {
"type": "Transitive",
"resolved": "2.1.0",
"contentHash": "3W8XTFz8iP6GQ5jDXK1/LANHiU+988k1kmmuPWNKcJLpmSg6CvFpbTpz+s4+LBzkAp64wHGOldSlkSuzYfrIKA==",
"dependencies": {
"NetTopologySuite": "[2.0.0, 3.0.0-A)"
}
},
"ProjNET": {
"type": "Transitive",
"resolved": "2.0.0",
"contentHash": "iMJG8qpGJ8SjFrB044O8wgo0raAWCdG1Bvly0mmVcjzsrexDHhC+dUct6Wb1YwQtupMBjSTWq7Fn00YeNErprA=="
},
"Serilog.Sinks.File": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "lxjg89Y8gJMmFxVkbZ+qDgjl+T4yC5F7WSLTvA+5q0R04tfKVLRL/EHpYoJ/MEQd2EeCKDuylBIVnAYMotmh2A==",
"dependencies": {
"Serilog": "4.0.0"
}
},
"Serilog.Sinks.Seq": {
"type": "Transitive",
"resolved": "9.0.0",
"contentHash": "aNU8A0K322q7+voPNmp1/qNPH+9QK8xvM1p72sMmCG0wGlshFzmtDW9QnVSoSYCj0MgQKcMOlgooovtBhRlNHw==",
"dependencies": {
"Serilog": "4.2.0",
"Serilog.Sinks.File": "6.0.0"
}
},
"System.CodeDom": {
"type": "Transitive",
"resolved": "6.0.0",
"contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA=="
},
"System.Composition": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "tRwgcAkDd85O8Aq6zHDANzQaq380cek9lbMg5Qma46u5BZXq/G+XvIYmu+UI+BIIZ9zssXLYrkTykEqxxvhcmg==",
"dependencies": {
"System.Composition.AttributedModel": "7.0.0",
"System.Composition.Convention": "7.0.0",
"System.Composition.Hosting": "7.0.0",
"System.Composition.Runtime": "7.0.0",
"System.Composition.TypedParts": "7.0.0"
}
},
"System.Composition.AttributedModel": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "2QzClqjElKxgI1jK1Jztnq44/8DmSuTSGGahXqQ4TdEV0h9s2KikQZIgcEqVzR7OuWDFPGLHIprBJGQEPr8fAQ=="
},
"System.Composition.Convention": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "IMhTlpCs4HmlD8B+J8/kWfwX7vrBBOs6xyjSTzBlYSs7W4OET4tlkR/Sg9NG8jkdJH9Mymq0qGdYS1VPqRTBnQ==",
"dependencies": {
"System.Composition.AttributedModel": "7.0.0"
}
},
"System.Composition.Hosting": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "eB6gwN9S+54jCTBJ5bpwMOVerKeUfGGTYCzz3QgDr1P55Gg/Wb27ShfPIhLMjmZ3MoAKu8uUSv6fcCdYJTN7Bg==",
"dependencies": {
"System.Composition.Runtime": "7.0.0"
}
},
"System.Composition.Runtime": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "aZJ1Zr5Txe925rbo4742XifEyW0MIni1eiUebmcrP3HwLXZ3IbXUj4MFMUH/RmnJOAQiS401leg/2Sz1MkApDw=="
},
"System.Composition.TypedParts": {
"type": "Transitive",
"resolved": "7.0.0",
"contentHash": "ZK0KNPfbtxVceTwh+oHNGUOYV2WNOHReX2AXipuvkURC7s/jPwoWfsu3SnDBDgofqbiWr96geofdQ2erm/KTHg==",
"dependencies": {
"System.Composition.AttributedModel": "7.0.0",
"System.Composition.Hosting": "7.0.0",
"System.Composition.Runtime": "7.0.0"
}
},
"entity": {
"type": "Project",
"dependencies": {
"Microsoft.EntityFrameworkCore": "[9.0.1, )",
"Microsoft.EntityFrameworkCore.Relational": "[9.0.1, )",
"Npgsql.EntityFrameworkCore.PostgreSQL": "[9.0.2, )",
"Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite": "[9.0.2, )"
}
},
"oceanbox.dataagent": {
"type": "Project",
"dependencies": {
"Dapper.FSharp": "[4.9.0, )",
"Dapr.Actors": "[1.16.0, )",
"Entity": "[1.0.0, )",
"FSharp.Core": "[9.0.303, )",
"FSharp.Data": "[6.4.1, )",
"FSharpPlus": "[1.7.0, )",
"Fable.Remoting.DotnetClient": "[3.35.0, )",
"Microsoft.EntityFrameworkCore": "[9.0.1, )",
"Microsoft.EntityFrameworkCore.Relational": "[9.0.1, )",
"NetTopologySuite": "[2.5.0, )",
"Npgsql.EntityFrameworkCore.PostgreSQL": "[9.0.2, )",
"Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite": "[9.0.2, )",
"Npgsql.NetTopologySuite": "[9.0.2, )",
"Oceanbox.FvcomKit": "[5.13.0, )",
"Oceanbox.SDSLite": "[2.8.0, )",
"Serilog.Sinks.Console": "[6.0.0, )",
"Thoth.Json.Net": "[12.0.0, )"
}
},
"Oceanbox.DataAgent.Api": {
"type": "Project",
"dependencies": {
"FSharp.Core": "[9.0.303, )"
}
},
"Dapper.FSharp": {
"type": "CentralTransitive",
"requested": "[4.9.0, )",
"resolved": "4.9.0",
"contentHash": "wqMi/wHSQV9v79/u8OELxO+lmUOxk3J5CAUuAmWbltbIYH0A64CV1z1RG+9EVpyAAD9bovKYAnQ2wNwDoPxTxA==",
"dependencies": {
"Dapper": "2.1.35",
"FSharp.Core": "8.0.200"
}
},
"Dapr.Actors": {
"type": "CentralTransitive",
"requested": "[1.16.0, )",
"resolved": "1.16.0",
"contentHash": "s9v6VofXXYoRqZJQlQbvNYYSlGhkL+Z+bpqrx1TRo06kLhANeDmXA9yeVaD+1KwJIO1chUFj5O4iKuTxIkg1sA==",
"dependencies": {
"Dapr.Client": "1.16.0",
"Dapr.Common": "1.16.0",
"Google.Api.CommonProtos": "2.17.0",
"Google.Protobuf": "3.32.0",
"Grpc.Net.Client": "2.71.0",
"Microsoft.Extensions.Configuration": "9.0.8",
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
"Microsoft.Extensions.DependencyInjection": "9.0.8",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Http": "9.0.8",
"Microsoft.Extensions.Logging": "9.0.8",
"Microsoft.Extensions.Logging.Abstractions": "9.0.8",
"Microsoft.Extensions.Options": "9.0.8"
}
},
"Dapr.Client": {
"type": "CentralTransitive",
"requested": "[1.16.0, )",
"resolved": "1.16.0",
"contentHash": "dFDKol+mtQrk1lIKlEyCx3k6W0Pf+0wC6xcsaDqa0Bg+XCWDc4juROuDcSb0/L1Y+Ev6LSLDMC/FgzNWMw9YtQ==",
"dependencies": {
"Dapr.Common": "1.16.0",
"Dapr.Protos": "1.16.0",
"Google.Api.CommonProtos": "2.17.0",
"Google.Protobuf": "3.32.0",
"Grpc.Net.Client": "2.71.0",
"Microsoft.Extensions.Configuration": "9.0.8",
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
"Microsoft.Extensions.DependencyInjection": "9.0.8",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Http": "9.0.8",
"Microsoft.Extensions.Logging": "9.0.8",
"Microsoft.Extensions.Logging.Abstractions": "9.0.8",
"Microsoft.Extensions.Options": "9.0.8"
}
},
"Fable.Core": {
"type": "CentralTransitive",
"requested": "[4.4.0, )",
"resolved": "3.1.6",
"contentHash": "w6M1F0zoLk4kTFc1Lx6x1Ft6BD3QwRe0eaLiinAqbjVkcF+iK+NiXGJO+a6q9RAF9NCg0vI48Xku7aNeqG4JVw==",
"dependencies": {
"FSharp.Core": "4.7.1"
}
},
"Fable.Remoting.DotnetClient": {
"type": "CentralTransitive",
"requested": "[3.35.0, )",
"resolved": "3.35.0",
"contentHash": "xaxt9nKfqIWh30+cOrG9GNl06+7yTy5htrcF5eXsZ7QJLLy7T5ZD3xeGpAb0xbh+TZTVQGluSQzxh/opIZm2PQ==",
"dependencies": {
"FSharp.Core": "6.0.0",
"Fable.Remoting.Json": "2.25.0",
"Fable.Remoting.MsgPack": "1.24.0"
}
},
"Fable.Remoting.MsgPack": {
"type": "CentralTransitive",
"requested": "[1.24.0, )",
"resolved": "1.24.0",
"contentHash": "Bn3nzoZbib6lPk70bIJumEu2wFMxciB4o8k0Zw6tRfAOpNKvUsi79OOll2nW3FU1P6MVBepT43m+R8JvfYnNiw==",
"dependencies": {
"FSharp.Core": "4.7.2"
}
},
"MessagePack": {
"type": "CentralTransitive",
"requested": "[3.1.3, )",
"resolved": "3.1.3",
"contentHash": "UiNv3fknvPzh5W+S0VV96R17RBZQQU71qgmsMnjjRZU2rtQM/XcTnOB+klT2dA6T1mxjnNKYrEm164AoXvGmYg==",
"dependencies": {
"MessagePack.Annotations": "3.1.3",
"MessagePackAnalyzer": "3.1.3",
"Microsoft.NET.StringTools": "17.11.4"
}
},
"Microsoft.EntityFrameworkCore.Relational": {
"type": "CentralTransitive",
"requested": "[9.0.1, )",
"resolved": "9.0.1",
"contentHash": "7Iu0h4oevRvH4IwPzmxuIJGYRt55TapoREGlluk75KCO7lenN0+QnzCl6cQDY48uDoxAUpJbpK2xW7o8Ix69dw==",
"dependencies": {
"Microsoft.EntityFrameworkCore": "9.0.1",
"Microsoft.Extensions.Caching.Memory": "9.0.1",
"Microsoft.Extensions.Configuration.Abstractions": "9.0.1",
"Microsoft.Extensions.Logging": "9.0.1"
}
},
"NetTopologySuite": {
"type": "CentralTransitive",
"requested": "[2.5.0, )",
"resolved": "2.5.0",
"contentHash": "5/+2O2ADomEdUn09mlSigACdqvAf0m/pVPGtIPEPQWnyrVykYY0NlfXLIdkMgi41kvH9kNrPqYaFBTZtHYH7Xw=="
},
"Newtonsoft.Json": {
"type": "CentralTransitive",
"requested": "[13.0.3, )",
"resolved": "13.0.3",
"contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ=="
},
"Npgsql": {
"type": "CentralTransitive",
"requested": "[9.0.2, )",
"resolved": "9.0.2",
"contentHash": "hCbO8box7i/XXiTFqCJ3GoowyLqx3JXxyrbOJ6om7dr+eAknvBNhhUHeJVGAQo44sySZTfdVffp4BrtPeLZOAA==",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "8.0.2"
}
},
"Npgsql.EntityFrameworkCore.PostgreSQL": {
"type": "CentralTransitive",
"requested": "[9.0.2, )",
"resolved": "9.0.2",
"contentHash": "cYdOGplIvr9KgsG8nJ8xnzBTImeircbgetlzS1OmepS5dAQW6PuGpVrLOKBNEwEvGYZPsV8037X5vZ/Dmpwz7Q==",
"dependencies": {
"Microsoft.EntityFrameworkCore": "[9.0.0, 10.0.0)",
"Microsoft.EntityFrameworkCore.Relational": "[9.0.0, 10.0.0)",
"Npgsql": "9.0.2"
}
},
"Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite": {
"type": "CentralTransitive",
"requested": "[9.0.2, )",
"resolved": "9.0.2",
"contentHash": "D38a3+CF8dO7nPiwt/NtQ/sLbrzZpX910jaaGiETdeS18KI0yMYEFvWWO5I/JBjVXLVnruodsukIUupdoD4fRA==",
"dependencies": {
"Npgsql.EntityFrameworkCore.PostgreSQL": "9.0.2",
"Npgsql.NetTopologySuite": "9.0.2"
}
},
"Npgsql.NetTopologySuite": {
"type": "CentralTransitive",
"requested": "[9.0.2, )",
"resolved": "9.0.2",
"contentHash": "DCwN+IVl3yWfOftPe0UBUUDOqa877ca+z+xSDQVi5ShDnOIAipaaYZlzDYm8Nga8hcxx6UrIQuImFnXv8fDpwg==",
"dependencies": {
"NetTopologySuite": "2.5.0",
"NetTopologySuite.IO.PostGIS": "2.1.0",
"Npgsql": "9.0.2"
}
},
"Oceanbox.FvcomKit": {
"type": "CentralTransitive",
"requested": "[5.13.0, )",
"resolved": "5.13.0",
"contentHash": "6uVL3fLhRf4OU1hWygGpVex4pI5YB+GaWrKZUgoL/LkGmdFv0qU8Y7v+meHNM3E9bjR7xKinCVfrw5SXsF6C8g==",
"dependencies": {
"FSharp.Core": "9.0.201",
"FSharp.Data": "6.4.1",
"FSharpPlus": "1.7.0",
"FsPickler": "5.3.2",
"KDTree": "1.4.1",
"MathNet.Numerics.FSharp": "5.0.0",
"MessagePack": "3.1.3",
"Oceanbox.SDSLite": "2.8.0",
"ProjNet.FSharp": "5.2.0",
"Serilog": "4.2.0",
"Serilog.Sinks.Console": "6.0.0",
"Serilog.Sinks.Seq": "9.0.0",
"Thoth.Json.Net": "12.0.0"
}
},
"Oceanbox.SDSLite": {
"type": "CentralTransitive",
"requested": "[2.8.0, )",
"resolved": "2.8.0",
"contentHash": "DzMcnywHhtmLVDZSVCZq6Mqq+SIm4snGRYgquho9xZSyEq5RhBkLdSa5k59m7o24FGZyt75DGpElN9p+dezU7Q==",
"dependencies": {
"DynamicInterop": "0.9.1"
}
},
"ProjNet.FSharp": {
"type": "CentralTransitive",
"requested": "[5.2.0, )",
"resolved": "5.2.0",
"contentHash": "sYSePg/0sVo16Fk3r7okVSga6i9GAN0kkjt1haEXVw25SF8A4S3Gcpf5+6lgknBGdYiZBmJ+3S6v5g1WSSCp2g==",
"dependencies": {
"FSharp.Core": "8.0.100",
"FSharp.Data": "6.3.0",
"FSharpPlus": "1.5.0",
"ProjNet": "2.0.0"
}
}
},
"net10.0/linux-x64": {}
}
}

View File

@@ -23,22 +23,22 @@ let fetchArchives modelAreaId callback =
let fetchModelAreas callback =
async {
let! res = archmaester.getModelArea HelloWorld
let! res = archmaester.getModelAreaArchives (HelloWorld, ArchiveType.FromString "*:*:*")
match res with
| Error err -> console.error $"Fetch model areas error: {err}"
| Ok modelAreas -> Some [| modelAreas |] |> callback
| Ok modelAreas -> Some modelAreas |> callback
}
[<LitElement("archive-listing")>]
let ArchiveListing () =
let _, _ = LitElement.init (fun init -> init.useShadowDom <- false)
let (modelAreas: ModelArea[] option), setModelAreas = Hook.useState None
let (modelAreas: ArchiveProps[] option), setModelAreas = Hook.useState None
Hook.useEffectOnce (fun _ -> fetchModelAreas setModelAreas |> Async.StartImmediate)
let archiveId (a: ArchiveProps) = a.archiveId.ToString()
let archiveId (a: ArchiveProps) = a.archiveId.ToString ()
let archiveFiles files =
let fileEntry (fileName, _) = html $"""<li>{fileName}</li>"""
@@ -119,7 +119,7 @@ let ArchiveListing () =
{item.name}
<div class="badge badge-secondary">{item.archiveType}</div>
</h2>
<h3>Start date: {item.startTime.ToShortDateString()} {item.startTime.ToShortTimeString()}</h3>
<h3>Start date: {item.startTime.ToShortDateString ()} {item.startTime.ToShortTimeString ()}</h3>
<span>Projection: {item.projection}</span>
{{archiveOwners item.acl}}
{{archiveUsers item.acl}}
@@ -136,8 +136,8 @@ let ArchiveListing () =
<p>{archiveName}</p>
"""
let modelAreaItem (item: ModelArea) =
let archiveList = Array.map archiveName item.archives
let modelAreaItem (item: ArchiveProps) =
// let archiveList = Array.map archiveName item.archives
html
$"""
@@ -146,7 +146,7 @@ let ArchiveListing () =
<h2 class="card-title">
{item.name}
</h2>
{archiveList}
{item}
</div>
</div>
"""

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<Version>7.1.0</Version>
</PropertyGroup>
<ItemGroup>
@@ -9,9 +9,9 @@
<Compile Include="App.fs"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Fable.Lit" Version="1.4.2"/>
<PackageReference Include="Fable.Remoting.Client" Version="7.32.0"/>
<PackageReference Update="FSharp.Core" Version="9.0.201"/>
<PackageReference Include="Fable.Lit" />
<PackageReference Include="Fable.Remoting.Client" />
<PackageReference Include="FSharp.Core" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../../Interfaces/Archmaester/Archmaester.Api.fsproj"/>

View File

@@ -2,7 +2,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<Version>7.1.0</Version>
</PropertyGroup>
<ItemGroup>
@@ -11,23 +11,23 @@
<Compile Include="Main.fs"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Argu" Version="6.2.5"/>
<PackageReference Include="Dapper.FSharp" Version="4.9.0"/>
<PackageReference Include="FSharp.Data" Version="6.4.1"/>
<PackageReference Include="FSharpPlus" Version="1.7.0"/>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.2">
<PackageReference Include="Argu" />
<PackageReference Include="Dapper.FSharp" />
<PackageReference Include="FSharp.Data" />
<PackageReference Include="FSharpPlus" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" >
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.2"/>
<PackageReference Include="Npgsql.NetTopologySuite" Version="9.0.2"/>
<PackageReference Include="Serilog" Version="4.2.0"/>
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0"/>
<PackageReference Include="Thoth.Json.Net" Version="12.0.0"/>
<PackageReference Include="NetTopologySuite" Version="2.5.0"/>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.2"/>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite" Version="9.0.2"/>
<PackageReference Update="FSharp.Core" Version="9.0.201"/>
<PackageReference Include="Microsoft.Extensions.Logging.Console" />
<PackageReference Include="Npgsql.NetTopologySuite" />
<PackageReference Include="Serilog" />
<PackageReference Include="Serilog.Sinks.Console" />
<PackageReference Include="Thoth.Json.Net" />
<PackageReference Include="NetTopologySuite" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite" />
<PackageReference Update="FSharp.Core" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Interfaces\Archmaester\Archmaester.Api.fsproj"/>

View File

@@ -1,90 +0,0 @@
open Fake.Core
open Fake.IO
open Farmer
open Farmer.Builders
open Helpers
initializeContext()
let serverPath = Path.getFullName "src/Server"
let clientPath = Path.getFullName "src/Client"
let testPath = Path.getFullName "test"
let libPath = Path.getFullName "src/Interfaces" |> Some
let distPath = Path.getFullName "dist"
let packPath = Path.getFullName "packages"
let versionFile = Path.getFullName ".version"
let vite = """bunx --bun vite -c ../../vite.config.js"""
let fable opts =
$"fable -e .jsx -o build --run {vite} build --emptyOutDir --outDir {distPath}/public {opts}"
let fableWatch = $"fable watch -e .jsx -o build --run {vite}"
Target.create "Clean" (fun _ -> Shell.cleanDir distPath)
Target.create "Bundle" (fun _ ->
[ "server", dotnet $"publish -c Release -o {distPath}" serverPath
"client", dotnet (fable "-m production") clientPath ]
|> runParallel
)
Target.create "BundleDebug" (fun _ ->
[ "server", dotnet $"publish -c Debug -o {distPath}" serverPath
"client", dotnet (fable "-m development --minify false --sourcemap true") clientPath ]
|> runParallel
)
Target.create "Pack" (fun _ ->
match libPath with
| Some p -> run dotnet $"pack -c Release -o \"{packPath}\"" p
| None -> ()
)
Target.create "Run" (fun _ ->
[ "server", dotnet "watch run" serverPath
"client", dotnet fableWatch clientPath ]
|> runParallel
)
Target.create "Client" (fun _ ->
run dotnet fableWatch clientPath
)
Target.create "Format" (fun _ ->
run dotnet "fantomas . -r" "src"
)
Target.create "Test" (fun _ ->
if System.IO.Directory.Exists testPath then
[ "server", dotnet "run" (testPath + "/Server")
"client", dotnet $"fable -e .jsx -o build --run {vite}" (testPath + "/Client") ]
|> runParallel
else ()
)
open Fake.Core.TargetOperators
let dependencies = [
"Clean"
==> "Bundle"
"Clean"
==> "BundleDebug"
"Clean"
==> "Test"
"Clean"
==> "Run"
"Clean"
==> "Pack"
"Client"
]
[<EntryPoint>]
let main args = runOrDefault args

View File

@@ -1 +1,6 @@
use nix
#!/usr/bin/env bash
# the shebang is ignored, but nice for editors
use nix
# HACK: Workaround for direnv bug
unset TMP TMPDIR TEMP TEMPDIR

View File

@@ -1,15 +1,15 @@
# yaml-language-server: $schema=https://gitlab.com/gitlab-org/gitlab/-/raw/master/app/assets/javascripts/editor/schema/ci.json
variables:
SKIP_TESTS: "true"
NODE_OPTIONS: "--max-old-space-size=16384"
SKIP_TESTS: "true"
include:
- project: oceanbox/gitlab-ci
ref: v4.1
file: DotnetDeployment.gitlab-ci.yml
inputs:
project-name: atlantis
project-dir: src/Atlantis
- project: oceanbox/gitlab-ci
ref: v4.5
file: DotnetDeployment.gitlab-ci.yml
inputs:
project-name: atlantis
project-dir: src/Atlantis
dotnet-build-atlantis:
dockerize-atlantis:
tags:
- saas-linux-large-amd64
- nix

View File

@@ -1,17 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="../../.build/Helpers.fs" />
<Compile Include=".build/Build.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Fake.Core.Target" Version="6.1.3" />
<PackageReference Include="Fake.DotNet.Cli" Version="6.1.3" />
<PackageReference Include="Fake.IO.FileSystem" Version="6.1.3" />
<PackageReference Include="Farmer" Version="1.9.6" />
<PackageReference Update="FSharp.Core" Version="9.0.201" />
</ItemGroup>
</Project>

View File

@@ -1,4 +1,5 @@
FROM mcr.microsoft.com/dotnet/aspnet:9.0
FROM mcr.microsoft.com/dotnet/aspnet:10.0
RUN apt-get update \
&& apt-get install -y gcc-multilib libnetcdf19 libnetcdf-dev

View File

@@ -10,6 +10,7 @@ allow_k8s_contexts(cluster)
load('ext://restart_process', 'docker_build_with_restart')
load('ext://namespace', 'namespace_inject')
load('ext://nix', 'build_nix_image')
clientPort=os.getenv('CLIENT_PORT')
if not clientPort:
@@ -33,10 +34,13 @@ docker_build_with_restart(
live_update=[sync('./dist', '/app')]
)
redis=str(local(
'helm template -f ./tilt/tilt/redis.yaml --namespace {namespace} {env}-{name}-redis bitnami/redis'
.format(namespace=namespace, env=env, name=name), echo_off=False, quiet=True)).replace('<x>',"{env}".format(env=env))
k8s_yaml(blob(redis))
# NOTE(mrtz): Kept for nix testing
# build_nix_image(
# '{repository}:latest'.format(repository=repository),
# "../../default.nix",
# "containers.atlantis --argstr debug 'Debug' --show-trace",
# "../../default.nix",
# )
manifest=helm(
'../../../manifests/charts/{name}'.format(name=name),
@@ -56,13 +60,13 @@ k8s_yaml(namespace_inject(blob(kustomizations), namespace))
local_resource(
'create-bundle',
cmd='dotnet run bundledebug',
cmd='just bundle-debug',
trigger_mode=TRIGGER_MODE_MANUAL
)
local_resource(
'build-server',
cmd='dotnet publish -o ./dist src/Server',
cmd='just bundle-debug-server',
deps=[
'./src/Server',
'./src/Shared'
@@ -70,6 +74,10 @@ local_resource(
ignore=[
'src/Server/bin',
'src/Server/obj',
'src/Server/Archmaester/obj',
'src/Server/Hipster/obj',
'src/Server/Petimeter/obj',
'src/Server/Common/obj',
'src/Shared/bin',
'src/Shared/obj',
],
@@ -80,7 +88,7 @@ local_resource(
local_resource(
'run-client',
serve_cmd='dotnet fable watch -e .jsx -o build --run vite -c ../../vite.config.js',
serve_cmd='just run-client',
serve_dir='./src/Client',
links=['https://{name}.local.oceanbox.io:{port}'.format(name=name, port=clientPort)],
resource_deps=['build-server'],

86
src/Atlantis/justfile Normal file
View File

@@ -0,0 +1,86 @@
# Atlantis build commands
# Install just: https://github.com/casey/just
set dotenv-load
src_path := "src"
server_path := "src/Server"
client_path := "src/Client"
test_path := "test"
lib_path := "src/Interfaces"
dist_path := "../../dist"
pack_path := "../../packages"
vite_prod := "bunx --bun vite build -c ../../vite.config.js -m production --emptyOutDir --outDir " + "../../dist/public"
vite_dev := "bunx --bun vite build -c ../../vite.config.js -m development --minify false --sourcemap true --emptyOutDir --outDir " + "../../dist/public"
vite := "bunx vite -c ../../vite.config.js -m development "
# Default recipe - show available commands
default:
@just --list
# Clean build artifacts
clean:
rm -rf {{dist_path}}
# Build production bundle (server + client)
[parallel]
bundle: clean bundle-server bundle-client
[working-directory: 'src/Server']
bundle-server:
dotnet build -tl -c Release -o {{dist_path}}
[working-directory: 'src/Client']
install-client:
bun install --frozen-lockfile
[working-directory: 'src/Client']
bundle-client: install-client
# Build debug bundle (server + client)
[parallel]
bundle-debug: clean bundle-debug-server bundle-debug-client
[working-directory: 'src/Server']
bundle-debug-server:
dotnet build -tl -c Debug -o {{dist_path}}
[working-directory: 'src/Client']
bundle-debug-client:
fable -e .jsx -o build --test:MSBuildCracker --run {{vite_dev}}
# Create NuGet package
[working-directory: 'src/Server']
pack: clean
dotnet pack -c Release -o "{{pack_path}}" {{lib_path}}
# Run development server (watch mode)
[parallel]
run: clean run-server run-client
[working-directory: 'src/Server']
run-server:
dotnet watch run
# Run client only in watch mode
[working-directory: 'src/Client']
run-client: install-client
fable watch -e .jsx -o build --test:MSBuildCracker --run {{vite}}
# Format code with Fantomas
format:
fantomas {{src_path}} -r
# Run tests
[parallel]
test: clean test-server test-client
[working-directory: 'src']
test-server:
dotnet run {{test_path}}/Server
[working-directory: 'src/Client']
test-client: install-client
fable -e .jsx -o build --run {{vite}}

View File

@@ -0,0 +1,17 @@
// Custom distribution bundle for Plotly
// We only import and register the components we use
// For more info: https://github.com/plotly/plotly.js#modules
//
// In vite.config.js we add an alias to redirect imports
// The original plotly can be accessed as 'plotly-dist'
import Plotly from 'plotly-dist/lib/core';
import scatter from 'plotly-dist/lib/scatter';
import contour from 'plotly-dist/lib/contour';
import heatmap from 'plotly-dist/lib/heatmap';
import box from 'plotly-dist/lib/box';
import bar from 'plotly-dist/lib/bar';
import barpolar from 'plotly-dist/lib/barpolar';
Plotly.register([scatter, contour, heatmap, box, bar, barpolar]);
export default Plotly;

View File

@@ -1,15 +1,13 @@
with import <nixpkgs> { };
{
sources ? import ./../../nix,
pkgs ? import sources.nixpkgs { },
}:
let
port = 8000;
baseShell = import ./../../shell.nix { inherit pkgs; };
in
mkShell {
packages = [
tilt
dapr-cli
kustomize
bun
mkcert
];
pkgs.mkShellNoCC {
inputsFrom = [ baseShell ];
LOG_LEVEL = "verbose";
REDIS = "localhost:6379";
@@ -21,9 +19,14 @@ mkShell {
DAPR_API_TOKEN = "anVzdCBmb3IgbG9jYWwgdXNlCg==";
ARCHMAESTER_AUTH = "YWRtaW46ZW4tdG8tdHJlLWZpcmU=";
# NOTE: Fixes docker error `Build Failed: failed to dial gRPC: unable to upgrade to h2c`
DOCKER_BUILDKIT = 0;
# NOTE(mrtz): Needed for tilt podman compat.
# DOCKER_HOST = "unix:///run/user/1000/podman/podman.sock";
shellHook = ''
export APP_ENV=$USER
export APP_NAME=atlantis
export APP_NAMESPACE=$USER-atlantis
'';
}
}

View File

@@ -5,6 +5,12 @@ open Fable.Core.JsInterop
open Lit
open Remoting
let initSentry = Sentry.hostTargets |> Array.contains window.location.hostname
if initSentry then
console.debug "Pushing to Sentry"
Sentry.init ()
[<LitElement("init-app")>]
let InitApp () =
let _ = LitElement.init (fun cfg -> cfg.useShadowDom <- false)
@@ -12,8 +18,9 @@ let InitApp () =
Hook.useEffectOnce (fun () ->
async {
let! authenticated = authApi.IsAuthenticated()
do! Auth.setId ()
if authenticated.IsSome && not (isNullOrUndefined sessionStorage["archive_id"]) then
do! Utils.initAtlantisSessionUrls ()
do! Auth.initSessionUrls ()
window.location.href <- "/map.html"
else
sessionStorage.removeItem "archive_id"
@@ -21,4 +28,5 @@ let InitApp () =
return ()
} |> Async.StartImmediate
)
Lit.nothing

View File

@@ -1,21 +1,22 @@
module Atlas
open System
open Browser
open Fable.Core
open Fable.Core.JsInterop
open Fable.OpenLayers
open Fable.OpenLayers.Event
open Feliz.prop
open Lit
open Lit.Elmish
open Remoting
open Archmaester.Dto
open Atlantis.Types
open Maps
open Remoting
importSideEffects "../public/style.scss"
importSideEffects "@spectrum-web-components/action-button/sp-action-button.js"
importSideEffects "@spectrum-web-components/action-group/sp-action-group.js"
importSideEffects "@spectrum-web-components/action-menu/sp-action-menu.js"
@@ -53,12 +54,8 @@ importSideEffects "@spectrum-web-components/table/sp-table-row.js"
importSideEffects "@spectrum-web-components/dialog/sp-dialog.js"
importSideEffects "@spectrum-web-components/underlay/sp-underlay.js"
importSideEffects "@shoelace-style/shoelace/dist/components/dialog/dialog.js"
let register () = ()
let private hmr = HMR.createToken ()
let polygonStyle (feature: Feature) =
let name = feature.get "name" :?> string
let models = feature.get "models" :?> int
@@ -96,7 +93,8 @@ let private createSelectInteraction () =
type private Model = {
activeTab: Tab
archives: Map<ModelAreaId, ArchiveProps[]>
// TODO: Save this in localstorage or IndexedDB
archives: Map<ModelAreaId, ArchiveProps array>
helloWorld: ModelArea option
map: OlMap
mapKind: MapKind
@@ -237,6 +235,7 @@ let createMapWithLayers center (layers: Layer.Layer[]) : OlMap =
let private init () =
let map = createMapWithLayers theCenter [| baseMapLayer |]
map.addControl (ScaleLine.scaleLine [ scaleLine.bar false; scaleLine.minWidth 75 ])
map.addControl (FullScreen.fullScreen [ ])
let model = { Model.empty with map = map }
model, Elmish.Cmd.none
@@ -253,11 +252,21 @@ let emptyModel = {
json = ""
}
let private hmr = HMR.createToken ()
[<HookComponent>]
let private archivePopup (m: ModelArea option) =
Hook.useHmr hmr
let modelArea, setModelArea =
Hook.useState<ModelArea> (fun () -> m |> Option.defaultValue emptyModel)
Hook.useEffectOnChange (m, Option.iter setModelArea)
Hook.useEffectOnChange (
m,
function
| Some modelArea -> setModelArea modelArea
| None -> ()
)
let archives =
if modelArea.archives > 0 then
@@ -268,7 +277,8 @@ let private archivePopup (m: ModelArea option) =
let areas =
if modelArea.models > 0 then
html $""" Available areas: {modelArea.models} """
else Lit.nothing
else
Lit.nothing
html
$"""
@@ -299,20 +309,23 @@ let private archivePopup (m: ModelArea option) =
"
>
{modelArea.name}
<sp-divider> </sp-divider>
<sp-divider size="s"></sp-divider>
</div>
<div>
<span>{archives}</span>
<span>{areas}</span>
</div>
<p>
{archives}
{areas}
</p>
</sp-popover>
</div>
"""
[<HookComponent>]
let private topNav (dispatch: Msg -> unit) (model: Model) =
Hook.useHmr hmr
let icon =
match model.activeTab with
| Tab.Select -> html $"""<img src="ob.png" height="35px"/>"""
| Tab.Select -> html $"""<img src="ob.png" />"""
| _ -> html $"""<sp-icon-undo></sp-icon-undo>"""
let handleActionMenu (ev: Types.Event) =
@@ -320,87 +333,100 @@ let private topNav (dispatch: Msg -> unit) (model: Model) =
| "logout" -> window.location.href <- "/signout"
| _ -> ()
let menu =
html
$"""
<sp-top-nav-item href="#about">Oceanbox</sp-top-nav-item>
<sp-action-menu label="Account" placement="bottom-end" @change={Ev (handleActionMenu)} style="margin-inline-start: auto;">
// NOTE: The img sizing is a bit weird, which the height is explicit
html
$"""
<div class="atlas-top-nav">
<div class="atlas-top-nav-start">
<a href="/" style="height: 32px;">{icon}</a>
<a href="#about">Oceanbox</a>
</div>
<sp-action-menu
label="Account"
placement="bottom-end"
@change={Ev handleActionMenu}
>
<sp-menu-item disabled>Settings</sp-menu-item>
<sp-menu-item disabled>Profile</sp-menu-item>
<sp-menu-divider></sp-menu-divider>
<sp-menu-item disabled>Help</sp-menu-item>
<sp-menu-item value="logout">Logout</sp-menu-item>
</sp-action-menu>
"""
let menuButton =
match model.activeTab with
| Tab.Select -> menu
| _ -> menu
html
$"""
<sp-top-nav size="s">
<sp-top-nav-item @click={Ev (fun _ -> window.location.reload ())}>
{icon}
</sp-top-nav-item>
{menuButton}
</sp-top-nav>
</div>
"""
[<HookComponent>]
let archiveSelectModal (modelAreaOpt: ModelArea option) (archives: ArchiveProps array) onClose =
let private archiveSelectModal (modelAreaOpt: ModelArea option) (archives: ArchiveProps array) onClose =
Hook.useHmr hmr
let modelAreaName = modelAreaOpt |> Option.map (fun model -> $"{model.name} archives") |> Option.defaultValue "N/A"
let archiveRow (archive: ArchiveProps) =
let selectArchive (archive: ArchiveProps) =
do Lib.Umami.track("atlas-select-archive", {| id = archive.archiveId; name = archive.name |})
sessionStorage["archive_id"] <- string archive.archiveId
window.location.href <- "/map.html"
let startTimeStr = archive.startTime.ToString "dd/MM/yyyy"
let daysLong = archive.endTime - archive.startTime
html
$"""
html $"""
<sp-table-row @click={Ev (fun _ -> selectArchive archive)}>
<sp-table-cell>{archive.name}</sp-table-cell>
<sp-table-cell>{startTimeStr}</sp-table-cell>
<sp-table-cell>{daysLong.Days}</sp-table-cell>
</sp-table-row>
"""
let archiveRows =
match modelAreaOpt with
| None -> html $"""No model area selected"""
| Some modelArea -> archives |> Lit.mapUnique (fun a -> a.archiveId.ToString ()) archiveRow
| Some modelArea ->
archives
|> Array.sortByDescending _.startTime
|> Lit.mapUnique (fun a -> a.archiveId.ToString ()) archiveRow
let content () =
if Array.isEmpty archives then
html
$"""
<div class="full-box flex-center">
<sp-progress-circle label="Loading archives" size="l" indeterminate></sp-progress-circle>
</div>
"""
else
html
$"""
<sp-table
size="s"
scroller="true"
style="width: 100%%;"
>
<sp-table-head>
<sp-table-head-cell>Name</sp-table-head-cell>
<sp-table-head-cell>Start</sp-table-head-cell>
<sp-table-head-cell>Days</sp-table-head-cell>
</sp-table-head>
<sp-table-body>
{archiveRows}
</sp-table-body>
</sp-table>
"""
html
$"""
<sp-underlay ?open={modelAreaOpt.IsSome} @click={fun _ -> onClose ()}></sp-underlay>
<sp-dialog size="l" dismissable @close={Ev (fun _ -> onClose ())}>
<h2 slot="heading"> {modelAreaOpt
|> Option.map _.name
|> Option.defaultValue "N/A"}</h2>
<h4>Archives</h4>
<sp-table
size="s"
scroller="true"
>
<sp-table-head>
<sp-table-head-cell>Name</sp-table-head-cell>
<sp-table-head-cell>Start</sp-table-head-cell>
<sp-table-head-cell>Days</sp-table-head-cell>
</sp-table-head>
<sp-table-body>
{archiveRows}
</sp-table-body>
</sp-table>
<h2 slot="heading">{modelAreaName}</h2>
{content ()}
</sp-dialog>
"""
"""
let selectStyle name =
let stroke' =
Style.stroke [ stroke.color "rgba(50, 50, 50, 0.9)"; stroke.width 1.3 ]
let fill' = Style.fill [ fill.color "rgba(255, 255, 255, 0.5)" ]
Fable.OpenLayers.Style.style [
Style.style [
style.fill fill'
style.stroke stroke'
style.text (
@@ -414,9 +440,8 @@ let selectStyle name =
[<HookComponent>]
let userAccessModal isRegistered isActive =
// let showModal, setModal = Hook.useState (isActive || isRegistered)
Hook.useHmr hmr
console.debug $"user registered={isRegistered} active={isActive}"
let accessNotifier =
if not isRegistered then
html
@@ -445,9 +470,8 @@ let userAccessModal isRegistered isActive =
</div>
"""
[<LitElement("atlas-app")>]
let SelectApp () =
let AtlasApp () =
Hook.useHmr hmr
LitElement.init (fun cfg -> cfg.useShadowDom <- false) |> ignore
@@ -474,7 +498,9 @@ let SelectApp () =
Hook.useEffectOnce (fun () ->
async {
do! Auth.establishAuthentication ()
do! Auth.setId ()
// do! Auth.startTokenRefreshLoopOrLogin ()
do! Lib.Umami.init ()
do! Atlantis.IDB.tryResetOutdatedIDB () |> Async.AwaitPromise
@@ -492,7 +518,7 @@ let SelectApp () =
// // TODO: Refresh in expiry time?
// sessionStorage["barentswatch_token"] <- token.AccessToken
do! Utils.initAtlantisSessionUrls ()
do! Auth.initSessionUrls ()
// let drifters = driftersJobApi ()
// let! fenceRadius = drifters.getFenceRadius()
@@ -536,7 +562,6 @@ let SelectApp () =
"click",
fun (event: MapBrowserEvent) ->
let features = model.map.getFeaturesAtPixel event.pixel
console.debug ("Features:", features)
features
|> Array.tryHead
|> Option.iter (fun feature ->
@@ -562,12 +587,10 @@ let SelectApp () =
let selectedModelAreaArchives =
model.selectedModelArea
|> Option.map (fun modelArea ->
if Map.containsKey modelArea.modelAreaId model.archives then
model.archives[modelArea.modelAreaId]
else
[||]
)
|> Option.bind (fun modelArea ->
model.archives
|> Map.tryFind modelArea.modelAreaId
)
|> Option.defaultValue [||]
html

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<DefineConstants>FABLE_COMPILER</DefineConstants>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<Version>2.87.0</Version>
@@ -11,27 +11,27 @@
<Compile Include="Atlas.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Fable.Browser.IndexedDB" Version="2.2.0" />
<PackageReference Include="Fable.Browser.WebGL" Version="1.3.0" />
<PackageReference Include="Fable.Core" Version="4.4.0" />
<PackageReference Include="Fable.Elmish" Version="4.2.0" />
<PackageReference Include="Fable.Fetch" Version="2.7.0" />
<PackageReference Include="Fable.Lit" Version="1.6.2-oceanbox" />
<PackageReference Include="Fable.Lit.React" Version="1.6.2-oceanbox" />
<PackageReference Include="Fable.Lit.Elmish" Version="1.6.2-oceanbox" />
<PackageReference Include="Fable.Promise" Version="3.2.0" />
<PackageReference Include="Fable.React" Version="9.4.0" />
<PackageReference Include="Fable.Remoting.Client" Version="7.32.0" />
<PackageReference Include="Fable.Remoting.MsgPack" Version="1.24.0" />
<PackageReference Include="Fable.OpenLayers" Version="2.18.0" />
<PackageReference Include="Fable.SignalR.Elmish" Version="2.1.0" />
<PackageReference Include="Fable.SimpleHttp" Version="3.6.0" />
<PackageReference Include="Feliz" Version="2.9.0" />
<PackageReference Include="Feliz.CompilerPlugins" Version="2.2.0" />
<PackageReference Include="Thoth.Fetch" Version="3.0.1" />
<PackageReference Include="Thoth.Json" Version="10.4.1" />
<PackageReference Update="FSharp.Core" Version="9.0.201" />
<PackageReference Include="Matplotlib.ColorMaps" Version="3.0.1" />
<PackageReference Include="Fable.Browser.IndexedDB" />
<PackageReference Include="Fable.Browser.WebGL" />
<PackageReference Include="Fable.Core" />
<PackageReference Include="Fable.Elmish" />
<PackageReference Include="Fable.Fetch" />
<PackageReference Include="Fable.Lit" />
<PackageReference Include="Fable.Lit.React" />
<PackageReference Include="Fable.Lit.Elmish" />
<PackageReference Include="Fable.Promise" />
<PackageReference Include="Fable.React" />
<PackageReference Include="Fable.Remoting.Client" />
<PackageReference Include="Fable.Remoting.MsgPack" />
<PackageReference Include="Fable.OpenLayers" />
<PackageReference Include="Fable.SignalR.Elmish" />
<PackageReference Include="Fable.SimpleHttp" />
<PackageReference Include="Feliz" />
<PackageReference Include="Feliz.CompilerPlugins" />
<PackageReference Include="Thoth.Fetch" />
<PackageReference Include="Thoth.Json" />
<PackageReference Include="Matplotlib.ColorMaps" />
<PackageReference Include="FSharp.Core" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Lib\Lib.fsproj" />

View File

@@ -0,0 +1,654 @@
{
"version": 2,
"dependencies": {
"net10.0": {
"Fable.Browser.IndexedDB": {
"type": "Direct",
"requested": "[2.2.0, )",
"resolved": "2.2.0",
"contentHash": "6RU8aqUeb4qpAekPEjnpaWP+RRTyYMB4ICE06eZoMoTXPq0oGWxsEkHPgDJIPVTmyDuAGJ4YMcDCt2D8850xMw==",
"dependencies": {
"FSharp.Core": "4.7.2",
"Fable.Browser.Dom": "2.14.0",
"Fable.Browser.Event": "1.5.0",
"Fable.Core": "3.2.8"
}
},
"Fable.Browser.WebGL": {
"type": "Direct",
"requested": "[1.3.0, )",
"resolved": "1.3.0",
"contentHash": "iQognakmr62KccqZg++oenn1J0eSdCexAFUII0fSWAz1tTfdaPxrFKIjagHd/3HWw5NettpyNJREVRDghklYTQ==",
"dependencies": {
"FSharp.Core": "4.7.2",
"Fable.Browser.Dom": "2.16.0",
"Fable.Browser.Event": "1.6.0",
"Fable.Core": "3.2.8"
}
},
"Fable.Core": {
"type": "Direct",
"requested": "[4.4.0, )",
"resolved": "4.4.0",
"contentHash": "zVQdiC8RqCOBb3KACTp9ASU9Q46esXXWosZQT/Vu/RhCpkfVwXPmBxVayy3iyqaRWc7XSu4Af7pbOqlcL/RtdA=="
},
"Fable.Elmish": {
"type": "Direct",
"requested": "[4.2.0, )",
"resolved": "4.2.0",
"contentHash": "A8lDcHbz2AKcwFa6IlnK8I/21nbsxBcP5Vxq6Gp+jT8dU7Vjpnk8Pbry5+zQrlqjwt1XHU/S5Oo0KZqaGemPUA==",
"dependencies": {
"FSharp.Core": "4.7.2",
"Fable.Core": "3.7.1"
}
},
"Fable.Fetch": {
"type": "Direct",
"requested": "[2.7.0, )",
"resolved": "2.7.0",
"contentHash": "2ndGZZTqpX9Hyso51tnIxWAskN2zrHX+7LeAwfG4zew+DtMMGa/3IyJGl2BOYUwweq2MhfuVqs1K3avgBFDq+Q==",
"dependencies": {
"FSharp.Core": "4.7.2",
"Fable.Browser.Blob": "1.2.0",
"Fable.Browser.Event": "1.5.0",
"Fable.Core": "3.7.1",
"Fable.Promise": "2.2.2"
}
},
"Fable.Lit": {
"type": "Direct",
"requested": "[1.6.2-oceanbox, )",
"resolved": "1.6.2-oceanbox",
"contentHash": "ylo6UgB6FiGyINpDvryYt3GPl8MH6fvB5tiNizCmsNNerGxw/THFMGGJHmukQDh64NHru4ARhPTPgkBvsyOTVA==",
"dependencies": {
"FSharp.Core": "8.0.300",
"Fable.Browser.Dom": "2.17.0",
"Fable.Core": "4.3.0",
"Fable.Promise": "3.2.0"
}
},
"Fable.Lit.Elmish": {
"type": "Direct",
"requested": "[1.6.2-oceanbox, )",
"resolved": "1.6.2-oceanbox",
"contentHash": "K0fBlpHWZs07s3OYcpBZXVo+xoot62f+USdw8Pi9yxd8a6rmfsbbPlDQvGe2k6VpkEelXNL0AMwEt9GxEY1DeQ==",
"dependencies": {
"Fable.Elmish": "4.2.0",
"Fable.Lit": "1.6.2-oceanbox"
}
},
"Fable.Lit.React": {
"type": "Direct",
"requested": "[1.6.2-oceanbox, )",
"resolved": "1.6.2-oceanbox",
"contentHash": "+k0/F4mWZe91GuPDJxBTawUFKegQyNU48OjwAQTzzp3RlibCo1wvFtZjISJz83OvHcjuCSmO02i7uxWm9j6gFw==",
"dependencies": {
"Fable.Lit": "1.6.2-oceanbox",
"Feliz": "2.8.0"
}
},
"Fable.OpenLayers": {
"type": "Direct",
"requested": "[2.19.0, )",
"resolved": "2.19.0",
"contentHash": "zVwnqj8LHHUVuw4loS88ZvFCYILKDtG/NWzhGOLwZ5yqj+pRxlD6Mex7zsGrSc08y3P/D/LLdjKmJuE/Nbgo6Q==",
"dependencies": {
"FSharp.Core": "9.0.303",
"Fable.Browser.Dom": "2.18.0",
"Fable.Browser.WebGL": "1.3.0",
"Fable.Core": "4.3.0"
}
},
"Fable.Promise": {
"type": "Direct",
"requested": "[3.2.0, )",
"resolved": "3.2.0",
"contentHash": "4A+Iiembrny2h3AE2BIbchfuLmWHNhpkOTvbTtFXHtGzHVMqEVFRXrAfdy83wX2wK5Og3fqRo1y8t/Bqkd7j6g==",
"dependencies": {
"FSharp.Core": "4.7.2",
"Fable.Core": "3.7.1"
}
},
"Fable.React": {
"type": "Direct",
"requested": "[9.4.0, )",
"resolved": "9.4.0",
"contentHash": "c33FD2BumoYvu4/8bz2ToWaLZyfq2GMo7nq0RB/Bdoj7KdNObNBw2s1jWTi9whcf/s3tmikoXS4gZUKpD9MJ8g==",
"dependencies": {
"FSharp.Core": "4.7.2",
"Fable.React.Types": "18.3.0",
"Fable.ReactDom.Types": "18.2.0"
}
},
"Fable.Remoting.Client": {
"type": "Direct",
"requested": "[7.32.0, )",
"resolved": "7.32.0",
"contentHash": "PMZ0gj8UXXBKrLg71IwGGTMmy2woSdesjFYkrkDNYmhNDvz3Z/h05fJYOVXYYKXp/d2XRX0fHtGV0DuYppip7A==",
"dependencies": {
"FSharp.Core": "4.7.2",
"Fable.Browser.XMLHttpRequest": "1.0.0",
"Fable.Core": "3.1.5",
"Fable.Remoting.MsgPack": "1.24.0",
"Fable.SimpleJson": "3.24.0"
}
},
"Fable.Remoting.MsgPack": {
"type": "Direct",
"requested": "[1.24.0, )",
"resolved": "1.24.0",
"contentHash": "Bn3nzoZbib6lPk70bIJumEu2wFMxciB4o8k0Zw6tRfAOpNKvUsi79OOll2nW3FU1P6MVBepT43m+R8JvfYnNiw==",
"dependencies": {
"FSharp.Core": "4.7.2"
}
},
"Fable.SignalR.Elmish": {
"type": "Direct",
"requested": "[2.1.0, )",
"resolved": "2.1.0",
"contentHash": "sPPuEcpKlRGACbX7Hk4kh31+aii8GAM8toTwYpmrtU+Zl9QocwbWK6nPJaE0YbQ41ZJgohyU6bNhKt7+SPKZhw==",
"dependencies": {
"FSharp.Core": "9.0.100",
"Fable.Elmish": "4.2.0",
"Fable.Promise": "3.2.0",
"Fable.SignalR": "2.1.0"
}
},
"Fable.SimpleHttp": {
"type": "Direct",
"requested": "[3.6.0, )",
"resolved": "3.6.0",
"contentHash": "RHXu3OQVxoxObErhUWl7J9JWXqDxLaQrpIXyo2MECF1a9ekNZ5bBnDGVB1RCEKRpVFB6SOun/pk+DB5wJDYmmg==",
"dependencies": {
"FSharp.Core": "4.6.2",
"Fable.Browser.Dom": "1.0.0",
"Fable.Browser.XMLHttpRequest": "1.1.0",
"Fable.Core": "3.0.0"
}
},
"Feliz": {
"type": "Direct",
"requested": "[2.9.0, )",
"resolved": "2.9.0",
"contentHash": "8nyGREGA60RysdSBamVWmr68MG+3lLy76W17fBiGaKi7uMFbtRcYBLyNtp2NyGZFfnuWCEyDAmAXM5YFnDhbhg==",
"dependencies": {
"FSharp.Core": "4.7.2",
"Fable.ReactDom.Types": "18.2.0",
"Feliz.CompilerPlugins": "2.2.0"
}
},
"Feliz.CompilerPlugins": {
"type": "Direct",
"requested": "[2.2.0, )",
"resolved": "2.2.0",
"contentHash": "ACkO++Hp4lUrEx/axeehIL5/3R8jMnak+CYpzd0/kLpejp9BETtrgjHK7oj6Lh3V9fB7WoAKsCxyPSrm4ADN2w==",
"dependencies": {
"FSharp.Core": "4.7.2",
"Fable.AST": "4.2.1"
}
},
"FSharp.Core": {
"type": "Direct",
"requested": "[9.0.303, )",
"resolved": "9.0.303",
"contentHash": "6JlV8aD8qQvcmfoe/PMOxCHXc0uX4lR23u0fAyQtnVQxYULLoTZgwgZHSnRcuUHOvS3wULFWcwdnP1iwslH60g=="
},
"Matplotlib.ColorMaps": {
"type": "Direct",
"requested": "[3.0.1, )",
"resolved": "3.0.1",
"contentHash": "Amw/NumOXIOB4Z/YbBErDd7gcZrtNhG10aeF9MydXUVNmmf7BJKeHDroSnzMRbsUOf3oQCXhzyjng6mhmRA0LA==",
"dependencies": {
"FSharp.Core": "6.0.4"
}
},
"Thoth.Fetch": {
"type": "Direct",
"requested": "[3.0.1, )",
"resolved": "3.0.1",
"contentHash": "5i8KQwTFzDEoIjE/fAwCw0GFICCsFzVkVq2w4uU1fRlOqbSfLlUNcCEq6JkeAvQ+Jj7syMKNPSH994T8NswcpA==",
"dependencies": {
"FSharp.Core": "4.7.2",
"Fable.Core": "3.2.8",
"Fable.Fetch": "2.1.0",
"Fable.Promise": "2.0.0",
"Thoth.Json": "6.0.0"
}
},
"Thoth.Json": {
"type": "Direct",
"requested": "[10.4.1, )",
"resolved": "10.4.1",
"contentHash": "hs76/uO+gHhvnlaxQDqbpUX2Y0L97ilEZ1Nx+LA4D6N7fuAYJmNwQWZB/KQLBE7wIeWK5oXMFHCuKdImSrF1Bg==",
"dependencies": {
"FSharp.Core": "5.0.2",
"Fable.Core": "4.1.0"
}
},
"Dapr.Common": {
"type": "Transitive",
"resolved": "1.16.0",
"contentHash": "6JyPI8LxNXSjSpO9vTdbfJh78zOiiC0sgeaXuY8O6SJQh2epaRdEPw0UpamNnld3CkDjp69/VCphox7pU/lh1Q==",
"dependencies": {
"Dapr.Protos": "1.16.0",
"Google.Api.CommonProtos": "2.17.0",
"Google.Protobuf": "3.32.0",
"Grpc.Net.Client": "2.71.0",
"Microsoft.Extensions.Configuration": "9.0.8",
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
"Microsoft.Extensions.DependencyInjection": "9.0.8",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Http": "9.0.8",
"Microsoft.Extensions.Logging": "9.0.8",
"Microsoft.Extensions.Logging.Abstractions": "9.0.8",
"Microsoft.Extensions.Options": "9.0.8"
}
},
"Dapr.Protos": {
"type": "Transitive",
"resolved": "1.16.0",
"contentHash": "4k4iKjyRCsFwX7KY5tDcBWDe6JPkhnvN1nqd8zRhDw3YcajF/Br3SU072YdEQKUQ/MJNvqafvzCNPbqSbK3nqg==",
"dependencies": {
"Google.Api.CommonProtos": "2.17.0",
"Google.Protobuf": "3.32.0",
"Grpc.Net.Client": "2.71.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Logging.Abstractions": "9.0.8"
}
},
"Fable.AST": {
"type": "Transitive",
"resolved": "4.2.1",
"contentHash": "/4V6U7Qw/WIRRxm9NJ7b+YTXTRCTk6/YKeJnbKYaVbtT45MstA3jkFvRfV0FqVFtkG9AL4uccetreygTjK7nbQ=="
},
"Fable.Browser.Blob": {
"type": "Transitive",
"resolved": "1.4.0",
"contentHash": "UlaxrIXUfMmABjP+8a4XJp/Af+eCRKa8KJ57Olq4sqphmPLn/gNtp3sk5hRNBZ385lwUszbO5yd3Q/rrl9BdOQ==",
"dependencies": {
"FSharp.Core": "4.7.2",
"Fable.Core": "3.2.8"
}
},
"Fable.Browser.Dom": {
"type": "Transitive",
"resolved": "2.18.0",
"contentHash": "usu19HS3yRIPvzQ//Yj+Dp6SkJ1fkVUVOREaeDR4iLXGTKl0UqR1nPT1tEBX2GGMefj7dVrmG0dbONirOlVFBw==",
"dependencies": {
"FSharp.Core": "4.7.2",
"Fable.Browser.Blob": "1.4.0",
"Fable.Browser.Event": "1.7.0",
"Fable.Browser.WebStorage": "1.3.0",
"Fable.Core": "3.2.8"
}
},
"Fable.Browser.Event": {
"type": "Transitive",
"resolved": "1.7.0",
"contentHash": "x+wqXQK0l4VlCnELDp68GC/mZAx6NbicDxYPliyAoNq8RPNDeR3R782icNwI5YmA+ufq11XvG6w1JjsL/ldy7w==",
"dependencies": {
"FSharp.Core": "4.7.2",
"Fable.Browser.Gamepad": "1.3.0",
"Fable.Core": "3.2.8"
}
},
"Fable.Browser.Gamepad": {
"type": "Transitive",
"resolved": "1.3.0",
"contentHash": "C4HZDzCgff+U094QjpQlJh425W5j5/vojvOi2FV5UFS34l7TJ6YBgBPpKoro02QhAi/UF3AeocR+V2yiYxHb0A==",
"dependencies": {
"FSharp.Core": "4.7.2",
"Fable.Core": "3.2.8"
}
},
"Fable.Browser.WebStorage": {
"type": "Transitive",
"resolved": "1.3.0",
"contentHash": "x8JL9oEtPiK0JY4GrRTqhomiLxT6Jaiv5uu8VXiNeA78bFvUogZWxQeejsK83iNFGErK5wpdiPd0tsREZTRLeg==",
"dependencies": {
"FSharp.Core": "4.7.2",
"Fable.Browser.Event": "1.6.0",
"Fable.Core": "3.2.8"
}
},
"Fable.Browser.XMLHttpRequest": {
"type": "Transitive",
"resolved": "1.1.0",
"contentHash": "27p/F8781NrnV9vQ23RhX10ww9MDkX+Yi3yTiV9s8U8Bufi/VCCjS4swX0LXvgKQANN3k87CwaNeiO75r2U7gw==",
"dependencies": {
"FSharp.Core": "4.6.2",
"Fable.Browser.Blob": "1.1.0",
"Fable.Browser.Event": "1.0.0",
"Fable.Core": "3.0.0"
}
},
"Fable.Parsimmon": {
"type": "Transitive",
"resolved": "4.0.0",
"contentHash": "AaHqEcwjjv8q5S2gCNu6XsVcpChYM8D6aEb3sjjsAiLspwLrNLqm6vOEKdJKGnh0gSLHg6UWzLGA/Q4jrk+t/w==",
"dependencies": {
"FSharp.Core": "4.6.2",
"Fable.Core": "3.0.0"
}
},
"Fable.React.Types": {
"type": "Transitive",
"resolved": "18.3.0",
"contentHash": "/b8WZ3Bdfhqy9r60ZK9JGZaGNjIMb0ogsrvWIg3k7KfCEvJs5X6+7hCybVkyjVoxwzn9wLyYGRbh5wmuHQT/Vg==",
"dependencies": {
"FSharp.Core": "4.7.2",
"Fable.Browser.Dom": "2.4.4",
"Fable.Core": "3.2.7"
}
},
"Fable.ReactDom.Types": {
"type": "Transitive",
"resolved": "18.2.0",
"contentHash": "2WoBjsLiSgrvR68OJXko0iVaqeMbkPM5Bx813A1WlxOSCJ50M9fwnlwG/MUEZtiOIhQmku/YTJY5a8E8r1+j2Q==",
"dependencies": {
"FSharp.Core": "4.7.2",
"Fable.React.Types": "18.3.0"
}
},
"Fable.SignalR": {
"type": "Transitive",
"resolved": "2.1.0",
"contentHash": "CTBaS44I4fGG++g9wRbNO/gOxy5gPKBkw1UPP9rA8j/bX0SpfJENtavVI0ZHJLltf5umy01RG9HMWlMpi1Q6Sw==",
"dependencies": {
"FSharp.Core": "9.0.100",
"Fable.Promise": "3.2.0",
"Fable.Remoting.MsgPack": "1.24.0",
"Fable.SimpleJson": "3.24.0"
}
},
"Fable.SimpleJson": {
"type": "Transitive",
"resolved": "3.24.0",
"contentHash": "mNk5s+8arkrrupT52/840xybT/DmaPUsJ926fTHk2uHOaWLnyNbUPY63Yg8zJZFCxSCzWrFpmB8rS9fcLVLJSg==",
"dependencies": {
"FSharp.Core": "4.7.0",
"Fable.Core": "3.1.5",
"Fable.Parsimmon": "4.0.0"
}
},
"Google.Api.CommonProtos": {
"type": "Transitive",
"resolved": "2.17.0",
"contentHash": "elfQPknFr495hm7vdy6ZlgyQh6yzZq9TU7sS35L/Fj/fqjM/mUGau9gVJLhvQEtUlPjtR80hpn/m9HvBMyCXIw==",
"dependencies": {
"Google.Protobuf": "[3.31.1, 4.0.0]"
}
},
"Google.Protobuf": {
"type": "Transitive",
"resolved": "3.32.0",
"contentHash": "fsKxV5bhcvXmZi+cUo5+IxzRMBHwHeFO8G5utNa9f+Mu37kmfy8JcUVvWPt4cX7EuQWAjjHUjZqVl7nGSTRHRg=="
},
"Grpc.Core.Api": {
"type": "Transitive",
"resolved": "2.71.0",
"contentHash": "QquqUC37yxsDzd1QaDRsH2+uuznWPTS8CVE2Yzwl3CvU4geTNkolQXoVN812M2IwT6zpv3jsZRc9ExJFNFslTg=="
},
"Grpc.Net.Client": {
"type": "Transitive",
"resolved": "2.71.0",
"contentHash": "U1vr20r5ngoT9nlb7wejF28EKN+taMhJsV9XtK9MkiepTZwnKxxiarriiMfCHuDAfPUm9XUjFMn/RIuJ4YY61w==",
"dependencies": {
"Grpc.Net.Common": "2.71.0",
"Microsoft.Extensions.Logging.Abstractions": "6.0.0"
}
},
"Grpc.Net.Common": {
"type": "Transitive",
"resolved": "2.71.0",
"contentHash": "v0c8R97TwRYwNXlC8GyRXwYTCNufpDfUtj9la+wUrZFzVWkFJuNAltU+c0yI3zu0jl54k7en6u2WKgZgd57r2Q==",
"dependencies": {
"Grpc.Core.Api": "2.71.0"
}
},
"Microsoft.Extensions.Configuration": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "6m+8Xgmf8UWL0p/oGqBM+0KbHE5/ePXbV1hKXgC59zEv0aa0DW5oiiyxDbK5kH5j4gIvyD5uWL0+HadKBJngvQ==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
"Microsoft.Extensions.Primitives": "9.0.8"
}
},
"Microsoft.Extensions.Configuration.Abstractions": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "yNou2KM35RvzOh4vUFtl2l33rWPvOCoba+nzEDJ+BgD8aOL/jew4WPCibQvntRfOJ2pJU8ARygSMD+pdjvDHuA==",
"dependencies": {
"Microsoft.Extensions.Primitives": "9.0.8"
}
},
"Microsoft.Extensions.Configuration.Binder": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "0vK9DnYrYChdiH3yRZWkkp4x4LbrfkWEdBc5HOsQ8t/0CLOWKXKkkhOE8A1shlex0hGydbGrhObeypxz/QTm+w==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8"
}
},
"Microsoft.Extensions.DependencyInjection": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "JJjI2Fa+QtZcUyuNjbKn04OjIUX5IgFGFu/Xc+qvzh1rXdZHLcnqqVXhR4093bGirTwacRlHiVg1XYI9xum6QQ==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "xY3lTjj4+ZYmiKIkyWitddrp1uL5uYiweQjqo4BKBw01ZC4HhcfgLghDpPZcUlppgWAFqFy9SgkiYWOMx365pw=="
},
"Microsoft.Extensions.Diagnostics": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "BKkLCFXzJvNmdngeYBf72VXoZqTJSb1orvjdzDLaGobicoGFBPW8ug2ru1nnEewMEwJzMgnsjHQY8EaKWmVhKg==",
"dependencies": {
"Microsoft.Extensions.Configuration": "9.0.8",
"Microsoft.Extensions.Diagnostics.Abstractions": "9.0.8",
"Microsoft.Extensions.Options.ConfigurationExtensions": "9.0.8"
}
},
"Microsoft.Extensions.Diagnostics.Abstractions": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "UDY7blv4DCyIJ/8CkNrQKLaAZFypXQavRZ2DWf/2zi1mxYYKKw2t8AOCBWxNntyPZHPGhtEmL3snFM98ADZqTw==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Options": "9.0.8"
}
},
"Microsoft.Extensions.Http": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "jDj+4aDByk47oESlDDTtk6LWzlXlmoCsjCn6ihd+i9OntN885aPLszUII5+w0B/7wYSZcS3KdjqLAIhKLSiBXQ==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Diagnostics": "9.0.8",
"Microsoft.Extensions.Logging": "9.0.8",
"Microsoft.Extensions.Logging.Abstractions": "9.0.8",
"Microsoft.Extensions.Options": "9.0.8"
}
},
"Microsoft.Extensions.Logging": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "Z/7ze+0iheT7FJeZPqJKARYvyC2bmwu3whbm/48BJjdlGVvgDguoCqJIkI/67NkroTYobd5geai1WheNQvWrgA==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "9.0.8",
"Microsoft.Extensions.Logging.Abstractions": "9.0.8",
"Microsoft.Extensions.Options": "9.0.8"
}
},
"Microsoft.Extensions.Logging.Abstractions": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "pYnAffJL7ARD/HCnnPvnFKSIHnTSmWz84WIlT9tPeQ4lHNiu0Az7N/8itihWvcF8sT+VVD5lq8V+ckMzu4SbOw==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8"
}
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "OmTaQ0v4gxGQkehpwWIqPoEiwsPuG/u4HUsbOFoWGx4DKET2AXzopnFe/fE608FIhzc/kcg2p8JdyMRCCUzitQ==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Primitives": "9.0.8"
}
},
"Microsoft.Extensions.Options.ConfigurationExtensions": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "eW2s6n06x0w6w4nsX+SvpgsFYkl+Y0CttYAt6DKUXeqprX+hzNqjSfOh637fwNJBg7wRBrOIRHe49gKiTgJxzQ==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
"Microsoft.Extensions.Configuration.Binder": "9.0.8",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Options": "9.0.8",
"Microsoft.Extensions.Primitives": "9.0.8"
}
},
"Microsoft.Extensions.Primitives": {
"type": "Transitive",
"resolved": "9.0.8",
"contentHash": "tizSIOEsIgSNSSh+hKeUVPK7xmTIjR8s+mJWOu1KXV3htvNQiPMFRMO17OdI1y/4ZApdBVk49u/08QGC9yvLug=="
},
"atlantis.api": {
"type": "Project",
"dependencies": {
"FSharp.Core": "[9.0.303, )",
"Hipster.Api": "[1.0.1, )",
"Petimeter.Api": "[1.0.0, )"
}
},
"hipster.api": {
"type": "Project",
"dependencies": {
"Dapr.Actors": "[1.16.0, )",
"Drifters.Api": "[6.22.0, )",
"FSharp.Core": "[9.0.303, )"
}
},
"lib": {
"type": "Project",
"dependencies": {
"Atlantis.Api": "[1.0.1, )",
"FSharp.Core": "[9.0.303, )",
"Fable.Browser.IndexedDB": "[2.2.0, )",
"Fable.Browser.WebGL": "[1.3.0, )",
"Fable.Core": "[4.4.0, )",
"Fable.Elmish": "[4.2.0, )",
"Fable.Fetch": "[2.7.0, )",
"Fable.Lit": "[1.6.2-oceanbox, )",
"Fable.Lit.Elmish": "[1.6.2-oceanbox, )",
"Fable.Lit.React": "[1.6.2-oceanbox, )",
"Fable.OpenLayers": "[2.19.0, )",
"Fable.Promise": "[3.2.0, )",
"Fable.React": "[9.4.0, )",
"Fable.Remoting.Client": "[7.32.0, )",
"Fable.Remoting.MsgPack": "[1.24.0, )",
"Fable.SignalR.Elmish": "[2.1.0, )",
"Fable.SimpleHttp": "[3.6.0, )",
"Feliz": "[2.9.0, )",
"Feliz.CompilerPlugins": "[2.2.0, )",
"FsToolkit.ErrorHandling": "[5.0.1, )",
"Hipster.Api": "[1.0.1, )",
"Matplotlib.ColorMaps": "[3.0.1, )",
"Oceanbox.DataAgent.Api": "[7.2.1, )",
"Petimeter.Api": "[1.0.0, )",
"Sorcerer.Api": "[4.19.0, )",
"Thoth.Fetch": "[3.0.1, )",
"Thoth.Json": "[10.4.1, )"
}
},
"Oceanbox.DataAgent.Api": {
"type": "Project",
"dependencies": {
"FSharp.Core": "[9.0.303, )"
}
},
"petimeter.api": {
"type": "Project",
"dependencies": {
"Dapr.Actors": "[1.16.0, )",
"FSharp.Core": "[9.0.303, )"
}
},
"sorcerer.api": {
"type": "Project",
"dependencies": {
"Drifters.Api": "[6.22.0, )",
"FSharp.Core": "[9.0.303, )",
"Oceanbox.DataAgent.Api": "[7.2.1, )"
}
},
"Dapr.Actors": {
"type": "CentralTransitive",
"requested": "[1.16.0, )",
"resolved": "1.16.0",
"contentHash": "s9v6VofXXYoRqZJQlQbvNYYSlGhkL+Z+bpqrx1TRo06kLhANeDmXA9yeVaD+1KwJIO1chUFj5O4iKuTxIkg1sA==",
"dependencies": {
"Dapr.Client": "1.16.0",
"Dapr.Common": "1.16.0",
"Google.Api.CommonProtos": "2.17.0",
"Google.Protobuf": "3.32.0",
"Grpc.Net.Client": "2.71.0",
"Microsoft.Extensions.Configuration": "9.0.8",
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
"Microsoft.Extensions.DependencyInjection": "9.0.8",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Http": "9.0.8",
"Microsoft.Extensions.Logging": "9.0.8",
"Microsoft.Extensions.Logging.Abstractions": "9.0.8",
"Microsoft.Extensions.Options": "9.0.8"
}
},
"Dapr.Client": {
"type": "CentralTransitive",
"requested": "[1.16.0, )",
"resolved": "1.16.0",
"contentHash": "dFDKol+mtQrk1lIKlEyCx3k6W0Pf+0wC6xcsaDqa0Bg+XCWDc4juROuDcSb0/L1Y+Ev6LSLDMC/FgzNWMw9YtQ==",
"dependencies": {
"Dapr.Common": "1.16.0",
"Dapr.Protos": "1.16.0",
"Google.Api.CommonProtos": "2.17.0",
"Google.Protobuf": "3.32.0",
"Grpc.Net.Client": "2.71.0",
"Microsoft.Extensions.Configuration": "9.0.8",
"Microsoft.Extensions.Configuration.Abstractions": "9.0.8",
"Microsoft.Extensions.DependencyInjection": "9.0.8",
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.8",
"Microsoft.Extensions.Http": "9.0.8",
"Microsoft.Extensions.Logging": "9.0.8",
"Microsoft.Extensions.Logging.Abstractions": "9.0.8",
"Microsoft.Extensions.Options": "9.0.8"
}
},
"Drifters.Api": {
"type": "CentralTransitive",
"requested": "[6.22.0, )",
"resolved": "6.22.0",
"contentHash": "EQguKE22Tfd3ayO/jdWiWMBK5R1uzcYo+8agG3ZzAJ1ltl72mIXHqr68BKqO4uhOLtiFs8ErZa4cZ9NVueYHWA==",
"dependencies": {
"FSharp.Core": "9.0.201"
}
},
"FsToolkit.ErrorHandling": {
"type": "CentralTransitive",
"requested": "[5.0.1, )",
"resolved": "5.0.1",
"contentHash": "93oG3WSogK05H4gkikAmx5pBf30TQJfO1Jky+o/N/nv+RTP3nfOfjlmCHzuyUjQCRFOQog/xQabcky+WBWceeQ==",
"dependencies": {
"FSharp.Core": "9.0.300"
}
}
}
}
}

View File

@@ -16,63 +16,46 @@ let fetchArchives modelAreaId callback =
let! res = archmaester.getArchive modelAreaId
match res with
| Error err ->
console.error $"Fetch archives error: {err}"
| Ok archives ->
Some [| archives |]
|> callback
} |> Async.StartImmediate
| Error err -> console.error $"Fetch archives error: {err}"
| Ok archives -> Some [| archives |] |> callback
}
|> Async.StartImmediate
let fetchModelAreas callback =
async {
let! res = archmaester.getModelArea HelloWorld
let! res = archmaester.getModelAreaArchives (HelloWorld, ArchiveType.FromString "*:*:*")
match res with
| Error err ->
console.error $"Fetch model areas error: {err}"
| Ok modelAreas ->
Some [| modelAreas |]
|> callback
| Error err -> console.error $"Fetch model areas error: {err}"
| Ok modelAreas -> Some modelAreas |> callback
}
[<LitElement("archive-listing")>]
let ArchiveListing () =
let _, _ =
LitElement.init(fun init ->
init.useShadowDom <- false
)
let _, _ = LitElement.init (fun init -> init.useShadowDom <- false)
let (modelAreas: ModelArea [] option), setModelAreas = Hook.useState None
let (modelAreas: ArchiveProps[] option), setModelAreas = Hook.useState None
Hook.useEffectOnce(fun _ ->
fetchModelAreas setModelAreas
|> Async.StartImmediate
)
Hook.useEffectOnce (fun _ -> fetchModelAreas setModelAreas |> Async.StartImmediate)
let archiveId (a: Archmaester.Dto.ArchiveProps) = a.archiveId.ToString ()
let archiveFiles files =
let fileEntry (fileName, _) =
html $"""<li>{fileName}</li>"""
files
|> Array.sortBy fst
|> Lit.mapUnique fst fileEntry
let fileEntry (fileName, _) = html $"""<li>{fileName}</li>"""
files |> Array.sortBy fst |> Lit.mapUnique fst fileEntry
let archiveOwners (acl: ArchiveAcl option) =
let owners =
match acl with
| Some x -> x.users
| None -> [||]
let userEntry name =
html $"""<li>{name}</li>"""
let users' =
owners
|> Array.map userEntry
|> Lit.ofArray
let userEntry name = html $"""<li>{name}</li>"""
let users' = owners |> Array.map userEntry |> Lit.ofArray
if Array.isEmpty owners then
Lit.nothing
else
html $"""
html
$"""
<span>Owners:</span>
<div class="overflow-y-scroll">
<ul class="list-disc list-inside">
@@ -86,17 +69,14 @@ let ArchiveListing () =
match acl with
| Some x -> x.users
| None -> [||]
let userEntry name =
html $"""<li>{name}</li>"""
let users' =
users
|> Array.map userEntry
|> Lit.ofArray
let userEntry name = html $"""<li>{name}</li>"""
let users' = users |> Array.map userEntry |> Lit.ofArray
if Array.isEmpty users then
Lit.nothing
else
html $"""
html
$"""
<span>Users:</span>
<div class="overflow-y-scroll">
<ul class="list-disc list-inside">
@@ -110,17 +90,14 @@ let ArchiveListing () =
match acl with
| Some x -> x.users
| None -> [||]
let groupEntry name =
html $"""<li>{name}</li>"""
let groups' =
groups
|> Array.map groupEntry
|> Lit.ofArray
let groupEntry name = html $"""<li>{name}</li>"""
let groups' = groups |> Array.map groupEntry |> Lit.ofArray
if Array.isEmpty groups then
Lit.nothing
else
html $"""
html
$"""
<span>Groups:</span>
<div class="overflow-y-scroll max-h-96">
<ul class="list-disc list-inside">
@@ -130,7 +107,8 @@ let ArchiveListing () =
"""
let archiveItem (item: ArchiveProps) =
html $"""
html
$"""
<div class="card w-full shadow-xl">
<div class="card-body">
<h2 class="card-title">
@@ -149,20 +127,22 @@ let ArchiveListing () =
"""
let archiveName archiveName =
html $"""
html
$"""
<p>{archiveName}</p>
"""
let modelAreaItem (item: ModelArea) =
let archiveList =
Array.map archiveName item.archives
html $"""
let modelAreaItem (item: ArchiveProps) =
// let archiveList =
// Array.map archiveName item.archiveType
html
$"""
<div class="card w-full shadow-xl">
<div class="card-body">
<h2 class="card-title">
{item.name}
</h2>
{archiveList}
{item}
</div>
</div>
"""
@@ -170,18 +150,16 @@ let ArchiveListing () =
let modelAreaList =
match modelAreas with
| None -> html $"""<progress class="progress w-56"></progress>"""
| Some [||] ->
html $"""<h1>No ModelAreas</h1>"""
| Some [||] -> html $"""<h1>No ModelAreas</h1>"""
| Some models ->
html $"""
{models
|> Lit.mapUnique
(fun m -> m.name)
modelAreaItem}
html
$"""
{models |> Lit.mapUnique (fun m -> m.name) modelAreaItem}
"""
html $"""
html
$"""
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
{modelAreaList}
</div>
"""
"""

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<Version>6.20.0</Version>
<RootNamespace>Archivist</RootNamespace>
</PropertyGroup>
@@ -10,9 +10,9 @@
<Compile Include="App.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Fable.Lit" Version="1.4.2" />
<PackageReference Include="Fable.Remoting.Client" Version="7.32.0" />
<PackageReference Update="FSharp.Core" Version="9.0.201" />
<PackageReference Include="Fable.Lit" />
<PackageReference Include="Fable.Remoting.Client" />
<PackageReference Include="FSharp.Core" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\Interfaces\Archmaester\Archmaester.Api.fsproj" />

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<DefineConstants>FABLE_COMPILER</DefineConstants>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<Version>2.102.0</Version>
@@ -9,19 +9,19 @@
<RootNamespace>Main</RootNamespace>
</PropertyGroup>
<ItemGroup>
<None Include="index.html"/>
<None Include="map.html"/>
<None Include="atlas.html"/>
<None Include="./public/*.scss"/>
<None Include="./public\js\*.js"/>
<None Include="./public\js\modules\*"/>
<Compile Include="App.fs"/>
<None Include="index.html" />
<None Include="map.html" />
<None Include="atlas.html" />
<None Include="./public/*.scss" />
<None Include="./public\js\*.js" />
<None Include="./public\js\modules\*" />
<Compile Include="App.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="FSharp.Core" Version="9.0.201"/>
<PackageReference Include="FSharp.Core" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="Atlas\Atlas.fsproj"/>
<ProjectReference Include="Mapster\Mapster.fsproj"/>
<ProjectReference Include="Atlas\Atlas.fsproj" />
<ProjectReference Include="Mapster\Mapster.fsproj" />
</ItemGroup>
</Project>
</Project>

View File

@@ -1,8 +1,10 @@
module Auth
open System
open Browser
open Thoth.Json
open Remoting
type private JwtToken = { exp: int }
@@ -13,17 +15,26 @@ let private jwtDecoder =
let establishAuthentication () =
async {
match! authApi.IsAuthenticated () with
| Some _ ->
console.debug $"user authenticated"
| Some _ -> console.debug $"user authenticated"
| None ->
console.log "user not authenticated"
console.error "User not authenticated"
window.location.href <- "/signin"
}
let setId () : Async<unit> =
async {
match! authApi.GetIdentity () with
| Some id ->
sessionStorage["id"] <- id.user
| None ->
console.error "[Auth] User not authenticated"
window.location.href <- "/signin"
}
let rec tokenRefreshLoop () =
async {
let decode (token: string) =
localStorage[ token ].Split '.'
localStorage[token].Split '.'
|> fun x -> Utils.fromBase64String x[1]
|> jwtDecoder
|> function
@@ -31,7 +42,7 @@ let rec tokenRefreshLoop () =
| Error e ->
console.log e
0
let t = DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()
let t = DateTimeOffset(DateTime.Now).ToUnixTimeSeconds ()
do! Async.Sleep 1000
let aExp = decode "access_token"
@@ -39,29 +50,44 @@ let rec tokenRefreshLoop () =
let dtA = int64 aExp - t
let dtR = int64 rExp - t
console.log $"{t} {dtA} {dtR}"
if dtR < 0 then Dom.window.location.href <- "/signin"
if dtR < 0 then
window.location.href <- "/signin"
if dtA < 30 then
async {
match! authApi.RefreshAccessToken localStorage["refresh_token"] with
| Some (access, refresh) ->
localStorage.setItem ("access_token", access)
localStorage.setItem ("refresh_token", refresh)
| None ->
Dom.window.location.href <- "/signin"
| None -> window.location.href <- "/signin"
}
|> Async.StartImmediate
do! Async.Sleep((int dtA - 30) * 1000)
do! Async.Sleep ((int dtA - 30) * 1000)
tokenRefreshLoop ()
}
|> Async.StartImmediate
let startTokenRefreshLoopOrLogin () =
async {
match! authApi.IsAuthenticated() with
match! authApi.IsAuthenticated () with
| Some _ ->
console.log "already authenticated"
tokenRefreshLoop ()
| None ->
console.log "not already authenticated"
Dom.window.location.href <- "/login"
window.location.href <- "/login"
}
let initSessionUrls () =
async {
try
let! archiveUrl = servicesApi.GetArchiveService ()
let! dataUrl = servicesApi.GetFileService ()
console.log $"Archive service: {archiveUrl}"
sessionStorage["archmaester_url"] <- archiveUrl
console.log $"Data service: {dataUrl}"
sessionStorage["sorcerer_url"] <- dataUrl
with e ->
console.error("Failed fetching services. Redirecting to signin. %s", e.Message)
window.location.href <- "/signin"
}

View File

@@ -0,0 +1,48 @@
module Chaikin
let private cut (start: float * float) (end': float * float) (ratio: float) =
let startX, startY = start
let endX, endY = end'
// Find point at a given ratio going from A to B
let r1 =
startX * (1.0 - ratio) + endX * ratio, startY * (1.0 - ratio) + endY * ratio
// Find point at a given ratio going from B to A
let r2 =
startX * ratio + endX * (1.0 - ratio), startY * ratio + endY * (1.0 - ratio)
r1, r2
let private chaikin (curve: (float * float) array) (iterations: int) (closed: bool) (ratio: float) =
// If ratio is greater than 0.5 flip it so we avoid cutting across the midpoint of the line.
let adjustedRatio = if ratio > 0.5 then 1.0 - ratio else ratio
let rec iterate (currentPoints: (float * float) array) remainingIterations =
let len = currentPoints.Length
if remainingIterations <= 0 || len = 0 then
currentPoints
else
let refined = ResizeArray<float * float> ()
refined.Add (currentPoints[0])
for i in 0 .. len - 2 do
let r1, r2 = cut currentPoints[i] currentPoints[i + 1] adjustedRatio
refined.Add r1
refined.Add r2
if closed then
let r1, r2 = cut currentPoints[len - 1] currentPoints[0] adjustedRatio
refined.Add r1
refined.Add r2
else
refined.Add (currentPoints[len - 1])
iterate (refined.ToArray ()) (remainingIterations - 1)
iterate curve iterations
let chaikinDefault curve = chaikin curve 1 false 0.25
let chaikinWithIterations iterations curve = chaikin curve iterations false 0.25
let chaikinWithClosed closed curve = chaikin curve 1 closed 0.25

View File

@@ -0,0 +1,20 @@
namespace Lib
module Colors =
open Matplotlib.ColorMaps
open Atlantis.Types
let getColormap (colorMap: ColorMode * ColorMap) (lo, hi) =
let mode, cmap = colorMap
let cm =
match cmap with
| ColorMap.Ocean name -> ColorMap<single>(name, lo, hi, 256, Ocean.colors)
| ColorMap.Color16 name -> ColorMap<single>(name, lo, hi, 256, Color16.colors)
| ColorMap.Custom _ -> failwith "not implemented"
match mode with
| ColorMode.Normal -> cm
| ColorMode.Mirror -> ColorMap<single>.mirrorPalette cm
| ColorMode.Gray -> ColorMap<single>.toGrayscale cm

View File

@@ -43,9 +43,7 @@ module Types =
let RGBAFormat: obj = jsNative
[<AllowNullLiteral>]
type Matrix4 =
interface
end
type Matrix4 = interface end
[<AllowNullLiteral>]
type Euler =
@@ -77,9 +75,7 @@ module Types =
abstract Create: ?p1: obj * ?p2: obj * ?p3: obj * ?p4: obj -> Vector4
[<AllowNullLiteral>]
type Float32BufferAttribute =
interface
end
type Float32BufferAttribute = interface end
and [<AllowNullLiteral>] Float32BufferAttributeType =
[<Emit("new THREE.$0($1...)")>]
abstract Create: p1: obj * p2: int -> Float32BufferAttribute
@@ -221,9 +217,7 @@ module Types =
abstract Create: unit -> DirectionalLight
[<AllowNullLiteral>]
type EventDispatcher =
interface
end
type EventDispatcher = interface end
// Type to resolve parameters for Geometries.
[<AllowNullLiteral>]

View File

@@ -91,7 +91,7 @@ module rec Timeline =
abstract ``end``: DateTime option with set, get
abstract start: DateTime with get, set
abstract object: obj
abstract type' : ItemType
abstract type': ItemType
[<StringEnum>]
[<RequireQualifiedAccess>]
@@ -116,11 +116,14 @@ module rec Timeline =
let inline mkTooltip (key: string) (value: obj) : TimelineTooltipOption = unbox (key, value)
type tooltip' =
static member inline template(value: string) : TimelineTooltipOption = mkTooltip "template" value
static member inline template(value: (TimelineItem) -> string) : TimelineTooltipOption = mkTooltip "template" value
static member inline template(value: (TimelineItem * TimelineItem) -> string) : TimelineTooltipOption = mkTooltip "template" value
static member inline followMouse(value: bool): TimelineTooltipOption = mkTooltip "followMouse" value
static member inline overflowMethod(value: TimelineTooltipOptionOverflowMethod): TimelineTooltipOption = mkTooltip "overflowMethod" value
static member inline delay(value: int): TimelineTooltipOption = mkTooltip "delay" value
static member inline template(value: (TimelineItem) -> string) : TimelineTooltipOption =
mkTooltip "template" value
static member inline template(value: (TimelineItem * TimelineItem) -> string) : TimelineTooltipOption =
mkTooltip "template" value
static member inline followMouse(value: bool) : TimelineTooltipOption = mkTooltip "followMouse" value
static member inline overflowMethod(value: TimelineTooltipOptionOverflowMethod) : TimelineTooltipOption =
mkTooltip "overflowMethod" value
static member inline delay(value: int) : TimelineTooltipOption = mkTooltip "delay" value
let private createTimelineItem options : TimelineItem = unbox options
@@ -174,7 +177,8 @@ module rec Timeline =
static member inline maxHeight(value: string) : TimelineOptions = mkTimelineOption "maxHeight" value
/// This option allows you to scroll horizontally to move backwards and forwards in the time range.
/// Only applicable when option zoomKey is defined or zoomable is false.
static member inline horizontalScroll(value: bool) : TimelineOptions = mkTimelineOption "horizontalScroll" value
static member inline horizontalScroll(value: bool) : TimelineOptions =
mkTimelineOption "horizontalScroll" value
/// NOTE: This probably does not work. Only functioning workaround we have found for nb locales is:
///
/// o?moment <- fun x ->
@@ -202,18 +206,19 @@ module rec Timeline =
static member inline groupOrder(value: string) : TimelineOptions = mkTimelineOption "groupOrder" value
/// Use a function to render groups as react elements.
/// Uses React.render in the bindings to apply the given function to all groups.
static member inline groupTemplate(renderFunc: obj -> Feliz.ReactElement): TimelineOptions =
static member inline groupTemplate(renderFunc: obj -> Feliz.ReactElement) : TimelineOptions =
let value (item, elem, _data) =
let root = Feliz.ReactDOM.createRoot elem
root.render(renderFunc item)
root.render (renderFunc item)
mkTimelineOption "groupTemplate" value
static member inline selectable(value: bool) : TimelineOptions = mkTimelineOption "selectable" value
static member inline showWeekScale(value: bool) : TimelineOptions = mkTimelineOption "showWeekScale" value
/// Reduces down to timeline.template, renamed to itemTemplate to match groupTemplate.
/// Use a function to render items as react elements.
/// Uses React.render in the bindings to apply the given function to all items.
static member inline itemTemplate(renderFunc: obj -> Feliz.ReactElement): TimelineOptions =
mkTimelineOption "template"
static member inline itemTemplate(renderFunc: obj -> Feliz.ReactElement) : TimelineOptions =
mkTimelineOption
"template"
(fun (item, element, data) ->
Browser.Dom.console.log item
Browser.Dom.console.log element
@@ -223,7 +228,7 @@ module rec Timeline =
()
else
let root = Feliz.ReactDOM.createRoot element
root.render(renderFunc item)
root.render (renderFunc item)
)
static member inline timeAxis(value: timeAxis) : TimelineOptions = mkTimelineOption "timeAxis" value
static member inline tooltip(value: TimelineTooltipOption) : TimelineOptions = mkTimelineOption "tooltip" value
@@ -231,7 +236,7 @@ module rec Timeline =
/// Specifies whether the Timeline can be zoomed by pinching or scrolling in the window. Only applicable when option moveable is set true.
static member inline zoomable(value: bool) : TimelineOptions = mkTimelineOption "zoomable" value
/// Specifies how strong the zooming is for each scroll tick. Higher zooming friction will slow zooming speed.
static member inline zoomFriction(value: float): TimelineOptions = mkTimelineOption "zoomFriction" value
static member inline zoomFriction(value: float) : TimelineOptions = mkTimelineOption "zoomFriction" value
/// Specifies whether the Timeline is only zoomed when an additional key is down.
/// Available values are '' (does not apply), 'altKey', 'ctrlKey', 'shiftKey' or 'metaKey'.
/// Only applicable when option moveable is set true.
@@ -243,17 +248,10 @@ module rec Timeline =
/// <example>36_000_000. ms is a minimum zoom of 1 hour in ms</example>
static member inline zoomMin(value: float) : TimelineOptions = mkTimelineOption "zoomMin" value
static member inline xssWhitelist(value: obj) : TimelineOptions =
let xss = {|
disabled = false
filterOptions = {|
whiteList = value
|}
|}
let xss = {| disabled = false; filterOptions = {| whiteList = value |} |}
mkTimelineOption "xss" xss
type timeAxis =
interface
end
type timeAxis = interface end
[<StringEnum(CaseRules.LowerFirst)>]
[<RequireQualifiedAccess>]
@@ -274,9 +272,7 @@ module rec Timeline =
static member inline showStipes(value: bool) = mkTimelineOption "showStipes" value
// Timeline Options
type EditableOption =
interface
end
type EditableOption = interface end
let inline mkEditableOption (key: string) (value: obj) : EditableOption = unbox (key, value)
@@ -318,9 +314,9 @@ type IDataSet =
[<Import("DataSet", "vis-timeline/standalone")>]
type DataSet(data: U2<TimelineItem array, TimelineGroup array>, ?options: obj) =
new(data: TimelineItem array) = DataSet(U2.Case1 data)
new(data: TimelineItem array) = DataSet (U2.Case1 data)
new(data: TimelineGroup array) = DataSet(U2.Case2 data)
new(data: TimelineGroup array) = DataSet (U2.Case2 data)
/// <summary>
/// Add one or multiple items to the DataSet. data can be a single item or an array with items. Adding an item will fail when there already is an item with the same id. The function returns an array with the ids of the added items. See section Data Manipulation.
@@ -450,38 +446,34 @@ type DataSet(data: U2<TimelineItem array, TimelineGroup array>, ?options: obj) =
type AnimationOptions =
| Animation of AnimationOption
| Bool of bool
and AnimationOption = {
duration: float
easingFunction: EasingFunction
}
and
[<StringEnum(CaseRules.LowerFirst)>]
EasingFunction =
| Linear
| EaseInQuad
| EaseOutQuad
| EaseInOutQuad
| EaseInCubic
| EaseOutCubic
| EaseInOutCubic
| EaseInQuart
| EaseOutQuart
| EaseInOutQuart
| EaseInQuint
| EaseOutQuint
| EaseInOutQuint
and AnimationOption = { duration: float; easingFunction: EasingFunction }
and [<StringEnum(CaseRules.LowerFirst)>] EasingFunction =
| Linear
| EaseInQuad
| EaseOutQuad
| EaseInOutQuad
| EaseInCubic
| EaseOutCubic
| EaseInOutCubic
| EaseInQuart
| EaseOutQuart
| EaseInOutQuart
| EaseInQuint
| EaseOutQuint
| EaseInOutQuint
[<AllowNullLiteral>]
[<Import("Timeline", "vis-timeline/standalone")>]
type Timeline(container: HTMLElement, items: U2<TimelineItem seq, DataSet>, ?groups: U2<TimelineGroup seq, DataSet>, ?options: TimelineOptions) =
type Timeline
(
container: HTMLElement,
items: U2<TimelineItem seq, DataSet>,
?groups: U2<TimelineGroup seq, DataSet>,
?options: TimelineOptions
) =
new(container, items, groups, options) =
Timeline(
container = container,
items = U2.Case2 items,
groups = U2.Case2 groups,
options = options
)
Timeline (container = container, items = U2.Case2 items, groups = U2.Case2 groups, options = options)
/// <summary>
/// Add new vertical bar representing a custom time that can be dragged by the user. Parameter time can be a Date,
@@ -559,8 +551,8 @@ type Timeline(container: HTMLElement, items: U2<TimelineItem seq, DataSet>, ?gro
member _.getCustomTime() : DateTime = jsNative
member _.getCustomTime(ids: Id array) : DateTime = jsNative
member _.itemsData : IDataSet = jsNative
member _.groupsData : IDataSet = jsNative
member _.itemsData: IDataSet = jsNative
member _.groupsData: IDataSet = jsNative
member _.moveTo(time: DateTime, ?options, ?callback) : unit = jsNative
member _.on(_: string, _: 'a -> unit) : unit = jsNative
@@ -601,7 +593,7 @@ type Timeline(container: HTMLElement, items: U2<TimelineItem seq, DataSet>, ?gro
member _.setGroups(_: DataSet) : unit = jsNative
member _.setOptions(_: obj) : unit = jsNative
member _.setSelection(_: string []) : unit = jsNative
member _.setSelection(_: string[]) : unit = jsNative
member _.setWindow(start: DateTime, end': DateTime, ?options: obj, ?callback: obj -> unit) : unit = jsNative
member _.zoomIn(_: float) : unit = jsNative
@@ -630,10 +622,11 @@ type Timeline(container: HTMLElement, items: U2<TimelineItem seq, DataSet>, ?gro
///
/// A callback function can be passed as an optional parameter. This function will be called at the end of zoomOut function.
/// </summary>
member _.zoomOut(percentage: float, options: {| animation: AnimationOption |}, ?callback: obj -> unit) : unit = jsNative
member _.zoomOut(percentage: float, options: {| animation: AnimationOption |}, ?callback: obj -> unit) : unit =
jsNative
member _.zoomOut(percentage: float, options: {| animation: bool |}, ?callback: obj -> unit) : unit = jsNative
member _.zoomOut(percentage: float) : unit = jsNative
[<ImportDefault("moment")>]
type Moment(timestamp: int) =
member _.locale(_:string): Moment = jsNative
member _.locale(_: string) : Moment = jsNative

View File

@@ -13,14 +13,14 @@ let floatingBox () =
let this, props =
LitElement.init (fun cfg ->
cfg.useShadowDom <- true
cfg.props <-
{|
title = Prop.Of ""
xPos = Prop.Of (Dom.window.document.documentElement.clientWidth - 250.0)
yPos = Prop.Of 50.0
|}
cfg.props <- {|
title = Prop.Of ""
xPos = Prop.Of (Dom.window.document.documentElement.clientWidth - 250.0)
yPos = Prop.Of 50.0
|}
cfg.styles <- [
css $"""
css
$"""
#floating-box {{
position: fixed;
padding: 3px;
@@ -38,8 +38,8 @@ let floatingBox () =
font-family: Georgia;
}}
"""
]
)
]
)
let xPos = props.xPos.Value
let yPos = props.yPos.Value
@@ -47,35 +47,41 @@ let floatingBox () =
if this.shadowRoot |> isNull |> not then
Hook.useEffectOnce (fun () ->
let document = Dom.document
let floatingBox = getShadowElementById this "floating-box" :?> Browser.Types.HTMLDivElement
let floatingBox =
getShadowElementById this "floating-box" :?> Browser.Types.HTMLDivElement
let mutable x, y = xPos, yPos
let mutable x', y' = xPos, yPos
floatingBox.onmousedown <- fun e ->
this.dispatchCustomEvent("dragStart")
// e.preventDefault ()
// NOTE(SimenLK): Hacky attempt to prevent moving the floating box when pressing items within it
x' <- e.clientX
y' <- e.clientY
floatingBox.onmousedown <-
fun e ->
this.dispatchCustomEvent ("dragStart")
// e.preventDefault ()
// NOTE(SimenLK): Hacky attempt to prevent moving the floating box when pressing items within it
x' <- e.clientX
y' <- e.clientY
floatingBox?style?cursor <- "grabbing"
document.onmouseup <- fun _ ->
this.dispatchCustomEvent("dragStop")
floatingBox?style?cursor <- ""
document.onmouseup <- fun _ -> ()
document.onmousemove <- fun _ -> ()
document.onmousemove <- fun e' ->
this.dispatchCustomEvent("dragging")
e.preventDefault ()
x <- x' - e'.clientX
y <- y' - e'.clientY
x' <- e'.clientX
y' <- e'.clientY
floatingBox?style?left <- $"{(floatingBox.offsetLeft - x)}px" // $"{e'.clientX}px"
floatingBox?style?top <- $"{(floatingBox.offsetTop - y)}px" // $"{e'.clientY}px"
()
())
floatingBox?style?cursor <- "grabbing"
document.onmouseup <-
fun _ ->
this.dispatchCustomEvent ("dragStop")
floatingBox?style?cursor <- ""
document.onmouseup <- fun _ -> ()
document.onmousemove <- fun _ -> ()
document.onmousemove <-
fun e' ->
this.dispatchCustomEvent ("dragging")
e.preventDefault ()
x <- x' - e'.clientX
y <- y' - e'.clientY
x' <- e'.clientX
y' <- e'.clientY
floatingBox?style?left <- $"{(floatingBox.offsetLeft - x)}px" // $"{e'.clientX}px"
floatingBox?style?top <- $"{(floatingBox.offsetTop - y)}px" // $"{e'.clientY}px"
()
()
)
html $"""
html
$"""
<div
id="floating-box" resize="both"
style="top: {yPos}px; left: {xPos}px;"

View File

@@ -11,74 +11,85 @@ let openDB () : IDBDatabase JS.Promise =
Promise.create (fun resolve reject ->
let openRequest = indexedDB.``open`` ("oceanbox", dbVersion)
openRequest.onerror <- (fun _ ->
console.error "Failed opening oceanbox database"
reject (unbox "Fail"))
openRequest.onerror <-
fun _ ->
console.error "Failed opening oceanbox database"
reject (unbox "Fail")
openRequest.onsuccess <-
fun _ ->
// console.log "oceanbox database opened"
let db: IDBDatabase = unbox openRequest.result
resolve db
openRequest.onupgradeneeded <-
fun ev ->
// console.log $"Upgrading oceanbox database"
let db: IDBDatabase = ev.target?result
db.onerror <- (fun ev -> console.error $"Could not upgrade database: {ev.target}")
let archives =
db.createObjectStore ("PlainGrids", !!{| keyPath = "GridSha"; autoIncrement = true |})
archives.createIndex ("Vertices", "Vertices") |> ignore
archives.createIndex ("Indices", "Indices") |> ignore
openRequest.onsuccess <- (fun _ ->
// console.log "oceanbox database opened"
let db: IDBDatabase = unbox openRequest.result
resolve db
)
openRequest.onupgradeneeded <- (fun ev ->
// console.log $"Upgrading oceanbox database"
let db: IDBDatabase = ev.target?result
db.onerror <- (fun ev -> console.error $"Could not upgrade database: {ev.target}")
let archives = db.createObjectStore("PlainGrids", !!{| keyPath = "GridSha"; autoIncrement = true |})
archives.createIndex("Vertices", "Vertices") |> ignore
archives.createIndex("Indices", "Indices") |> ignore
)
)
let getFromIDB<'T> (db: IDBDatabase) (store: string) (sha: string): 'T option JS.Promise =
let getFromIDB<'T> (db: IDBDatabase) (store: string) (sha: string) : 'T option JS.Promise =
Promise.create (fun resolve reject ->
let t = db.transaction(store, IDBTransactionMode.Readonly)
let t = db.transaction (store, IDBTransactionMode.Readonly)
let index = t.objectStore store
let request = index.get sha
t.commit ()
request.onerror <- (fun _ ->
console.error $"Failed retrieving {store}: {sha}"
reject (unbox false))
request.onerror <-
fun _ ->
console.error $"Failed retrieving {store}: {sha}"
reject (unbox false)
request.onsuccess <- (fun _ ->
// console.log $"checking {store} {sha}"
// NOTE(SimenLK): when getting on aid, if it is not present, the result will be undefined, otherwise, unbox
// to get the DB archive grid
request.result
|> Option.map unbox<'T>
|> resolve)
request.onsuccess <-
fun _ ->
// console.log $"checking {store} {sha}"
// NOTE(SimenLK): when getting on aid, if it is not present, the result will be undefined, otherwise, unbox
// to get the DB archive grid
request.result |> Option.map unbox<'T> |> resolve
)
let saveToIDB (db: IDBDatabase) (store: string) item =
Promise.create (fun resolve reject ->
let t = db.transaction(store, IDBTransactionMode.Readwrite)
let archives = t.objectStore(store)
let t = db.transaction (store, IDBTransactionMode.Readwrite)
let archives = t.objectStore (store)
let req = archives.add item
req.onerror <- (fun ev ->
let error = ev.target :?> IDBRequest
console.error $"saveToIDB error: {error.error}"
reject (error.error :?> exn))
req.onsuccess <- (fun _ ->
console.debug $"Successfully added {store} to indexedDB"
resolve ())
)
req.onerror <-
fun ev ->
let error = ev.target :?> IDBRequest
console.error $"saveToIDB error: {error.error}"
reject (error.error :?> exn)
req.onsuccess <-
fun _ ->
console.debug $"Successfully added {store} to indexedDB"
resolve ()
)
let tryResetOutdatedIDB () =
Promise.create (fun resolve reject ->
openDB () |> Promise.iter (fun db ->
openDB ()
|> Promise.iter (fun db ->
if db.objectStoreNames.contains "WireframeGrids" then
db.close ()
let req = indexedDB.deleteDatabase "oceanbox"
req.onerror <- (fun ev ->
let error = ev.target :?> IDBRequest
console.error $"Reset indexDB error: {error.error}"
reject (error.error :?> exn))
req.onsuccess <- (fun _ ->
console.debug $"Successfully reset indexedDB"
resolve ())
req.onerror <-
fun ev ->
let error = ev.target :?> IDBRequest
console.error $"Reset indexDB error: {error.error}"
reject (error.error :?> exn)
req.onsuccess <-
fun _ ->
console.debug $"Successfully reset indexedDB"
resolve ()
else
resolve ()
resolve ()
)
)

View File

@@ -5,11 +5,18 @@ open Fable.Core.JsInterop
let register () = ()
importAll "../public/style.scss"
importSideEffects "@spectrum-web-components/theme/sp-theme.js"
importSideEffects "@spectrum-web-components/theme/spectrum-two/scale-medium.js"
importSideEffects "@spectrum-web-components/theme/spectrum-two/scale-large.js"
importSideEffects "@spectrum-web-components/theme/spectrum-two/theme-light.js"
importSideEffects "@spectrum-web-components/theme/spectrum-two/theme-dark.js"
importSideEffects "@spectrum-web-components/accordion/sp-accordion.js"
importSideEffects "@spectrum-web-components/accordion/sp-accordion-item.js"
importSideEffects "@spectrum-web-components/action-button/sp-action-button.js"
importSideEffects "@spectrum-web-components/action-group/sp-action-group.js"
importSideEffects "@spectrum-web-components/action-menu/sp-action-menu.js"
importSideEffects "@spectrum-web-components/action-menu/sync/sp-action-menu.js"
importSideEffects "@spectrum-web-components/alert-banner/sp-alert-banner.js"
importSideEffects "@spectrum-web-components/button/sp-button.js"
importSideEffects "@spectrum-web-components/card/sp-card.js"
importSideEffects "@spectrum-web-components/checkbox/sp-checkbox.js"
@@ -24,13 +31,12 @@ importSideEffects "@spectrum-web-components/menu/sp-menu-group.js"
importSideEffects "@spectrum-web-components/menu/sp-menu-item.js"
importSideEffects "@spectrum-web-components/menu/sp-menu-divider.js"
importSideEffects "@spectrum-web-components/number-field/sp-number-field.js"
importSideEffects "@spectrum-web-components/picker/sp-picker.js"
importSideEffects "@spectrum-web-components/popover/sp-popover.js"
importSideEffects "@spectrum-web-components/progress-bar/sp-progress-bar.js"
importSideEffects "@spectrum-web-components/progress-circle/sp-progress-circle.js"
importSideEffects "@spectrum-web-components/radio/sp-radio.js"
importSideEffects "@spectrum-web-components/radio/sp-radio-group.js"
importSideEffects "@spectrum-web-components/slider/sp-slider.js"
importSideEffects "@spectrum-web-components/slider/sync/sp-slider.js"
importSideEffects "@spectrum-web-components/slider/sp-slider-handle.js"
importSideEffects "@spectrum-web-components/split-view/sp-split-view.js"
importSideEffects "@spectrum-web-components/switch/sp-switch.js"
@@ -44,28 +50,31 @@ importSideEffects "@spectrum-web-components/table/sp-table-head-cell.js"
importSideEffects "@spectrum-web-components/table/sp-table-row.js"
importSideEffects "@spectrum-web-components/tabs/sp-tabs.js"
importSideEffects "@spectrum-web-components/tabs/sp-tab.js"
importSideEffects "@spectrum-web-components/tabs/sp-tab-panel.js"
importSideEffects "@spectrum-web-components/textfield/sp-textfield.js"
importSideEffects "@spectrum-web-components/theme/sp-theme.js"
importSideEffects "@spectrum-web-components/theme/spectrum-two/scale-medium.js"
importSideEffects "@spectrum-web-components/theme/spectrum-two/scale-large.js"
importSideEffects "@spectrum-web-components/theme/spectrum-two/theme-light.js"
importSideEffects "@spectrum-web-components/theme/spectrum-two/theme-dark.js"
importSideEffects "@spectrum-web-components/toast/sp-toast.js"
importSideEffects "@spectrum-web-components/top-nav/sp-top-nav.js"
importSideEffects "@spectrum-web-components/top-nav/sp-top-nav-item.js"
importSideEffects "@spectrum-web-components/underlay/sp-underlay.js"
importSideEffects "@spectrum-web-components/overlay/overlay-trigger.js"
importSideEffects "@spectrum-web-components/overlay/sync/overlay-trigger.js"
importSideEffects "@spectrum-web-components/overlay/sp-overlay.js"
importSideEffects "@spectrum-web-components/tooltip/sp-tooltip.js"
importSideEffects "@spectrum-web-components/dialog/sp-dialog.js"
importSideEffects "@spectrum-web-components/dialog/sp-dialog-base.js"
importSideEffects "@spectrum-web-components/dialog/sp-dialog-wrapper.js"
importSideEffects "@spectrum-web-components/infield-button/sp-infield-button.js"
importSideEffects "@spectrum-web-components/picker/sync/sp-picker.js"
importSideEffects "@spectrum-web-components/contextual-help/sp-contextual-help.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-add.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-add-circle.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-alert.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-prototyping.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-close.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-copy.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-crosshairs.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-target.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-delete.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-color-harmony.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-deselect.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-erase.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-reorder.js"
@@ -83,11 +92,17 @@ importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-asteris
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-bug.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-sampler.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-measure.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-ruler.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-circle.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-image-map-rectangle.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-histogram.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-chart-bar-vert.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-chart-trend.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-location.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-crop.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-download.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-edit.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-cancel.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-browse.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-filter.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-filter-remove.js"
@@ -105,4 +120,5 @@ importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-social-
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-checkmark.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-rotate-cc-w.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-align-bottom.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-transform-perspective.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-transform-perspective.js"
importSideEffects "@spectrum-web-components/icons-workflow/icons/sp-icon-search.js"

View File

@@ -0,0 +1,41 @@
module Intl
open Browser
open Fable.Core
open Fable.Core.JsInterop
[<Emit("new Intl.DateTimeFormat($0, $1)")>]
let private dateTimeFormat (lang: string) (opt: obj) = jsNative
// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat
let private uk : obj =
let opts = {|
dateStyle = "full"
timeStyle = "short"
|}
dateTimeFormat "en-GB" opts
// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat
let private ukShort : obj =
let opts = {|
dateStyle = "short"
|}
dateTimeFormat "en-GB" opts
// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat
let private ukDateTimeShort : obj =
let opts = {|
dateStyle = "short"
timeStyle = "short"
|}
dateTimeFormat "en-GB" opts
/// Returns date string formatted as e.g.: "Wednesday 11 June 2025 at 06:00"
let format (date: System.DateTime) : string = uk?format date
let shortDate (date: System.DateTime) : string = ukShort?format date
let shortDateTime (date: System.DateTime) : string = ukDateTimeShort?format date

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<DefineConstants>FABLE_COMPILER</DefineConstants>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<Version>2.87.0</Version>
@@ -9,41 +9,49 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="../../Shared/Atlantis.Shared.fs" />
<Compile Include="Fable.VisJS.fs" />
<Compile Include="Types.fs" />
<Compile Include="Utils.fs" />
<Compile Include="React.fs" />
<Compile Include="Sentry.fs" />
<Compile Include="Remoting.fs" />
<Compile Include="Auth.fs" />
<Compile Include="Search.fs" />
<Compile Include="Imports.fs" />
<Compile Include="Fable.VisJS.fs" />
<Compile Include="IDB.fs" />
<Compile Include="FloatingBox.fs" />
<Compile Include="Turf.fs" />
<Compile Include="Types.fs" />
<Compile Include="Remoting.fs" />
<Compile Include="Utils.fs" />
<Compile Include="Auth.fs" />
<Compile Include="Chaikin.fs" />
<Compile Include="Umami.fs" />
<Compile Include="StreamLayer.fs" />
<Compile Include="WebGLLayer.fs" />
<Compile Include="Intl.fs" />
<Compile Include="Colors.fs" />
<Compile Include="Maps.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Fable.Browser.IndexedDB" Version="2.2.0" />
<PackageReference Include="Fable.Browser.WebGL" Version="1.3.0" />
<PackageReference Include="Fable.Core" Version="4.4.0" />
<PackageReference Include="Fable.Elmish" Version="4.2.0" />
<PackageReference Include="Fable.Fetch" Version="2.7.0" />
<PackageReference Include="Fable.Lit" Version="1.6.2-oceanbox" />
<PackageReference Include="Fable.Lit.React" Version="1.6.2-oceanbox" />
<PackageReference Include="Fable.Lit.Elmish" Version="1.6.2-oceanbox" />
<PackageReference Include="Fable.Promise" Version="3.2.0" />
<PackageReference Include="Fable.React" Version="9.4.0" />
<PackageReference Include="Fable.Remoting.Client" Version="7.32.0" />
<PackageReference Include="Fable.Remoting.MsgPack" Version="1.24.0" />
<PackageReference Include="Fable.OpenLayers" Version="2.18.0" />
<PackageReference Include="Fable.SignalR.Elmish" Version="2.1.0" />
<PackageReference Include="Fable.SimpleHttp" Version="3.6.0" />
<PackageReference Include="Feliz" Version="2.9.0" />
<PackageReference Include="Feliz.CompilerPlugins" Version="2.2.0" />
<PackageReference Include="Thoth.Fetch" Version="3.0.1" />
<PackageReference Include="Thoth.Json" Version="10.4.1" />
<PackageReference Update="FSharp.Core" Version="9.0.201" />
<PackageReference Include="Matplotlib.ColorMaps" Version="3.0.1" />
<PackageReference Include="Fable.Browser.IndexedDB" />
<PackageReference Include="Fable.Browser.WebGL" />
<PackageReference Include="Fable.Core" />
<PackageReference Include="Fable.Elmish" />
<PackageReference Include="Fable.Fetch" />
<PackageReference Include="Fable.Lit" />
<PackageReference Include="Fable.Lit.React" />
<PackageReference Include="Fable.Lit.Elmish" />
<PackageReference Include="Fable.Promise" />
<PackageReference Include="Fable.React" />
<PackageReference Include="Fable.Remoting.Client" />
<PackageReference Include="Fable.Remoting.MsgPack" />
<PackageReference Include="Fable.OpenLayers" />
<PackageReference Include="Fable.SignalR.Elmish" />
<PackageReference Include="Fable.SimpleHttp" />
<PackageReference Include="Feliz" />
<PackageReference Include="Feliz.CompilerPlugins" />
<PackageReference Include="FsToolkit.ErrorHandling" />
<PackageReference Include="Thoth.Fetch" />
<PackageReference Include="Thoth.Json" />
<PackageReference Include="Matplotlib.ColorMaps" />
<PackageReference Include="FSharp.Core" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\Interfaces\Atlantis\Atlantis.Api.fsproj" />

View File

@@ -1,6 +1,5 @@
module Maps
open System.Text.RegularExpressions
open Browser
open Fable.Core
open Fable.Core.JsInterop
@@ -18,7 +17,7 @@ open Atlantis.Types
let theCenter = [| 14.39; 65.2 |]
let flyTo (map: OlMap) (zoom: float) (c: float []) =
let flyTo (map: OlMap) (zoom: float) (c: float[]) =
let v = map.getView ()
Animation.animationOptions [
@@ -32,7 +31,7 @@ let flyTo (map: OlMap) (zoom: float) (c: float []) =
let zoomOut (map: OlMap) (zoom: Zoom) =
let v = map.getView ()
let center = v.getCenter()
let center = v.getCenter ()
let anim =
Animation.animationOptions [
Animation.animation.center center
@@ -42,7 +41,7 @@ let zoomOut (map: OlMap) (zoom: Zoom) =
Animation.animation.easing Animation.easeOut
]
do v.animate(anim)
do v.animate (anim)
module SimpleWebGLLayer =
[<ImportDefault("../public/js/WebGLLayer")>]
@@ -69,6 +68,7 @@ type barbTile =
static member inline drawColor(value: string) = unbox ("drawColor", value)
static member inline time(value: int) = unbox ("time", value)
static member inline url(value: string) = unbox ("url", value)
static member inline template(value: string) = unbox ("template", value)
module BarbTile =
type BarbTile =
@@ -103,50 +103,40 @@ let getInteractionByName (map: OlMap) name =
|> fun c -> c.getArray ()
|> Seq.tryFind (fun i ->
let name' = i.get "name"
name' = name)
name' = name
)
let simpleWebGL layerName (data: MapData) =
webGLLayer [
webglLayer.className layerName
webglLayer.indices data.Grid.Indices
webglLayer.props data.Props
// NOTE(SimenLK): Breaks if you change the source from xyz
// NOTE(simkir): Breaks if you change the source from xyz
webglLayer.source (Source.xyz [])
webglLayer.vertices data.Grid.Vertices
webglLayer.opacity 0.15
webglLayer.attenuate 0.0
]
let osmLayer =
Layer.tileLayer [
layer.source (Source.osm [])
]
let osmLayer = Layer.tileLayer [ layer.source (Source.osm []) ]
let baseMapLayer =
Layer.tileLayer [
layer.source ((MapKind.MapTiler MapTiler.Basic).source())
] :> Layer.Layer
Layer.tileLayer [ layer.source ((MapKind.MapTiler MapTiler.Basic).source ()) ] :> Layer.Layer
let selectedMapLayer (kind: MapKind) =
match kind with
| OSM
| MapTiler _ ->
Layer.tileLayer [
layer.source (kind.source())
layer.source (kind.source ())
layer.minZoom 0.0
// layer.maxZoom 15.0
] :> Layer.Layer
// layer.maxZoom 15.0
]
:> Layer.Layer
| NorgesKart Sentinel2
| NorgesKart SeaRaster ->
Layer.imageLayer [
layer.source (kind.source ())
layer.minZoom (kind.minZoom())
] :> Layer.Layer
| NorgesKart _ ->
Layer.tileLayer [
layer.source (kind.source ())
layer.minZoom (kind.minZoom())
] :> Layer.Layer
Layer.imageLayer [ layer.source (kind.source ()); layer.minZoom (kind.minZoom ()) ] :> Layer.Layer
| NorgesKart _ -> Layer.tileLayer [ layer.source (kind.source ()); layer.minZoom (kind.minZoom ()) ] :> Layer.Layer
let createStreamsLayer (data: MapData) uvFlat bbox =
let streamIndices = data.Grid.Indices
@@ -161,13 +151,20 @@ let createStreamsLayer (data: MapData) uvFlat bbox =
]
// Example filter from openlayers example: https://openlayers.org/en/latest/examples/vector-wfs-getfeature.html
let private wfsLoader (vectorSource: VectorSource) = // must return a lambda!
fun (extent: Extent) (_: Resolution) (_: Projection) (success: Feature[] -> unit) (failure: unit -> unit) ->
let private wfsLoader (vectorSource: VectorSource) = // NOTE: must return a lambda!
fun
(extent: Extent)
(_resolution: Resolution)
(projection: Projection)
(success: Feature array -> unit)
(failure: unit -> unit) ->
let proj = projection?getCode()
let url = Lokaliteter.wfsUrl ()
let lower = extent[0], extent[1]
let upper = extent[2], extent[3]
// NOTE: THE XML IS CASE SENSITIVE!
let filter = $"""
let filter =
$"""
<fes:Filter>
<fes:And>
<fes:And>
@@ -190,93 +187,118 @@ let private wfsLoader (vectorSource: VectorSource) = // must return a lambda!
</fes:And>
</fes:Filter>"""
let query = $"""
<wfs:GetFeature service="WFS" version="2.0.0" outputFormat="GeoJSON"
let query =
$"""<wfs:GetFeature service="WFS" version="2.0.0" outputFormat="GeoJSON"
xmlns:FiskeridirWFS="http://gis.fiskeridir.no/wfs/2.0"
xmlns:wfs="http://www.opengis.net/wfs/2.0"
xmlns:fes="http://www.opengis.net/fes/2.0"
xmlns:gml="http://www.opengis.net/gml/3.2">
xmlns:gml="http://www.opengis.net/gml/3.2"
>
<wfs:Query typeName="FiskeridirWFS:Akvakultur_-_Lokaliteter" srsName="EPSG:3857">
{filter}
{filter}
</wfs:Query>
</wfs:GetFeature>"""
let xhr = XMLHttpRequest.Create()
let xhr = XMLHttpRequest.Create ()
xhr.``open`` ("POST", url)
xhr.addEventListener("error", fun _ ->
vectorSource.removeLoadedExtent(extent)
failure ())
xhr.addEventListener("load", fun _ ->
if xhr.status = 200 then
let fmt = vectorSource.getFormat () :?> GeoJSON
let features = fmt.readFeatures(xhr.responseText)
vectorSource.addFeatures features
success(features)
else
xhr.addEventListener (
"error",
fun _ ->
vectorSource.removeLoadedExtent extent
failure ()
)
)
xhr.addEventListener (
"load",
fun _ ->
if xhr.status = 200 then
let fmt = vectorSource.getFormat () :?> GeoJSON
let features = fmt.readFeatures xhr.responseText
do
features
|> Array.iter (fun feature ->
let geom = feature.getGeometry()
let coords = geom?getCoordinates()
let transformed = Utils.coordToEpsg3857 coords
geom?setCoordinates transformed
)
vectorSource.addFeatures features
success features
else
failure ()
)
xhr.send query
let private createAquacultureLocalityLayer () =
// let f : FeatureUrlFunction = emitJsExpr urlFunc "$0"
let vectorSource =
Source.vectorSource [
source.projection epsg3857
source.format (Format.geoJSON [])
source.strategy LoadingStrategy.bbox
]
let proj = vectorSource.getProjection()
console.debug("[Maps] Vector source projection: %o", proj)
// NOTE(simkir): Here is were we set the custom WFS loader
do vectorSource.setLoader (wfsLoader vectorSource)
let defaultStyle =
Style.style [
style.image (
Style.circle [
circle.radius 5.0
circle.fill (
Style.fill [
fill.color "rgba(150, 0, 0, 1.0)"
]
)
circle.stroke (
Style.stroke [
stroke.color "white"
]
)
]
)
]
let polygonStyle =
Style.style [
style.stroke (
Style.stroke [
stroke.color "white"
stroke.width 1
]
)
style.fill (
Style.fill [
fill.color "rgba(0.5, 0.5, 0.5, 0.1)"
]
)
]
Layer.vectorLayer [
layer.className (string MapLayer.Aquaculture)
layer.zIndex 12
layer.minZoom 7.5
layer.source vectorSource
layer.style (fun feature ->
if feature.get "type" = "polygon" then
polygonStyle
else
if feature.get "selected" = "true" then
Style.style []
else
defaultStyle
)
]
// Ref: https://gis.fiskeridir.no/server/services/FiskeridirWFS_akva/MapServer/WFSServer?SERVICE=WFS&REQUEST=GetCapabilities
// arcgis wfs info: https://enterprise.arcgis.com/en/server/latest/publish-services/linux/communicating-with-a-wfs-service-in-a-web-browser.htm
let fiskeri (topic: InfoLayer) : Ol.Layer.Layer =
if topic = Lokaliteter then
console.debug("fiskeri")
// let f : FeatureUrlFunction = emitJsExpr urlFunc "$0"
let vectorSource =
Source.vectorSource [
source.projection epsg3857
source.format (Format.geoJSON [])
source.strategy LoadingStrategy.bbox
]
vectorSource.setLoader (wfsLoader vectorSource)
let defaultStyle =
Style.style [
style.image (
Style.circle [
circle.radius 5.0
circle.fill (
Style.fill [
fill.color "rgba(150, 0, 0, 1.0)"
]
)
circle.stroke (
Style.stroke [
stroke.color "white"
]
)
]
)
]
let polygonStyle =
Style.style [
style.stroke (
Style.stroke [
stroke.color "white"
stroke.width 1
]
)
style.fill (
Style.fill [
fill.color "rgba(0.5, 0.5, 0.5, 0.1)"
]
)
]
// TODO: Try webglPoints again
Layer.vectorLayer [
layer.className (string MapLayer.Aquaculture)
layer.minZoom 7.5
layer.source vectorSource
layer.style (fun feature ->
if feature.get "type" = "polygon" then
polygonStyle
else
if feature.get "selected" = "true" then
Style.style []
else
defaultStyle)
layer.zIndex 12
]
console.debug ("[Map] Creating fiskeri OpenLayers Layer on topic %o", topic)
createAquacultureLocalityLayer ()
else
let alpha =
match topic with
@@ -290,30 +312,22 @@ let fiskeri (topic: InfoLayer) : Ol.Layer.Layer =
source.serverType Source.ServerType.Geoserver
source.params' !!{| layers = string topic |}
]
Layer.imageLayer [
layer.source source
layer.opacity alpha
]
Layer.imageLayer [ layer.source source; layer.opacity alpha ]
let toGeometry ((x, y): single * single) =
Geometry.point [
geometry.coordinates [|
float x
float y
|]
geometry.coordinates [| float x; float y |]
geometry.layout GeometryLayout.XY
]
let toFeature (p: single * single) =
let g = toGeometry p
let f =
Feature.feature [
feature.geometryOrProperties g
]
let f = Feature.feature [ feature.geometryOrProperties g ]
f.setGeometry g
f
let toFeatures (p: (single * single) []) = Array.map toFeature p
let toFeatures (p: (single * single)[]) = Array.map toFeature p
// let conc (r, b) (p: Particles) =
// let source =
@@ -362,49 +376,38 @@ let createWebGLWireframeLayer (grid: WireframeGrid) =
WebGLWireframeLayer.webGLWireframeLayer [
layer.source (Source.osm [])
layer.className "wireframe"
(Interop.mkLayerProp "vertices" grid.Vertices)
(Interop.mkLayerProp "barycentric" grid.BarycentricCoords)
Interop.mkLayerProp "vertices" grid.Vertices
Interop.mkLayerProp "barycentric" grid.BarycentricCoords
]
let createMapWithLayers center (layers: Layer.Layer []) =
let createMapWithLayers center (layers: Layer.Layer[]) =
let lonLat = fromLonLat center
let view =
View.view [
view.projection epsg3857
view.center lonLat
view.zoom 5.5
]
let view = View.view [ view.projection epsg3857; view.center lonLat; view.zoom 5.5 ]
OlMap.map [
map.layers layers
map.view view
]
OlMap.map [ map.layers layers; map.view view ]
let crossHairSelect (map: OlMap) (clickKey: Event.EventsKey option ref) onClick active =
let elem = map.getTargetElement()
let elem = map.getTargetElement ()
let key =
if active then
elem?style?cursor <- "crosshair"
map.on (
"click",
(fun (e: Event.MapBrowserEvent) ->
onClick e)
)
|> Some
map.on ("click", (fun (e: Event.MapBrowserEvent) -> onClick e)) |> Some
else
None
clickKey.contents <- key
Hook.createDisposable (fun () ->
console.debug("Maps.crossHairSelect dispose")
if not (isNullOrUndefined elem) then elem?style?cursor <- ""
Observable.unByKey key)
console.debug ("Maps.crossHairSelect dispose")
if not (isNullOrUndefined elem) then
elem?style?cursor <- ""
Observable.unByKey key
)
let private hmr = HMR.createToken ()
// let private olCss': {| ``default``: string |} =
// importSideEffects "../public/ol.css"
// importSideEffects "../public/ol.css"
// let private olCss = olCss'.``default``
// console.log olCss
@@ -413,15 +416,15 @@ let private hmr = HMR.createToken ()
let unsafeCSS _ = jsNative
[<LitElement("ol-map")>]
let OlMapElement() =
let OlMapElement () =
let this, props =
LitElement.init (fun cfg ->
cfg.useShadowDom <- true
cfg.props <- {| map = Prop.Of(OlMap.map [], attribute = "map") |}
cfg.styles <-
[
// unsafeCSS olCss
unsafeCSS """
cfg.props <- {| map = Prop.Of (OlMap.map [], attribute = "map") |}
cfg.styles <- [
// unsafeCSS olCss
unsafeCSS
"""
:host {
height: 100%;
}
@@ -447,12 +450,14 @@ let OlMapElement() =
padding: 10px;
}
"""
])
]
)
Hook.useEffect (fun () ->
if isNull this.shadowRoot |> not then
let target = getShadowElementById this "map"
props.map.Value.setTarget target)
props.map.Value.setTarget target
)
let classes = Lit.classes [ "map", true ]

View File

@@ -0,0 +1,7 @@
namespace Lib
module React =
open Fable.Core
open Feliz
let inline fromJsx (el: JSX.Element) : ReactElement = unbox el

View File

@@ -1,10 +1,32 @@
module Remoting
open Browser
open Fable.Remoting.Client
open FsToolkit.ErrorHandling
open Atlantis
open Sorcerer
let getArchiveUrl () = sessionStorage["archmaester_url"]
let getDataUrl () = sessionStorage["sorcerer_url"]
let tryGetArchiveUrl () = sessionStorage["archmaester_url"] |> Utils.tryStr
let tryGetDataUrl () = sessionStorage["sorcerer_url"] |> Utils.tryStr
/// NOTE: This function will redirect the user on a ProxyRequestException
let tryCatch (comp: Async<'T>) : Async<Result<'T, exn>> =
comp
|> Async.Catch
|> Async.map (fun choice ->
choice
|> Result.ofChoice
|> Result.teeError (function
| :? ProxyRequestException as ex ->
console.error("[Remoting] Proxy request error: %o", ex.Response)
window.location.href <- "/signin"
| _ -> ()
)
)
let authApi =
Remoting.createApi ()
|> Remoting.withCredentials true
@@ -87,6 +109,7 @@ type DriftersApi(url) =
member val Particles = createBinApi Remoting.buildProxy<Api.Drifters.Particles>
member val FieldMetaData = createBinApi Remoting.buildProxy<Api.Drifters.FieldMetaData>
member val Sedimentation = createBinApi Remoting.buildProxy<Api.Drifters.Sedimentation>
member val WaterContact = createBinApi Remoting.buildProxy<Api.Drifters.WaterContact>
member val Field2D = createBinApi Remoting.buildProxy<Api.Drifters.Field2D>
member val Field3D = createBinApi Remoting.buildProxy<Api.Drifters.Field3D>
member val Network = createApi Remoting.buildProxy<Api.Drifters.Network>
@@ -109,12 +132,17 @@ type StatsApi(url) =
|> Remoting.withRouteBuilder Api.Stats.routeBuilder
|> f
member val FvStatsInfo =
createApi Remoting.buildProxy<Api.Stats.FvStatsInfo>
member val FvStatsInfo = createApi Remoting.buildProxy<Api.Stats.FvStatsInfo>
member val FvStatsByLayer =
createApi (Remoting.withBinarySerialization >> Remoting.buildProxy<Api.Stats.FvStatsByLayer>)
createApi (
Remoting.withBinarySerialization
>> Remoting.buildProxy<Api.Stats.FvStatsByLayer>
)
member val FvStatsByIndex =
createApi (Remoting.withBinarySerialization >> Remoting.buildProxy<Api.Stats.FvStatsByIndex>)
createApi (
Remoting.withBinarySerialization
>> Remoting.buildProxy<Api.Stats.FvStatsByIndex>
)
member val FvStatsSeries =
createApi (Remoting.withBinarySerialization >> Remoting.buildProxy<Api.Stats.FvStatsSeries>)
@@ -124,8 +152,6 @@ let driftersJobApi () =
|> Remoting.withRouteBuilder Api.authorizedRouteBuilder
|> Remoting.buildProxy<Api.Drifters>
let archiveUrl = Browser.WebStorage.sessionStorage["archmaester_url"]
let archiveApi () =
Remoting.createApi ()
|> Remoting.withCredentials true
@@ -161,4 +187,16 @@ let cropStatsApi url =
|> Remoting.withBaseUrl url
|> Remoting.withRouteBuilder Api.Crop.routeBuilder
|> Remoting.withBinarySerialization
|> Remoting.buildProxy<Api.Crop.Stats>
|> Remoting.buildProxy<Api.Crop.Stats>
let plumeApi () =
Remoting.createApi ()
|> Remoting.withCredentials true
|> Remoting.withRouteBuilder Api.authorizedRouteBuilder
|> Remoting.buildProxy<Api.Plume>
let xtractApi () =
Remoting.createApi ()
|> Remoting.withCredentials true
|> Remoting.withRouteBuilder Api.authorizedRouteBuilder
|> Remoting.buildProxy<Api.Xtract>

View File

@@ -0,0 +1,44 @@
module Search
open FsToolkit.ErrorHandling
open Sorcerer.Types
/// Search an archive for the closest node and elem idx based on the given coordinate
let tryGetNearestNodeAndElement aid (x, y) : Async<Option<NodeIdx * ElemIdx>> =
async {
let dataSvc = Remoting.getDataUrl ()
let api = Remoting.proximityApi dataSvc
let! nodes = api.GetNearestNodes (aid, [| x, y |])
let! elems = api.GetNearestElements (aid, [| x, y |])
let firstNode = nodes |> Array.tryHead |> Option.flatten
let firstElem = elems |> Array.tryHead |> Option.flatten
let result =
firstNode
|> Option.bind (fun node -> firstElem |> Option.map (fun elem -> node, elem))
return result
}
/// Search an archive for the closest node and elem idx based on the given coordinate
let tryGetNearestNodeAndElementRes dataSvc aid (x, y) : Async<Result<NodeIdx * ElemIdx, exn>> =
asyncResult {
let api = Remoting.proximityApi dataSvc
let! nodes = api.GetNearestNodes (aid, [| x, y |]) |> Remoting.tryCatch
let! elems = api.GetNearestElements (aid, [| x, y |]) |> Remoting.tryCatch
let firstNode = nodes |> Array.tryHead |> Option.flatten
let firstElem = elems |> Array.tryHead |> Option.flatten
let result =
firstNode
|> Option.bind (fun node -> firstElem |> Option.map (fun elem -> node, elem))
|> Result.requireSome (exn "Could not find node")
return! result
}
let probePoint (aid: System.Guid) (point: float * float) : Async<Option<(float * float) * Atlantis.Types.GridIdx>> =
async {
let! idxOpt = tryGetNearestNodeAndElement aid point
let res = idxOpt |> Option.map (fun idx -> point, idx)
return res
}

Some files were not shown because too many files have changed in this diff Show More