Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
2116 commits
Select commit Hold shift + click to select a range
216577a
tweak(sync): fix inaccurate type
jaskfla Jan 15, 2026
91d7cae
Revert "added debug info"
chris-bes Jan 16, 2026
d3b5883
fix sync_lookup
chris-bes Jan 18, 2026
9508e63
fix concurrent issue
chris-bes Jan 18, 2026
843ee7e
refactor(database): safer bindings in `EntityHierarchyCacher.refreshA…
jaskfla Jan 19, 2026
f67cbd6
Merge branch 'dev' into rn-1545-epic-datatrak-offline
jaskfla Jan 19, 2026
9db8f6b
merge: fix bad conflict resolution
jaskfla Jan 19, 2026
948094f
Merge branch 'rn-1545-epic-datatrak-offline' into rn-1717-test-four
jaskfla Jan 19, 2026
3179c9c
refactor(database): incremental adoption of es-toolkit in `SurveyResp…
jaskfla Jan 19, 2026
4146b42
refactor(database): remove useless recursion
jaskfla Jan 19, 2026
d7dd973
tweak(database): define `DatabaseRecordName` type
jaskfla Jan 19, 2026
70d149e
refactor(database): replace `compact` with `filter`
jaskfla Jan 19, 2026
064afab
tweak(db): use `DatabaseRecordName` type
jaskfla Jan 19, 2026
04f1529
refactor: use es-toolkit’s `mapValues`
jaskfla Jan 19, 2026
f4fffbc
tweak(types): type sync session info (#6620)
jaskfla Jan 19, 2026
7233d2d
fix(database): `markSessionErrored` race condition
jaskfla Jan 19, 2026
336c28f
fix(datatrak): RN-1490: blank country selector (#6608)
jaskfla Jan 20, 2026
89a491d
fix(database): `SyncSession. addInfo` race condition
jaskfla Jan 20, 2026
b05d2a4
Merge branch 'dev' into rn-1545-epic-datatrak-offline
jaskfla Jan 20, 2026
e9a2f6b
Merge branch 'rn-1545-epic-datatrak-offline' into rn-1717-test-four
jaskfla Jan 20, 2026
e3fd43d
feat(database): tiny type hint
jaskfla Jan 20, 2026
e7d2e40
fix(centralServer): bad merge resolution
jaskfla Jan 20, 2026
bb3d7b1
Merge branch 'rn-1545-epic-datatrak-offline' into rn-1717-test-four
jaskfla Jan 20, 2026
8034abc
fix(database): sync fact type mismatches (#6601)
jaskfla Jan 20, 2026
fcae357
refactor(sync): faster filter callback in `bulkUpdateForClient` (#6602)
jaskfla Jan 20, 2026
8bfe444
type(centralServer): type hints
jaskfla Jan 20, 2026
d25f62e
tweak(datatrak): narrow type
jaskfla Jan 20, 2026
2110d4b
refactor(database): make `sync_session.info` JSONB
jaskfla Jan 20, 2026
6cd7c83
add columnToMerge
chris-bes Jan 21, 2026
fabd411
remove null
chris-bes Jan 21, 2026
88fc0e4
clean up if stream throws error
chris-bes Jan 21, 2026
6314f2c
tweak: various type hints
jaskfla Jan 20, 2026
50cc98c
tweak(centralServer): short circuit `EditUserAccounts`
jaskfla Jan 20, 2026
9cd9381
tweak(centralServer): remove unused import
jaskfla Jan 20, 2026
a4d9d7d
refactor: dedupilcate `snakeKeys` function
jaskfla Jan 20, 2026
ce12504
tweak(datatrakWeb): tighten `ClientSyncManager` types
jaskfla Jan 21, 2026
6794d8a
tweak: tighten types
jaskfla Jan 21, 2026
60abb70
tweak(centralServer): remove unused import
jaskfla Jan 21, 2026
d3a581c
fix(sync): bad type
jaskfla Jan 22, 2026
67520a9
tweak: type database `recordType` (and similar) symbols
jaskfla Jan 22, 2026
886ea59
refactor(syncServer): don’t build unused map
jaskfla Jan 22, 2026
a03627d
refactor(datatrakWeb): use safe bindings in `clearDatabase`
jaskfla Jan 22, 2026
c697ff9
tweak(database): more survey type hints
jaskfla Jan 22, 2026
3a73bd2
fix(database): bad types
jaskfla Jan 23, 2026
1b67d5d
refactor(database): use safe binding in `Survey` model
jaskfla Jan 23, 2026
2ccd23c
tweak(database): type `FeedItem.createAccessPolicyQueryClause`
jaskfla Jan 23, 2026
ad949a3
style(datatrak): unify `FeedItem` and `Survey` model code style
jaskfla Jan 23, 2026
d6f0898
refactor(database): fetch only necessary columns
jaskfla Jan 23, 2026
dd06983
tweak(database): organise exports
jaskfla Jan 23, 2026
7648e98
missing await
chris-bes Jan 24, 2026
7994d8d
save more memory
chris-bes Jan 24, 2026
b7377af
remove
chris-bes Jan 24, 2026
9b54d3f
revert(database): restore missing exported types
jaskfla Jan 24, 2026
6e156a9
test(database): work around Jest’s ES6 module limitation
jaskfla Jan 25, 2026
5ddf124
fix test
chris-bes Jan 25, 2026
b4530a4
test(database): fix same error with `SCHEMA`
jaskfla Jan 25, 2026
c97dfeb
doc(sync): fix bad comment
jaskfla Jan 25, 2026
cf082f2
add some logs for debug
chris-bes Jan 25, 2026
a4a3167
add initial memory
chris-bes Jan 25, 2026
511985c
log error
chris-bes Jan 25, 2026
511193e
revert
chris-bes Jan 26, 2026
c810420
add more debug
chris-bes Jan 26, 2026
615cca9
tweak(database): `deleteById` return type hint
jaskfla Jan 26, 2026
7f11e88
tweak(dabase): define model `join`s as `const`s
jaskfla Jan 26, 2026
9b9838e
tweak(database): narrower model types
jaskfla Jan 26, 2026
bb30b83
style(database): organise imports
jaskfla Jan 26, 2026
34babb8
tweak(centralServer): throw nongeneric error
jaskfla Jan 27, 2026
331919c
tweak(centralServer): declare `permissionsFilteredInternally` overrid…
jaskfla Jan 24, 2026
92103d4
doc(datatrakWeb): remove dead comment
jaskfla Jan 27, 2026
8ef1730
change batch size
chris-bes Jan 27, 2026
e744f68
reduce batch size
chris-bes Jan 27, 2026
3754c51
add more debug logs
chris-bes Jan 27, 2026
528b012
more logging
chris-bes Jan 28, 2026
e192bcf
fix batching
chris-bes Jan 28, 2026
3c32e27
more debug logs
chris-bes Jan 28, 2026
f4a7caa
fixed debug
chris-bes Jan 28, 2026
2d61676
more debug
chris-bes Jan 28, 2026
5029ef4
more debug
chris-bes Jan 28, 2026
98cff39
update batch size
chris-bes Jan 28, 2026
ae43fe1
more debug
chris-bes Jan 28, 2026
c691464
fix debug log
chris-bes Jan 28, 2026
3f42e29
updated batch size
chris-bes Jan 29, 2026
ae64603
test: use valid Base64 URI in relevant unit tests
jaskfla Jan 29, 2026
998617f
test(centralServer): replace magic strings with enums
jaskfla Jan 29, 2026
667044c
fix(datatrak): RN-1752: `File` answer text format from offline upload…
jaskfla Jan 29, 2026
146fd36
fix batching
chris-bes Jan 29, 2026
4f67c20
fix(datatrak): RN-1491: filter local surveys by both access policy an…
jaskfla Jan 29, 2026
6c5efcc
tweak(datatrakWeb): narrower return type
jaskfla Jan 29, 2026
a0f11cf
tweak(datatrakWeb): remove MediTrak reference from home view
jaskfla Jan 29, 2026
2fb8e9f
increase memory 2nd attempt
chris-bes Jan 29, 2026
eb681b4
increase
chris-bes Jan 30, 2026
672a108
increase memory
chris-bes Jan 30, 2026
c484025
vacuum
chris-bes Jan 30, 2026
665249f
create only
chris-bes Jan 30, 2026
bc922af
no vacuum
chris-bes Jan 30, 2026
8a32e90
nginx + log
chris-bes Jan 31, 2026
a707f3c
more logs
chris-bes Jan 31, 2026
d11b76d
relaxedDurability
chris-bes Jan 31, 2026
28d3ee0
upgrade
chris-bes Jan 31, 2026
de20c3f
test remove relaxedDurability
chris-bes Jan 31, 2026
f5d8261
Merge branch 'dev' into rn-1545-epic-datatrak-offline
jaskfla Feb 1, 2026
0d6ebff
merge: fix bad merge resolution
jaskfla Feb 1, 2026
7dd9c84
refactor(database): use `exists` method
jaskfla Feb 1, 2026
512fc17
tweak: fix unsafe typing
jaskfla Jan 29, 2026
4ff6705
tweak(centralServer): narrow `UserRecord` types
jaskfla Feb 1, 2026
17bca9a
Merge branch 'rn-1545-epic-datatrak-offline' into rn-1717-test-four
jaskfla Feb 1, 2026
2f83e94
fix(database): bad `database.connection.raw` usage
jaskfla Feb 1, 2026
ece8a53
Merge branch 'rn-1545-epic-datatrak-offline' into rn-1717-test-four
jaskfla Feb 1, 2026
6532e18
revert unnecessary logs
chris-bes Feb 1, 2026
6550e6b
add logs for memory for incremental sync
chris-bes Feb 1, 2026
17c6da0
optional
chris-bes Feb 1, 2026
c825221
refactor(webConfigServer): more efficient `fetchHasPendingProjectAccess`
jaskfla Feb 1, 2026
166b6c8
fix(database): restore missing type
jaskfla Feb 1, 2026
c37f80c
style(database): reflow awkward inline comment
jaskfla Feb 1, 2026
49dd632
increase timeout
chris-bes Feb 2, 2026
1a782e2
increase timeout
chris-bes Feb 2, 2026
cbf4449
remove timeout
chris-bes Feb 2, 2026
182c9c4
revert nginx
chris-bes Feb 2, 2026
396146b
fix(database): `debug_log` update race condition
jaskfla Feb 2, 2026
86b719d
add debug log
chris-bes Feb 2, 2026
16a5119
add log
chris-bes Feb 2, 2026
03294a5
remove users
chris-bes Feb 2, 2026
499af4b
tweak(entityServer): narrower types
jaskfla Feb 2, 2026
aa1eb21
fix(serverBoilerplate): unsafe property access
jaskfla Feb 2, 2026
395f3f9
refactor(entityServer): improve static type inference
jaskfla Feb 2, 2026
6585827
tweak(database): improve Task model & record type hints
jaskfla Feb 2, 2026
1732e3b
tweak(datatrak): clearer errors in bad mutation function calls
jaskfla Feb 2, 2026
8e0e724
tweak: custom error with unsafe accesses
jaskfla Feb 2, 2026
5030141
refactor(entityServer): fix unsafe access, short circuit
jaskfla Feb 2, 2026
12edb7b
tweak(tsmodels): bolster `Model` generic typing
jaskfla Feb 2, 2026
3c8973f
fix(datatrakWebServer): fetching a survey’s project
jaskfla Feb 2, 2026
9a7da66
tweak(datatrakWeb): fix query key collision
jaskfla Feb 3, 2026
6bf8023
tweak(serverUtils): use `node:` protocol import
jaskfla Feb 3, 2026
92a177d
tweak(database): sync-related model instance method type hints
jaskfla Feb 3, 2026
75c178a
tweak(constants): tighten `const` types
jaskfla Feb 3, 2026
0227098
refactor(datatrakWebServer): await promise
jaskfla Feb 3, 2026
ee0f391
style(datatrakWebServer): remove useless template
jaskfla Feb 3, 2026
a7e3733
refactor(datatrakWeb): more idiomatic query function
jaskfla Feb 3, 2026
52d9cf7
fix(datatrak): RN-1490: make survey submission history available offl…
jaskfla Feb 3, 2026
00432be
fix(datatrakWebServer): bad type
jaskfla Feb 3, 2026
6494c68
tweak(datatrakWebServer): 404 when task not found
jaskfla Feb 3, 2026
7cc53fd
tweak(datatrakWebServer): use const, interface
jaskfla Feb 3, 2026
4c3b775
refactor(datatrakWebServer): parallelise awaits
jaskfla Feb 3, 2026
4605045
fix(datatrakWeb): capitalisation of DataTrak
jaskfla Feb 3, 2026
ffe3e73
tweak(datatrakWeb): refine update modal typography
jaskfla Feb 3, 2026
5558c4a
refactor: faster `ProjectModel.getAccessibleProjects` (marginally)
jaskfla Feb 3, 2026
42c86a0
tweak(tsmodels): tighten `MultiJoinItem` types
jaskfla Feb 3, 2026
d5ec36b
tweak(centralServer): use `const`
jaskfla Feb 4, 2026
3419daf
refactor(centralServer): short circuit if permission assertion fails
jaskfla Feb 4, 2026
0db6acc
refactor(centralServer): join in SQL
jaskfla Feb 4, 2026
7514b40
refactor(datatrakWebServer): use `const`, `interface`
jaskfla Feb 4, 2026
5f19c11
tweak(datatrakWeb): improve query key
jaskfla Feb 4, 2026
1d366fc
refactor(datatrakWebServer): dedupe constant definition
jaskfla Feb 5, 2026
aa735aa
format(database): reflow SQL string
jaskfla Feb 5, 2026
c53213c
tweak(database): more comprehensive SyncLookupQueryDetailsBuilder
jaskfla Feb 5, 2026
b565d66
fix(tsmodels): bad type
jaskfla Feb 5, 2026
f1478cf
tweak(types): tighten `FeedItem` types
jaskfla Feb 5, 2026
483f89d
format(centralServer): enforce code format
jaskfla Feb 5, 2026
ea668b0
style(centralServer): lint `importSurveyResponses`
jaskfla Feb 5, 2026
d074f8d
sync all entities
chris-bes Feb 7, 2026
c35be69
refactor(dhisApi): use set semantics for cacheable endpoints
jaskfla Feb 9, 2026
06acfb2
tweak(centralServer): `emailVerification` type hints
jaskfla Feb 9, 2026
8f8c601
refactor(centralServer): use `findOneOrThrow`, await promises
jaskfla Feb 9, 2026
1cd3875
tweak(centralServer): faster check for existing account
jaskfla Feb 9, 2026
91c2959
tweak(centralServer): `409 Conflict` when account exists
jaskfla Feb 9, 2026
885b16c
refactor(centralServer): less I/O marking user as verified
jaskfla Feb 9, 2026
3a84206
fix: hyphenation of ‘email’
jaskfla Feb 9, 2026
e32b271
tweak(database): remove unused `@typedef`
jaskfla Feb 10, 2026
b1ea59a
Merge branch 'dev' into rn-1545-epic-datatrak-offline
jaskfla Feb 10, 2026
471ee7f
Merge branch 'rn-1545-epic-datatrak-offline' into rn-1717-test-four
jaskfla Feb 10, 2026
f1182d9
refactor(database): simplify `getAllProjectDetails` binding
jaskfla Feb 10, 2026
5c3113d
refactor(sync): faster save phase (#6604)
jaskfla Feb 10, 2026
83e01a7
Merge branch 'dev' into rn-1545-epic-datatrak-offline
jaskfla Feb 10, 2026
917abf5
Merge branch 'rn-1545-epic-datatrak-offline' into rn-1717-test-four
jaskfla Feb 10, 2026
00d4425
Revert "sync all entities"
chris-bes Feb 11, 2026
1b79a56
sync all entities
chris-bes Feb 11, 2026
01f1d54
clean up EntityHierarchyCacher
chris-bes Feb 11, 2026
4b50e3b
refactor(centralServer): avoid needless fetch
jaskfla Feb 12, 2026
31b6501
tweak(constants): more permission group name consts
jaskfla Feb 12, 2026
b006c48
tweak(constants): dedupe object ID regex pattern
jaskfla Feb 12, 2026
2bf19e6
repeatable read
chris-bes Feb 12, 2026
444e5fd
remove repeatable read
chris-bes Feb 12, 2026
962795d
fix advisory lock
chris-bes Feb 12, 2026
d28dcad
refactor(database): avoid needless `Date` instantiation
jaskfla Feb 12, 2026
b5ee011
Revert "fix advisory lock"
chris-bes Feb 12, 2026
a312e6f
add autobind to tupaia database
chris-bes Feb 12, 2026
e7a4ce2
refactor(centralServer): remove redundant predicate
jaskfla Feb 12, 2026
40ad3bd
tweak(datatrakWeb): educate apostrophe
jaskfla Feb 12, 2026
897883f
fix(database): bad `getTimezone` type and usages (#6637)
jaskfla Feb 14, 2026
8705f34
refactor(datatrakWeb): set semantics for read-only question types
jaskfla Feb 14, 2026
f113734
refactor(datatrakWeb): extract survey route matchers, dedupe
jaskfla Feb 14, 2026
44e8144
chore: dedupe type guards, delete unused
jaskfla Feb 15, 2026
1066b65
refactor(centralSurvey): fetch only necessary column
jaskfla Feb 15, 2026
59c1b51
style(sync): remove useless templates
jaskfla Feb 15, 2026
d57b456
tweak(apiClient): use const type
jaskfla Feb 15, 2026
02b3c10
format(datatrak): enforce code format
jaskfla Feb 15, 2026
587eca9
fix(sync): RN-1717: Fix advisory lock causing db connection pool exha…
chris-bes Feb 16, 2026
5afded9
refactor(datatrakWeb): simplify project button label
jaskfla Feb 16, 2026
c88b793
refactor(datatrakWeb): faster check for unsynced data
jaskfla Feb 16, 2026
2a860f7
refactor(database): delegate ObjectID timestamp to bson-objectid
jaskfla Feb 16, 2026
760411c
tweak(database): `generateId` type hints
jaskfla Feb 16, 2026
2c238ad
fix(centralSever): RN-1711: slow email verification (#6634)
jaskfla Feb 16, 2026
d544c5d
fix(datatrakWeb): RN-1490: offline survey response history (#6632)
jaskfla Feb 17, 2026
c81cbe5
chore(database): remove unused symbols
jaskfla Feb 17, 2026
bb8bae9
style(database): no assignment in expression
jaskfla Feb 17, 2026
9956dfe
chore(database): dedupe instance method
jaskfla Feb 17, 2026
1f46e2f
chore: remove suspicious `forEach` callback return
jaskfla Feb 17, 2026
7002102
refactor: avoid useless `Date` instance
jaskfla Feb 17, 2026
e93fe2a
refactor(database): use optional chain
jaskfla Feb 17, 2026
3eab489
refactor(database): use literal key
jaskfla Feb 17, 2026
de42d65
refactor(database): use `indexOf`
jaskfla Feb 17, 2026
2504734
chore(utils): remove unused symbols
jaskfla Feb 17, 2026
8430dd6
refactor(utils): faster `calculateOuterBounds`
jaskfla Feb 17, 2026
5fbeb59
chore: remove unused imports
jaskfla Feb 17, 2026
4a9dbd3
style: misleading variable name
jaskfla Feb 17, 2026
6caed49
clean up properly after migration
chris-bes Feb 17, 2026
e29810a
fix(database): undefined `handlerLock` in `TupaiaDatabase` subclass i…
jaskfla Feb 18, 2026
cc47089
refactor(datatrakWeb): define constants at top-level scope
jaskfla Feb 18, 2026
5588c9b
format(datatrakWeb): enforce code format
jaskfla Feb 18, 2026
d312e02
format(datatrakWeb): reflow long comment
jaskfla Feb 18, 2026
5459574
tweak(datatrakWeb): use transaction to get user locally
jaskfla Feb 19, 2026
2b4d176
refactor(datatrakWeb): use `Object.hasOwn`
jaskfla Feb 19, 2026
fae5063
tweak(datatrakWeb): safer default local mutation function context type
jaskfla Feb 19, 2026
fdce7aa
chore(datatrakWeb): use literal key
jaskfla Feb 19, 2026
96fb77e
chore(datatrakWeb): remove suspicious `let` declaration
jaskfla Feb 19, 2026
8587edf
style(datatrakWeb): use optional chain
jaskfla Feb 19, 2026
2f0cda2
chore(datatrakWeb): remove useless fragment
jaskfla Feb 19, 2026
1daf14f
refactor(datatrakWeb): remove spread from reducer
jaskfla Feb 19, 2026
61a4bf8
refactor(datatrak): fix dubious activity feed Effect dependencies
jaskfla Feb 19, 2026
d79d917
refactor(datatrakWeb): use only relevant primitive as dependency
jaskfla Feb 19, 2026
f0f16ac
tweak(datatrakWeb): use transaction to save user locally
jaskfla Feb 19, 2026
a9c94f3
tweak(datatrakWeb): remove bad selector
jaskfla Feb 19, 2026
8813ca7
tweak(datatrakWeb): clearer error message
jaskfla Feb 19, 2026
8b750f9
tweak(datatrakWeb): get entity descendants with transaction
jaskfla Feb 20, 2026
e5eb55d
fix issue sometimes there's no data showing after initial sync
chris-bes Feb 21, 2026
50eb9ec
revert all debug logs
chris-bes Feb 22, 2026
c756379
revert debug logs 2
chris-bes Feb 22, 2026
031a834
remove logs
chris-bes Feb 22, 2026
3c94d4a
tweak(apiClient): use const type
jaskfla Feb 20, 2026
dff108f
fmt(apiClient): enforce code format
jaskfla Feb 20, 2026
3559d0e
tweak(datatrakWeb): narrower inferred type
jaskfla Feb 20, 2026
fdf7867
clean up datatrak auto update
chris-bes Feb 22, 2026
af4d327
tweak(apiClient): use const
jaskfla Feb 22, 2026
2128912
fmt(syncServer): format `createApp.ts`
jaskfla Feb 22, 2026
18e8502
tweak(datatrakWeb): RN-1806: show loading spinner while logging out (…
jaskfla Feb 23, 2026
78730b5
clean up update
chris-bes Feb 23, 2026
2f7c1c8
🔄
jaskfla Dec 8, 2025
9e3c6b7
read initial value from sync manager
jaskfla Dec 8, 2025
9c49f18
treat `ClientSyncManager.isSyncing` as source of truth
jaskfla Dec 11, 2025
9fbc5ce
comment
jaskfla Dec 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
10 changes: 6 additions & 4 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,14 @@ const config: StorybookConfig = {
resolve: {
preserveSymlinks: true, // use the yarn workspace symlinks
alias: {
http: path.resolve(__dirname, '../moduleMock.js'),
winston: path.resolve(__dirname, '../moduleMock.js'),
jsonwebtoken: path.resolve(__dirname, '../moduleMock.js'),
'node-fetch': path.resolve(__dirname, '../moduleMock.js'),
http: path.resolve(__dirname, '../mock/moduleMock.js'),
winston: path.resolve(__dirname, '../mock/moduleMock.js'),
jsonwebtoken: path.resolve(__dirname, '../mock/moduleMock.js'),
'node-fetch': path.resolve(__dirname, '../mock/moduleMock.js'),
// This is a workaround for us using react-16 in the monorepo
'@storybook/react-dom-shim': '@storybook/react-dom-shim/dist/react-16',
'pg-pubsub': path.resolve(__dirname, '../mock/moduleMock.js'),
'@node-rs/argon2': path.resolve(__dirname, '../mock/argon2ModuleMock.js'),
},
},
});
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ While each package type has their own structure, there are a few common standard
- [entity-server](/packages/entity-server/README.md)
- [report-server](/packages/report-server/README.md)
- [data-table-server](/packages/data-table-server/README.md)
- [sync-server](/packages/sync-server/README.md)
- [sync](/packages/sync/README.md)

Server packages can be built by running `yarn workspace @tupaia/package-name build`. Server packages can then be started by running `yarn workspace @tupaia/package-name start`.

Expand Down
1 change: 1 addition & 0 deletions env/api-client.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ ENTITY_API_URL="http://localhost:8050/v1"
REPORT_API_URL="http://localhost:8030/v1"
TUPAIA_WEB_SERVER_API_URL="http://localhost:8100/v1"
WEB_CONFIG_API_URL="http://localhost:8000/api/v1"
SYNC_API_URL="http://localhost:8120/v1"
13 changes: 13 additions & 0 deletions mock/argon2ModuleMock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const hash = () => {
throw new Error('@node-rs/argon2 not available in browser environment');
};

export const verify = () => {
throw new Error('@node-rs/argon2 not available in browser environment');
};

// Export any other functions your code imports
export default {
hash,
verify,
};
File renamed without changes.
8 changes: 8 additions & 0 deletions mock/pgMock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const types = () => {
throw new Error('pg not available in browser environment');
};

// Export any other functions your code imports
export default {
types,
};
21 changes: 21 additions & 0 deletions mock/winston.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const winston = {
error: console.error,
warn: console.warn,
info: console.info,
log: console.log,
http: console.log,
verbose: console.log,
debug: console.debug,
};

// Support both CommonJS and ES modules
export default winston;
export const {
error,
warn,
info,
log,
http,
verbose,
debug,
} = winston;
6 changes: 5 additions & 1 deletion packages/access-policy/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@
"test:coverage": "yarn test --coverage"
},
"devDependencies": {
"@babel/core": "^7.26.10",
"@babel/eslint-parser": "^7.27.5",
"@tupaia/utils": "workspace:*",
"eslint": "^7.32.0",
"npm-run-all": "^4.1.5",
"rimraf": "^6.0.1"
},
"dependencies": {
"@tupaia/constants": "workspace:*",
"@tupaia/tsutils": "workspace:*"
}
}
7 changes: 5 additions & 2 deletions packages/access-policy/src/AccessPolicy.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ export class AccessPolicy {
return [...this.getPermissionGroupsSet(entities)];
}

/**
* @returns {Set<string>}
*/
getPermissionGroupsSet(requestedEntities) {
// if no specific entities were requested, fetch the permissions for all of them
const entities = requestedEntities || Object.keys(this.policy);
Expand All @@ -112,9 +115,9 @@ export class AccessPolicy {
/**
* Return entities the user has access to the given permission group for
*
* @param {string} [permissionGroup]
* @param {string} [permissionGroup] Permission group name
*
* @returns entities[] The entity objects
* @returns {string[]} The entity codes
*/
getEntitiesAllowed(permissionGroup) {
const allEntityCodes = Object.keys(this.policy);
Expand Down
17 changes: 17 additions & 0 deletions packages/access-policy/src/index.js
Original file line number Diff line number Diff line change
@@ -1 +1,18 @@
export { AccessPolicy } from './AccessPolicy';
export {
allowNoPermissions,
assertAdminPanelAccess,
assertAllPermissions,
assertAnyPermissions,
assertBESAdminAccess,
assertPermissionGroupAccess,
assertPermissionGroupsAccess,
assertVizBuilderAccess,
hasBESAdminAccess,
hasPermissionGroupAccess,
hasPermissionGroupsAccess,
hasSomePermissionGroupsAccess,
hasTupaiaAdminPanelAccess,
hasTupaiaAdminPanelAccessToCountry,
hasVizBuilderAccess,
} from './permissions';
144 changes: 144 additions & 0 deletions packages/access-policy/src/permissions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import {
BES_ADMIN_PERMISSION_GROUP,
TUPAIA_ADMIN_PANEL_PERMISSION_GROUP,
VIZ_BUILDER_PERMISSION_GROUP,
} from '@tupaia/constants';

/**
* @typedef {import('./AccessPolicy').AccessPolicy} AccessPolicy
* @typedef {(accessPolicy: AccessPolicy) => true} SimplePermissionsAssertion
* @typedef {(accessPolicy: AccessPolicy) => boolean} SimplePermissionsChecker
* @typedef {string} PermissionGroupName
*/

/**
* Returns true all the time. This is for any route handlers that do not need permissions.
* @returns {true}
*/
export const allowNoPermissions = () => true;

/**
* Returns true if all of the permissions assertions pass, or throws an error
* @param {function[]} assertions Each permissions assertion should return `true` or throw
* @param {string} errorMessage
* @returns {true}
* @privateRemarks Ideally, these should throw `PermissionsError`s but adding @tupaia/utils as a
* transitive dependency of @tupaia/meditrak-app causes MediTrak build to fail because it has
* imports from node:fs.
*/
export const assertAllPermissions = (assertions, errorMessage) => async accessPolicy => {
try {
await Promise.all(assertions.map(assertion => assertion(accessPolicy)));
return true;
} catch (e) {
throw errorMessage ? new Error(errorMessage) : e;
}
};

/**
* Returns `true` if any of the permissions assertions pass, otherwise throws
* @param {function[]} assertions Each permissions assertion should return true or throw
* @param {string} [errorMessage]
* @returns {function}
* @privateRemarks Ideally, these should throw `PermissionsError`s but adding @tupaia/utils as a
* transitive dependency of @tupaia/meditrak-app causes MediTrak build to fail because it has
* imports from node:fs.
*/
export const assertAnyPermissions = (assertions, errorMessage) => async accessPolicy => {
const combinedErrorMessages = ['One of the following conditions need to be satisfied:'];

for (const assertion of assertions) {
try {
await assertion(accessPolicy);
return true;
} catch (e) {
combinedErrorMessages.push(e.message);
// swallow specific errors, in case any assertion returns true
}
}
throw new Error(errorMessage || combinedErrorMessages.join('\n'));
};

/** @type {SimplePermissionsChecker} */
export const hasBESAdminAccess = accessPolicy =>
accessPolicy.allowsSome(undefined, BES_ADMIN_PERMISSION_GROUP);

/** @type {SimplePermissionsChecker} */
export const hasVizBuilderAccess = accessPolicy =>
accessPolicy.allowsSome(undefined, VIZ_BUILDER_PERMISSION_GROUP);

/**
* @param {AccessPolicy} accessPolicy
* @param {PermissionGroupName} permissionGroup
* @returns {boolean}
*/
export const hasPermissionGroupAccess = (accessPolicy, permissionGroup) =>
accessPolicy.allowsSome(undefined, permissionGroup);

/**
* Has access to all permission groups inputted
* @param {AccessPolicy} accessPolicy
* @param {PermissionGroupName[]} permissionGroups
* @returns {boolean}
*/
export const hasPermissionGroupsAccess = (accessPolicy, permissionGroups) =>
permissionGroups.every(pg => accessPolicy.allowsSome(undefined, pg));

/** @type {SimplePermissionsChecker} */
export const hasSomePermissionGroupsAccess = (accessPolicy, permissionGroups) =>
permissionGroups.some(pg => accessPolicy.allowsSome(undefined, pg));

/** @type {SimplePermissionsAssertion} */
export const assertBESAdminAccess = accessPolicy => {
if (hasBESAdminAccess(accessPolicy)) return true;
throw new Error(`Need ${BES_ADMIN_PERMISSION_GROUP} access`);
};

/** @type {SimplePermissionsAssertion} */
export const assertVizBuilderAccess = accessPolicy => {
if (hasVizBuilderAccess(accessPolicy)) return true;
throw new Error(`Need ${VIZ_BUILDER_PERMISSION_GROUP} access`);
};

/** @type {SimplePermissionsChecker} */
export const hasTupaiaAdminPanelAccess = accessPolicy =>
accessPolicy.allowsSome(undefined, TUPAIA_ADMIN_PANEL_PERMISSION_GROUP);

/**
* @param {AccessPolicy} accessPolicy
* @param {string} countryCode
* @returns {boolean}
*/
export const hasTupaiaAdminPanelAccessToCountry = (accessPolicy, countryCode) =>
accessPolicy.allows(countryCode, TUPAIA_ADMIN_PANEL_PERMISSION_GROUP);

/** @type {SimplePermissionsAssertion} */
export const assertAdminPanelAccess = accessPolicy => {
if (hasTupaiaAdminPanelAccess(accessPolicy)) return true;
throw new Error(`Need ${TUPAIA_ADMIN_PANEL_PERMISSION_GROUP} access`);
};

/**
* @param {AccessPolicy} accessPolicy
* @param {PermissionGroupName} permissionGroupName
* @returns {true}
*/
export const assertPermissionGroupAccess = (accessPolicy, permissionGroupName) => {
if (hasPermissionGroupAccess(accessPolicy, permissionGroupName)) return true;
throw new Error(`Need ${permissionGroupName} access`);
};

/**
* @param {AccessPolicy} accessPolicy
* @param {PermissionGroupName[]} permissionGroupNames
* @returns {true}
*/
export const assertPermissionGroupsAccess = (accessPolicy, permissionGroupNames) => {
if (
hasBESAdminAccess(accessPolicy) ||
hasPermissionGroupsAccess(accessPolicy, permissionGroupNames)
) {
return true;
}
throw new Error(`Need access to ${permissionGroupNames.join(', ')}`);
};
40 changes: 21 additions & 19 deletions packages/admin-panel-server/src/app/createApp.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { NextFunction, Request, Response } from 'express';
import { NextFunction, Request, RequestHandler, Response } from 'express';

import { TupaiaDatabase } from '@tupaia/database';
import {
OrchestratorApiBuilder,
Expand All @@ -7,48 +8,49 @@ import {
handleWith,
} from '@tupaia/server-boilerplate';
import { getEnvVarOrDefault } from '@tupaia/utils';
import { AdminPanelSessionModel } from '../models';
import { hasTupaiaAdminPanelAccess } from '../utils';

import { upload } from '../middleware';
import { AdminPanelSessionModel } from '../models';
import {
ExportDashboardVisualisationRequest,
ExportDashboardVisualisationRoute,
ExportDataTableRequest,
ExportDataTableRoute,
ExportEntityHierarchiesRequest,
ExportEntityHierarchiesRoute,
ExportMapOverlayVisualisationRequest,
ExportMapOverlayVisualisationRoute,
FetchDashboardVisualisationRequest,
FetchDashboardVisualisationRoute,
FetchDataTableBuiltInParamsRequest,
FetchDataTableBuiltInParamsRoute,
FetchDataTablePreviewDataRequest,
FetchDataTablePreviewDataRoute,
FetchHierarchyEntitiesRequest,
FetchHierarchyEntitiesRoute,
FetchMapOverlayVisualisationRequest,
FetchMapOverlayVisualisationRoute,
FetchReportPreviewDataRequest,
FetchReportPreviewDataRoute,
FetchDataTablePreviewDataRequest,
FetchDataTablePreviewDataRoute,
FetchTransformSchemasRequest,
FetchTransformSchemasRoute,
ImportDashboardVisualisationRequest,
ImportDashboardVisualisationRoute,
ImportDataTableRequest,
ImportDataTableRoute,
ImportMapOverlayVisualisationRequest,
ImportMapOverlayVisualisationRoute,
PresentationOptionsPromptRequest,
PresentationOptionsPromptRoute,
SaveDashboardVisualisationRequest,
SaveDashboardVisualisationRoute,
SaveMapOverlayVisualisationRequest,
SaveMapOverlayVisualisationRoute,
UploadTestDataRequest,
UploadTestDataRoute,
UserRoute,
ImportMapOverlayVisualisationRequest,
ImportMapOverlayVisualisationRoute,
FetchTransformSchemasRequest,
FetchTransformSchemasRoute,
FetchDataTableBuiltInParamsRequest,
FetchDataTableBuiltInParamsRoute,
ExportEntityHierarchiesRequest,
ExportEntityHierarchiesRoute,
PresentationOptionsPromptRequest,
PresentationOptionsPromptRoute,
} from '../routes';
import { hasTupaiaAdminPanelAccess } from '../utils';
import { PromptManager } from '../viz-builder/prompts/PromptManager';

export const addPromptManagerToContext =
Expand Down Expand Up @@ -131,22 +133,22 @@ export async function createApp(promptManager: PromptManager) {
)
.post<ImportDashboardVisualisationRequest>(
'import/dashboardVisualisations',
upload.array('dashboardVisualisations'),
upload.array('dashboardVisualisations') as RequestHandler,
handleWith(ImportDashboardVisualisationRoute),
)
.post<ImportDataTableRequest>(
'import/dataTables',
upload.array('dataTables'),
upload.array('dataTables') as RequestHandler,
handleWith(ImportDataTableRoute),
)
.post<ImportMapOverlayVisualisationRequest>(
'import/mapOverlayVisualisations',
upload.array('mapOverlayVisualisations'),
upload.array('mapOverlayVisualisations') as RequestHandler,
handleWith(ImportMapOverlayVisualisationRoute),
)
.post<UploadTestDataRequest>(
'uploadTestData',
upload.single('testData'),
upload.single('testData') as RequestHandler,
handleWith(UploadTestDataRoute),
)
.get<FetchMapOverlayVisualisationRequest>(
Expand Down
2 changes: 1 addition & 1 deletion packages/admin-panel/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"ace-builds": "^1.10.1",
"axios": "^1.12.2",
"content-disposition-header": "^0.6.0",
"date-fns": "^2.29.2",
"date-fns": "^2.30.0",
"file-saver": "^1.3.3",
"jsoneditor": "^10.0.1",
"lodash.debounce": "^4.0.8",
Expand Down
Loading